summaryrefslogtreecommitdiff
path: root/jim-tty.c
diff options
context:
space:
mode:
Diffstat (limited to 'jim-tty.c')
-rw-r--r--jim-tty.c343
1 files changed, 343 insertions, 0 deletions
diff --git a/jim-tty.c b/jim-tty.c
new file mode 100644
index 0000000..5d9cee7
--- /dev/null
+++ b/jim-tty.c
@@ -0,0 +1,343 @@
+/*
+ * 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",
+ "echo",
+ NULL
+};
+
+enum {
+ OPT_BAUD,
+ OPT_DATA,
+ OPT_HANDSHAKE,
+ OPT_INPUT,
+ OPT_OUTPUT,
+ OPT_PARITY,
+ OPT_STOP,
+ OPT_VMIN,
+ OPT_VTIME,
+ OPT_ECHO
+};
+
+
+#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;
+ size_t 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;
+ size_t 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);
+ tio.c_iflag |= ICRNL;
+ }
+ else {
+ tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
+ tio.c_iflag &= ~ICRNL;
+ }
+ break;
+
+ case OPT_ECHO:
+ if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
+ goto badvalue;
+ }
+ if (l) {
+ tio.c_lflag |= ECHO;
+ }
+ else {
+ tio.c_lflag &= ~ECHO;
+ }
+ break;
+
+ }
+ }
+
+ if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) {
+ return -1;
+ }
+ return 0;
+}