LuPPC/src/c/serial.c

159 lines
3.7 KiB
C

#include "lupi.h"
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/stat.h>
int s_listports(lua_State * L) {
DIR * d = opendir("/sys/class/tty");
struct dirent * ent = NULL;
struct stat statbuf;
char path[255];
char linkpath[255];
lua_newtable(L);
size_t index = 1;
while ((ent = readdir(d)) != NULL) {
sprintf(path, "/sys/class/tty/%s/device", ent->d_name);
if (stat(path, &statbuf) == 0) {
readlink(path, linkpath, 255);
if (strstr(linkpath, "serial8250") == NULL) {
// We have a serial port
lua_pushinteger(L, index);
lua_pushstring(L, ent->d_name);
lua_settable(L, -3);
}
}
}
return 1;
}
struct baudrate {
uint32_t baud;
tcflag_t flags;
};
// taken from here: https://man7.org/linux/man-pages/man3/termios.3.html
struct baudrate baudrates[] = {
{0, B0},
{50, B50},
{75, B75},
{110, B110},
{134, B134},
{150, B150},
{200, B200},
{300, B300},
{600, B600},
{1200, B1200},
{1800, B1800},
{2400, B2400},
{4800, B4800},
{9600, B9600},
{19200, B19200},
{38400, B38400},
{57600, B57600},
{115200, B115200},
{230400, B230400}
};
struct baudrate * getbaud(uint32_t baud) {
for (size_t i=0; i<sizeof(baudrates)/sizeof(struct baudrate); ++i) {
if (baudrates[i].baud == baud) {
return &baudrates[i];
} else if (baudrates[i].baud > baud) {
return &baudrates[i-1];
}
}
return getbaud(230400);
}
struct sport {
uint32_t baudrate;
int fd;
struct termios options;
};
int s_openport(lua_State * L) {
char * path = luaL_checkstring(L, 1);
struct sport * port = lua_newuserdata(L, sizeof(struct sport));
port->baudrate = 9600;
char rpath[255];
sprintf(rpath, "/dev/%s", path);
port->fd = open(rpath, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
if (port->fd == -1)
luaL_error(L, "unable to open port %s: ", path, strerror(errno));
tcgetattr(port->fd, &port->options);
cfsetispeed(&port->options, B9600);
cfsetospeed(&port->options, B9600);
tcsetattr(port->fd, TCSANOW, &port->options);
return 1;
}
int s_setbaudrate(lua_State * L) {
struct sport * port = lua_touserdata(L, 1);
long raw_br = luaL_checkinteger(L, 2);
struct baudrate * br = getbaud(raw_br & 0xFFFFFFFF);
//port->options.c_cflag &= ~(CBAUD);
//port->options.c_cflag |= br->flags;
cfsetispeed(&port->options, br->flags);
cfsetospeed(&port->options, br->flags);
port->baudrate = br->baud;
lua_pushinteger(L, br->baud);
port->options.c_cflag &= ~PARENB;
port->options.c_cflag &= ~CSTOPB;
port->options.c_cflag &= ~CSIZE;
port->options.c_cflag |= CS8;
tcsetattr(port->fd, TCSANOW, &port->options);
return 1;
}
int s_getbaudrate(lua_State * L) {
struct sport * port = lua_touserdata(L, 1);
lua_pushinteger(L, port->baudrate);
return 1;
}
int s_write(lua_State * L) {
struct sport * port = lua_touserdata(L, 1);
size_t len = 0;
char * data = luaL_checklstring(L, 2, &len);
size_t ioc = 0;
if ((ioc = write(port->fd, data, len)) != len) {
luaL_error(L, "io error! %llu != %llu", ioc, len);
}
return 0;
}
int s_read(lua_State * L) {
struct sport * port = lua_touserdata(L, 1);
char buffer[2048]; // Max packet size
long size = luaL_checkinteger(L, 2);
size_t ioc = read(port->fd, buffer, size);
if (ioc != size && ioc != 0) {
luaL_error(L, "io error! %llu != %llu or 0", ioc, size);
} else if (ioc != 0) {
lua_pushlstring(L, buffer, size);
return 1;
}
return 0;
}
void serial_start(lua_State * L) {
struct luaL_Reg seriallib[] = {
{"open", s_openport},
{"list", s_listports},
{"write", s_write},
{"read", s_read},
{"setbaud", s_setbaudrate},
{"getbaud", s_getbaudrate},
{NULL, NULL}
};
luaL_openlib(L, "serial", seriallib, 0);
}