1
0
Fork 0
forked from fun/fun

Added serial device support. (0.37.23)

This commit is contained in:
Johannes Findeisen 2025-12-28 02:23:44 +01:00
commit a1c545c6a2
19 changed files with 403 additions and 1 deletions

31
src/vm/os/serial_close.c Normal file
View file

@ -0,0 +1,31 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-28
*/
#ifdef __unix__
#include <unistd.h>
#endif
case OP_SERIAL_CLOSE: {
/* Pops fd (int); returns 1/0 */
Value fdv = pop_value(vm);
int ok = 0;
#ifdef __unix__
if (fdv.type != VAL_INT) {
fprintf(stderr, "Runtime type error: serial_close expects (int fd)\n");
} else {
int fd = (int)fdv.i;
if (close(fd) == 0) ok = 1;
}
#endif
free_value(fdv);
push_value(vm, make_int(ok));
break;
}

90
src/vm/os/serial_config.c Normal file
View file

@ -0,0 +1,90 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-28
*/
#ifdef __unix__
#include <termios.h>
#endif
case OP_SERIAL_CONFIG: {
/* Pops flow_control (int), stop_bits (int), parity (int), data_bits (int), fd (int); returns 1/0 */
Value flowv = pop_value(vm);
Value stopv = pop_value(vm);
Value parityv = pop_value(vm);
Value datav = pop_value(vm);
Value fdv = pop_value(vm);
int ok = 0;
#ifdef __unix__
if (flowv.type != VAL_INT || stopv.type != VAL_INT || parityv.type != VAL_INT ||
datav.type != VAL_INT || fdv.type != VAL_INT) {
fprintf(stderr, "Runtime type error: serial_config expects (int fd, int data_bits, int parity, int stop_bits, int flow_control)\n");
} else {
int fd = (int)fdv.i;
int data_bits = (int)datav.i;
int parity = (int)parityv.i;
int stop_bits = (int)stopv.i;
int flow = (int)flowv.i;
struct termios options;
if (tcgetattr(fd, &options) == 0) {
// Data bits
options.c_cflag &= ~CSIZE;
switch (data_bits) {
case 5: options.c_cflag |= CS5; break;
case 6: options.c_cflag |= CS6; break;
case 7: options.c_cflag |= CS7; break;
case 8: default: options.c_cflag |= CS8; break;
}
// Parity
switch (parity) {
case 0: // None
options.c_cflag &= ~PARENB;
break;
case 1: // Odd
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
break;
case 2: // Even
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
break;
}
// Stop bits
if (stop_bits == 2) {
options.c_cflag |= CSTOPB;
} else {
options.c_cflag &= ~CSTOPB;
}
// Flow control
#ifdef CRTSCTS
if (flow == 1) { // Hardware (RTS/CTS)
options.c_cflag |= CRTSCTS;
} else {
options.c_cflag &= ~CRTSCTS;
}
#endif
if (tcsetattr(fd, TCSANOW, &options) == 0) {
ok = 1;
}
}
}
#endif
free_value(flowv);
free_value(stopv);
free_value(parityv);
free_value(datav);
free_value(fdv);
push_value(vm, make_int(ok));
break;
}

82
src/vm/os/serial_open.c Normal file
View file

@ -0,0 +1,82 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-28
*/
#ifdef __unix__
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#endif
case OP_SERIAL_OPEN: {
/* Pops baud_rate (int), path (string); returns fd (int) or 0 */
Value baudv = pop_value(vm);
Value pathv = pop_value(vm);
int fd = 0;
#ifdef __unix__
if (baudv.type != VAL_INT || pathv.type != VAL_STRING) {
fprintf(stderr, "Runtime type error: serial_open expects (string path, int baud_rate)\n");
free_value(baudv);
free_value(pathv);
push_value(vm, make_int(0));
break;
}
const char *path = pathv.s ? pathv.s : "";
int baud = (int)baudv.i;
speed_t speed;
switch (baud) {
case 50: speed = B50; break;
case 75: speed = B75; break;
case 110: speed = B110; break;
case 134: speed = B134; break;
case 150: speed = B150; break;
case 200: speed = B200; break;
case 300: speed = B300; break;
case 600: speed = B600; break;
case 1200: speed = B1200; break;
case 1800: speed = B1800; break;
case 2400: speed = B2400; break;
case 4800: speed = B4800; break;
case 9600: speed = B9600; break;
case 19200: speed = B19200; break;
case 38400: speed = B38400; break;
case 57600: speed = B57600; break;
case 115200: speed = B115200; break;
case 230400: speed = B230400; break;
default: speed = B9600; break;
}
fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd != -1) {
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, speed);
cfsetospeed(&options, speed);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_oflag &= ~OPOST;
tcsetattr(fd, TCSANOW, &options);
fcntl(fd, F_SETFL, 0); // block on read
} else {
fd = 0;
}
#endif
free_value(baudv);
free_value(pathv);
push_value(vm, make_int(fd > 0 ? fd : 0));
break;
}

47
src/vm/os/serial_recv.c Normal file
View file

@ -0,0 +1,47 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-28
*/
#ifdef __unix__
#include <unistd.h>
#endif
case OP_SERIAL_RECV: {
/* Pops maxlen (int), fd (int); returns data (string) */
Value maxv = pop_value(vm);
Value fdv = pop_value(vm);
char *out = NULL;
#ifdef __unix__
if (fdv.type != VAL_INT || maxv.type != VAL_INT) {
fprintf(stderr, "Runtime type error: serial_recv expects (int fd, int maxlen)\n");
} else {
int fd = (int)fdv.i;
int maxlen = (int)maxv.i;
if (maxlen <= 0) maxlen = 4096;
if (maxlen > 1<<20) maxlen = 1<<20; /* cap at 1MB */
out = (char*)malloc((size_t)maxlen + 1);
if (out) {
ssize_t n = read(fd, out, (size_t)maxlen);
if (n <= 0) {
free(out);
out = NULL;
} else {
out[n] = '\0';
}
}
}
#endif
free_value(maxv);
free_value(fdv);
Value s = make_string(out ? out : "");
if (out) free(out);
push_value(vm, s);
break;
}

36
src/vm/os/serial_send.c Normal file
View file

@ -0,0 +1,36 @@
/**
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-28
*/
#ifdef __unix__
#include <unistd.h>
#endif
case OP_SERIAL_SEND: {
/* Pops data (string), fd (int); returns bytes sent (int) */
Value datav = pop_value(vm);
Value fdv = pop_value(vm);
int sent = -1;
#ifdef __unix__
if (fdv.type != VAL_INT || datav.type != VAL_STRING) {
fprintf(stderr, "Runtime type error: serial_send expects (int fd, string data)\n");
} else {
int fd = (int)fdv.i;
const char *buf = datav.s ? datav.s : "";
size_t len = strlen(buf);
ssize_t n = write(fd, buf, len);
if (n >= 0) sent = (int)n;
}
#endif
free_value(datav);
free_value(fdv);
push_value(vm, make_int(sent));
break;
}