aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2016-09-02 17:05:51 +1000
committerSteve Bennett <steveb@workware.net.au>2016-09-09 17:38:37 +1000
commit685edd21efef7311ad8ef7dcb4023db30201b99f (patch)
tree9e587fa0a7fbcf21ae4d780b8c1e9c41548ef0b2
parent4baa884e739b59dfd50cbf606786c41de1d289a8 (diff)
downloadjimtcl-685edd21efef7311ad8ef7dcb4023db30201b99f.zip
jimtcl-685edd21efef7311ad8ef7dcb4023db30201b99f.tar.gz
jimtcl-685edd21efef7311ad8ef7dcb4023db30201b99f.tar.bz2
aio: add tty settings support (via termios)
Enough to make serial ports work and support raw and cooked input/output Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r--auto.def3
-rw-r--r--jim-aio.c59
-rw-r--r--jim-tty.c327
-rw-r--r--jim-tty.h30
4 files changed, 418 insertions, 1 deletions
diff --git a/auto.def b/auto.def
index e531743..0ff91fb 100644
--- a/auto.def
+++ b/auto.def
@@ -350,6 +350,9 @@ if {[have-feature windows]} {
user-error "cygwin/mingw require --shared for dynamic modules"
}
}
+if {[have-feature termios.h]} {
+ lappend extra_objs jim-tty.o
+}
if {[ext-get-status regexp] in {y m}} {
if {![have-feature regcomp]} {
diff --git a/jim-aio.c b/jim-aio.c
index f36acdd..e8af2f3 100644
--- a/jim-aio.c
+++ b/jim-aio.c
@@ -68,6 +68,10 @@
#include <openssl/err.h>
#endif
+#ifdef HAVE_TERMIOS_H
+#include <jim-tty.h>
+#endif
+
#include "jim-eventloop.h"
#include "jim-subcmd.h"
@@ -490,7 +494,6 @@ static void JimAioDelProc(Jim_Interp *interp, void *privData)
SSL_free(af->ssl);
}
#endif
-
if (!(af->openFlags & AIO_KEEPOPEN)) {
fclose(af->fp);
}
@@ -1207,6 +1210,51 @@ static int aio_cmd_unlock(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
}
#endif /* JIM_BOOTSTRAP */
+#if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
+static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ AioFile *af = Jim_CmdPrivData(interp);
+ Jim_Obj *dictObjPtr;
+ int ret;
+
+ if (argc == 0) {
+ /* get the current settings as a dictionary */
+ dictObjPtr = Jim_GetTtySettings(interp, af->fd);
+ if (dictObjPtr == NULL) {
+ JimAioSetError(interp, NULL);
+ return JIM_ERR;
+ }
+ Jim_SetResult(interp, dictObjPtr);
+ return JIM_OK;
+ }
+
+ if (argc > 1) {
+ /* Convert name value arguments to a dictionary */
+ dictObjPtr = Jim_NewListObj(interp, argv, argc);
+ }
+ else {
+ /* The settings are already given as a list */
+ dictObjPtr = argv[0];
+ }
+ Jim_IncrRefCount(dictObjPtr);
+
+ if (Jim_ListLength(interp, dictObjPtr) % 2) {
+ /* Must be a valid dictionary */
+ Jim_DecrRefCount(interp, dictObjPtr);
+ return -1;
+ }
+
+ ret = Jim_SetTtySettings(interp, af->fd, dictObjPtr);
+ if (ret < 0) {
+ JimAioSetError(interp, NULL);
+ ret = JIM_ERR;
+ }
+ Jim_DecrRefCount(interp, dictObjPtr);
+
+ return ret;
+}
+#endif /* JIM_BOOTSTRAP */
+
static const jim_subcmd_type aio_command_table[] = {
{ "read",
"?-nonewline? ?len?",
@@ -1397,6 +1445,15 @@ static const jim_subcmd_type aio_command_table[] = {
/* Description: Relase a lock. */
},
#endif /* JIM_BOOTSTRAP */
+#if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
+ { "tty",
+ "?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?vmin n? ?vtime n?",
+ aio_cmd_tty,
+ 0,
+ -1,
+ /* Description: Get or set tty settings - valid only on a tty */
+ },
+#endif /* JIM_BOOTSTRAP */
{ NULL }
};
diff --git a/jim-tty.c b/jim-tty.c
new file mode 100644
index 0000000..e9c6ee9
--- /dev/null
+++ b/jim-tty.c
@@ -0,0 +1,327 @@
+/*
+ * Support for tty settings using termios
+ *
+ * (c) 2016 Steve Bennett <steveb@workware.net.au>
+ *
+ */
+
+/* termios support is required */
+
+#include <jim-tty.h>
+#include <termios.h>
+
+static const struct {
+ unsigned baud;
+ speed_t speed;
+} baudtable[] = {
+ { 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 },
+#ifdef B230400
+ { 230400, B230400 },
+#endif
+#ifdef B460800
+ { 460800, B460800 }
+#endif
+};
+
+struct flag_name_map {
+ const char *name;
+ unsigned value;
+};
+
+static const struct flag_name_map parity_map[] = {
+ { "none", 0 },
+ { "even", PARENB },
+ { "odd", PARENB | PARODD },
+};
+static const struct flag_name_map data_size_map[] = {
+ { "5", CS5 },
+ { "6", CS6 },
+ { "7", CS7 },
+ { "8", CS8 },
+};
+static const struct flag_name_map stop_size_map[] = {
+ { "1", 0 },
+ { "2", CSTOPB },
+};
+static const struct flag_name_map input_map[] = {
+ { "raw", 0 },
+ { "cooked", ICANON },
+};
+static const struct flag_name_map output_map[] = {
+ { "raw", 0 },
+ { "cooked", OPOST },
+};
+
+static const char * const tty_settings_names[] = {
+ "baud",
+ "data",
+ "handshake",
+ "input",
+ "output",
+ "parity",
+ "stop",
+ "vmin",
+ "vtime",
+ NULL
+};
+
+enum {
+ OPT_BAUD,
+ OPT_DATA,
+ OPT_HANDSHAKE,
+ OPT_INPUT,
+ OPT_OUTPUT,
+ OPT_PARITY,
+ OPT_STOP,
+ OPT_VMIN,
+ OPT_VTIME
+};
+
+
+#define ARRAYSIZE(A) (sizeof(A)/sizeof(*(A)))
+
+/**
+ * Search the flag/name map for an entry with the given name.
+ * (Actually, just matching the first char)
+ * Returns a pointer to the entry if found, or NULL if not.
+ */
+static const struct flag_name_map *flag_name_to_value(const struct flag_name_map *map, int len, const char *name)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ /* Only need to compare the first character since all names are unique in the first char */
+ if (*name == *map[i].name) {
+ return &map[i];
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Search the flag/name map for an entry with the matching value.
+ * Returns the corresponding name if found, or NULL if no match.
+ */
+static const char *flag_value_to_name(const struct flag_name_map *map, int len, unsigned value)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (value == map[i].value) {
+ return map[i].name;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * If 'str2' is not NULL, appends 'str1' and 'str2' to the list.
+ * Otherwise does nothing.
+ */
+static void JimListAddPair(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *str1, const char *str2)
+{
+ if (str2) {
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, str1, -1));
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, str2, -1));
+ }
+}
+
+Jim_Obj *Jim_GetTtySettings(Jim_Interp *interp, int fd)
+{
+ struct termios tio;
+ int i;
+ const char *p;
+ Jim_Obj *listObjPtr;
+ speed_t speed;
+ int baud;
+
+ if (tcgetattr(fd, &tio) < 0) {
+ return NULL;
+ }
+
+ listObjPtr = Jim_NewListObj(interp, NULL, 0);
+
+ p = flag_value_to_name(parity_map, ARRAYSIZE(parity_map), tio.c_cflag & (PARENB | PARODD));
+ JimListAddPair(interp, listObjPtr, "parity", p);
+ p = flag_value_to_name(data_size_map, ARRAYSIZE(data_size_map), tio.c_cflag & CSIZE);
+ JimListAddPair(interp, listObjPtr, "data", p);
+ p = flag_value_to_name(stop_size_map, ARRAYSIZE(stop_size_map), tio.c_cflag & CSTOPB);
+ JimListAddPair(interp, listObjPtr, "stop", p);
+ if (tio.c_iflag & (IXON | IXOFF)) {
+ p = "xonxoff";
+ }
+ else if (tio.c_cflag & CRTSCTS) {
+ p = "rtscts";
+ }
+ else {
+ p = "none";
+ }
+ JimListAddPair(interp, listObjPtr, "handshake", p);
+ p = flag_value_to_name(input_map, ARRAYSIZE(input_map), tio.c_lflag & ICANON);
+ JimListAddPair(interp, listObjPtr, "input", p);
+ p = flag_value_to_name(output_map, ARRAYSIZE(output_map), tio.c_oflag & OPOST);
+ JimListAddPair(interp, listObjPtr, "output", p);
+
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "vmin", -1));
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, tio.c_cc[VMIN]));
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "vtime", -1));
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, tio.c_cc[VTIME]));
+
+ speed = cfgetispeed(&tio);
+ baud = 0;
+ for (i = 0; i < sizeof(baudtable) / sizeof(*baudtable); i++) {
+ if (baudtable[i].speed == speed) {
+ baud = baudtable[i].baud;
+ break;
+ }
+ }
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "baud", -1));
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, baud));
+
+ return listObjPtr;
+}
+
+int Jim_SetTtySettings(Jim_Interp *interp, int fd, Jim_Obj *dictObjPtr)
+{
+ int len = Jim_ListLength(interp, dictObjPtr);
+ int i;
+
+ struct termios tio;
+
+ if (tcgetattr(fd, &tio) < 0) {
+ return -1;
+ }
+
+ for (i = 0; i < len; i += 2) {
+ Jim_Obj *nameObj = Jim_ListGetIndex(interp, dictObjPtr, i);
+ Jim_Obj *valueObj = Jim_ListGetIndex(interp, dictObjPtr, i + 1);
+ int opt;
+ const struct flag_name_map *p;
+ long l;
+ int j;
+
+ if (Jim_GetEnum(interp, nameObj, tty_settings_names, &opt, "setting", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
+ return JIM_ERR;
+ }
+
+ switch (opt) {
+ case OPT_BAUD:
+ if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
+ goto badvalue;
+ }
+ for (j = 0; j < ARRAYSIZE(baudtable); j++) {
+ if (baudtable[j].baud == l) {
+ break;
+ }
+ }
+ if (j == ARRAYSIZE(baudtable)) {
+ goto badvalue;
+ }
+ cfsetospeed(&tio, baudtable[j].speed);
+ cfsetispeed(&tio, baudtable[j].speed);
+ break;
+
+ case OPT_PARITY:
+ p = flag_name_to_value(parity_map, ARRAYSIZE(parity_map), Jim_String(valueObj));
+ if (p == NULL) {
+badvalue:
+ Jim_SetResultFormatted(interp, "bad value for %#s: %#s", nameObj, valueObj);
+ return JIM_ERR;
+ }
+ tio.c_cflag &= ~(PARENB | PARODD);
+ tio.c_cflag |= p->value;
+ break;
+
+ case OPT_STOP:
+ p = flag_name_to_value(stop_size_map, ARRAYSIZE(stop_size_map), Jim_String(valueObj));
+ if (p == NULL) {
+ goto badvalue;
+ }
+ tio.c_cflag &= ~CSTOPB;
+ tio.c_cflag |= p->value;
+ break;
+
+ case OPT_DATA:
+ p = flag_name_to_value(data_size_map, ARRAYSIZE(data_size_map), Jim_String(valueObj));
+ if (p == NULL) {
+ goto badvalue;
+ }
+ tio.c_cflag &= ~CSIZE;
+ tio.c_cflag |= p->value;
+ break;
+
+ case OPT_HANDSHAKE:
+ tio.c_iflag &= ~(IXON | IXOFF);
+ tio.c_cflag &= ~(CRTSCTS);
+ if (Jim_CompareStringImmediate(interp, valueObj, "xonxoff")) {
+ tio.c_iflag |= (IXON | IXOFF);
+ }
+ else if (Jim_CompareStringImmediate(interp, valueObj, "rtscts")) {
+ tio.c_cflag |= CRTSCTS;
+ }
+ else if (!Jim_CompareStringImmediate(interp, valueObj, "none")) {
+ goto badvalue;
+ }
+ break;
+
+ case OPT_VMIN:
+ if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
+ goto badvalue;
+ }
+ tio.c_cc[VMIN] = l;
+ break;
+
+ case OPT_VTIME:
+ if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
+ goto badvalue;
+ }
+ tio.c_cc[VTIME] = l;
+ break;
+
+ case OPT_OUTPUT:
+ p = flag_name_to_value(output_map, ARRAYSIZE(output_map), Jim_String(valueObj));
+ if (p == NULL) {
+ goto badvalue;
+ }
+ tio.c_oflag &= ~OPOST;
+ tio.c_oflag |= p->value;
+ break;
+
+ case OPT_INPUT:
+ p = flag_name_to_value(input_map, ARRAYSIZE(input_map), Jim_String(valueObj));
+ if (p == NULL) {
+ goto badvalue;
+ }
+ if (p->value) {
+ tio.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
+ }
+ else {
+ tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
+ }
+ break;
+
+ }
+ }
+
+ if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) {
+ return -1;
+ }
+ return 0;
+}
diff --git a/jim-tty.h b/jim-tty.h
new file mode 100644
index 0000000..d052d73
--- /dev/null
+++ b/jim-tty.h
@@ -0,0 +1,30 @@
+#ifndef JIM_TTY_H
+#define JIM_TTY_H
+
+#include <jim.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Return the tty settings for the given file descriptor as a dictionary
+ * with a zero reference count.
+ *
+ * Returns NULL and sets errno file descriptor is not a valid tty.
+ */
+Jim_Obj *Jim_GetTtySettings(Jim_Interp *interp, int fd);
+
+/**
+ * Sets the tty settings given in 'dictObjPtr'
+ *
+ * Returns JIM_OK if OK, JIM_ERR if any settings are invalid,
+ * or -1 (and sets errno) if the file descriptor is not a valid tty.
+ */
+int Jim_SetTtySettings(Jim_Interp *interp, int fd, Jim_Obj *dictObjPtr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif