From b1f7c349b5714ebe304f93e43576a0ff3f721fc1 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Thu, 23 Feb 2012 17:12:37 -0800 Subject: [PATCH] Add support for serial devices as socket streams on unix. --- config | 1 + makefile | 1 + src/makefile | 18 ++++- src/serial.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/socket.h | 3 + src/usocket.c | 60 +++++++++++++++++ 6 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 src/serial.c diff --git a/config b/config index d6085ad..7d73638 100644 --- a/config +++ b/config @@ -11,6 +11,7 @@ MIME_V=1.0.2 SOCKET_SO=socket.$(EXT).$(SOCKET_V) MIME_SO=mime.$(EXT).$(MIME_V) UNIX_SO=unix.$(EXT) +SERIAL_SO=serial.$(EXT) #------ # Lua includes and libraries diff --git a/makefile b/makefile index 97a72e9..b1c9f18 100644 --- a/makefile +++ b/makefile @@ -48,6 +48,7 @@ install: all install-unix: install all-unix cd src; $(INSTALL_EXEC) $(UNIX_SO) $(INSTALL_SOCKET_LIB)/$(UNIX_SO) + cd src; $(INSTALL_EXEC) $(SERIAL_SO) $(INSTALL_SOCKET_LIB)/$(SERIAL_SO) #------ # End of makefile diff --git a/src/makefile b/src/makefile index 6ec8718..c5c22f2 100644 --- a/src/makefile +++ b/src/makefile @@ -47,6 +47,17 @@ UNIX_OBJS:=\ usocket.o \ unix.o +#------ +# Modules belonging to serial (device streams) +# +SERIAL_OBJS:=\ + buffer.o \ + auxiliar.o \ + timeout.o \ + io.o \ + usocket.o \ + serial.o + all: $(SOCKET_SO) $(MIME_SO) $(SOCKET_SO): $(SOCKET_OBJS) @@ -55,11 +66,14 @@ $(SOCKET_SO): $(SOCKET_OBJS) $(MIME_SO): $(MIME_OBJS) $(LD) $(LDFLAGS) -o $@ $(MIME_OBJS) -all-unix: all $(UNIX_SO) +all-unix: all $(UNIX_SO) $(SERIAL_SO) $(UNIX_SO): $(UNIX_OBJS) $(LD) $(LDFLAGS) -o $@ $(UNIX_OBJS) +$(SERIAL_SO): $(SERIAL_OBJS) + $(LD) $(LDFLAGS) -o $@ $(SERIAL_OBJS) + #------ # List of dependencies # @@ -74,6 +88,8 @@ mime.o: mime.c mime.h options.o: options.c auxiliar.h options.h socket.h io.h timeout.h \ usocket.h inet.h select.o: select.c socket.h io.h timeout.h usocket.h select.h +serial.o: serial.c auxiliar.h socket.h io.h timeout.h usocket.h \ + unix.h buffer.h tcp.o: tcp.c auxiliar.h socket.h io.h timeout.h usocket.h inet.h \ options.h tcp.h buffer.h timeout.o: timeout.c auxiliar.h timeout.h diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..b356a3a --- /dev/null +++ b/src/serial.c @@ -0,0 +1,183 @@ +/*=========================================================================*\ +* Serial stream +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" +#include + +/* +Reuses userdata definition from unix.h, since it is useful for all +stream-like objects. + +If we stored the serial path for use in error messages or userdata +printing, we might need our own userdata definition. + +Group usage is semi-inherited from unix.c, but unnecessary since we +have only one object type. +*/ + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_send(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_close(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); + +/* serial object methods */ +static luaL_reg un[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"close", meth_close}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"settimeout", meth_settimeout}, + {NULL, NULL} +}; + +/* our socket creation function */ +static luaL_reg func[] = { + {"serial", global_create}, + {NULL, NULL} +}; + + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int luaopen_socket_serial(lua_State *L) { + /* create classes */ + auxiliar_newclass(L, "serial{client}", un); + /* create class groups */ + auxiliar_add2group(L, "serial{client}", "serial{any}"); + /* make sure the function ends up in the package table */ + luaL_openlib(L, "socket", func, 0); + /* return the function instead of the 'socket' table */ + lua_pushstring(L, "serial"); + lua_gettable(L, -2); + return 1; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ + + +/*-------------------------------------------------------------------------*\ +* Creates a serial object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + const char* path = luaL_checkstring(L, 1); + + /* allocate unix object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + + /* open serial device */ + t_socket sock = open(path, O_NOCTTY|O_RDWR); + + /*printf("open %s on %d\n", path, sock);*/ + + if (sock < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + lua_pushnumber(L, errno); + return 3; + } + /* set its type as client object */ + auxiliar_setclass(L, "serial{client}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; +} diff --git a/src/socket.h b/src/socket.h index de5d79f..debb13a 100644 --- a/src/socket.h +++ b/src/socket.h @@ -68,6 +68,9 @@ const char *socket_strerror(int err); int socket_send(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm); int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm); +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); const char *socket_ioerror(p_socket ps, int err); int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp); diff --git a/src/usocket.c b/src/usocket.c index e43cfa4..46087c6 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -300,6 +300,66 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, return IO_UNKNOWN; } + +/*-------------------------------------------------------------------------*\ +* Write with timeout +* +* socket_read and socket_write are cut-n-paste of socket_send and socket_recv, +* with send/recv replaced with write/read. We can't just use write/read +* in the socket version, because behaviour when size is zero is different. +\*-------------------------------------------------------------------------*/ +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) write(*ps, data, count); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Read with timeout +* See note for socket_write +\*-------------------------------------------------------------------------*/ +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) read(*ps, data, count); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + /*-------------------------------------------------------------------------*\ * Put socket into blocking mode \*-------------------------------------------------------------------------*/