diff options
author | Steve Bennett <steveb@workware.net.au> | 2016-09-02 17:05:51 +1000 |
---|---|---|
committer | Steve Bennett <steveb@workware.net.au> | 2016-09-09 17:38:37 +1000 |
commit | 685edd21efef7311ad8ef7dcb4023db30201b99f (patch) | |
tree | 9e587fa0a7fbcf21ae4d780b8c1e9c41548ef0b2 /jim-tty.c | |
parent | 4baa884e739b59dfd50cbf606786c41de1d289a8 (diff) | |
download | jimtcl-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>
Diffstat (limited to 'jim-tty.c')
-rw-r--r-- | jim-tty.c | 327 |
1 files changed, 327 insertions, 0 deletions
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; +} |