From 67e32c82fa2248a70a02d0828b8785b9a25692b0 Mon Sep 17 00:00:00 2001 From: Steve Bennett Date: Tue, 5 May 2020 22:50:27 +1000 Subject: aio: Add support for modem control signals RTS, DTR, etc. and sending a break condition Signed-off-by: Steve Bennett --- jim-aio.c | 50 +++++++++++++++++++++++++++++++++ jim-tty.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ jim-tty.h | 16 +++++++++++ jim_tcl.txt | 21 +++++++++++++- 4 files changed, 178 insertions(+), 1 deletion(-) diff --git a/jim-aio.c b/jim-aio.c index 0805d41..7f1d608 100644 --- a/jim-aio.c +++ b/jim-aio.c @@ -1906,6 +1906,49 @@ static int aio_cmd_tty(Jim_Interp *interp, int argc, Jim_Obj *const *argv) return ret; } + +static int aio_cmd_ttycontrol(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_GetTtyControlSettings(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_SetTtyControlSettings(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[] = { @@ -2151,6 +2194,13 @@ static const jim_subcmd_type aio_command_table[] = { -1, /* Description: Get or set tty settings - valid only on a tty */ }, + { "ttycontrol", + "?rts 0|1? ?dtr 0|1?", + aio_cmd_ttycontrol, + 0, + -1, + /* Description: Get or set tty modem control settings - valid only on a tty */ + }, #endif #endif /* JIM_BOOTSTRAP */ { NULL } diff --git a/jim-tty.c b/jim-tty.c index 534e65c..245d3bf 100644 --- a/jim-tty.c +++ b/jim-tty.c @@ -7,6 +7,7 @@ /* termios support is required */ #include +#include #include static const struct { @@ -349,3 +350,94 @@ badvalue: } return 0; } + +Jim_Obj *Jim_GetTtyControlSettings(Jim_Interp *interp, int fd) +{ + size_t i; + Jim_Obj *listObjPtr; + int status; + static const struct { + const char * const name; + int value; + } modem_signals[] = { + { "rts", TIOCM_RTS }, + { "dtr", TIOCM_DTR }, + + { "dcd", TIOCM_CAR }, + { "dsr", TIOCM_DSR }, + { "ring", TIOCM_RNG }, + { "cts", TIOCM_CTS }, + }; + + if (ioctl(fd, TIOCMGET, &status) < 0) { + return NULL; + } + + listObjPtr = Jim_NewListObj(interp, NULL, 0); + for (i = 0; i < ARRAYSIZE(modem_signals); i++) { + Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, modem_signals[i].name, -1)); + Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, (status & modem_signals[i].value & status) != 0)); + } + return listObjPtr; +} + +int Jim_SetTtyControlSettings(Jim_Interp *interp, int fd, Jim_Obj *dictObjPtr) +{ + static const char * const modem_signal_names[] = { + "rts", "dtr", "break", NULL + }; + enum { OPT_RTS, OPT_DTR, OPT_BREAK, }; + int len = Jim_ListLength(interp, dictObjPtr); + int i; + + int status; + + /* Get the current status of DTR and RTS in case we aren't setting everything */ + if (ioctl(fd, TIOCMGET, &status) < 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; + long l; + + if (Jim_GetEnum(interp, nameObj, modem_signal_names, &opt, "signal", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { + return JIM_ERR; + } + if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) { + Jim_SetResultFormatted(interp, "bad value for %#s: %#s", nameObj, valueObj); + return JIM_ERR; + } + + switch (opt) { + case OPT_RTS: + if (l) { + status |= TIOCM_RTS; + } + else { + status &= ~TIOCM_RTS; + } + break; + + case OPT_DTR: + if (l) { + status |= TIOCM_DTR; + } + else { + status &= ~TIOCM_DTR; + } + break; + + case OPT_BREAK: + tcsendbreak(fd, l); + break; + } + } + + if (ioctl(fd, TIOCMSET, &status) < 0) { + return -1; + } + return JIM_OK; +} diff --git a/jim-tty.h b/jim-tty.h index d052d73..bf1ad7f 100644 --- a/jim-tty.h +++ b/jim-tty.h @@ -23,6 +23,22 @@ Jim_Obj *Jim_GetTtySettings(Jim_Interp *interp, int fd); */ int Jim_SetTtySettings(Jim_Interp *interp, int fd, Jim_Obj *dictObjPtr); +/** + * Return the tty modem control 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_GetTtyControlSettings(Jim_Interp *interp, int fd); + +/** + * Sets the tty modem control 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_SetTtyControlSettings(Jim_Interp *interp, int fd, Jim_Obj *dictObjPtr); + #ifdef __cplusplus } #endif diff --git a/jim_tcl.txt b/jim_tcl.txt index 05a2e26..9cfa6f8 100644 --- a/jim_tcl.txt +++ b/jim_tcl.txt @@ -5136,7 +5136,7 @@ This command returns an empty string. +$handle *tty* ?settings?+:: If no arguments are given, returns a dictionary containing the tty settings for the stream. - If arguments are given, they must either be a dictionary, or +setting value \...+ + If arguments are given, they must either be a dictionary, or +setting value \...+. Abbreviations are supported for both settings and values, so the following is acceptable: +$f tty parity e input c out raw+. Only available on platforms that support 'termios(3)'. Supported settings are: @@ -5179,6 +5179,25 @@ This command returns an empty string. +*vstop* 'char'+;; Stop character for xonoff, usually 0x13 (^S) ++$handle *ttycontrol* ?settings?+:: + If no arguments are given, returns a dictionary containing the modem control signals + from the stream (must be a serial-type device). e.g. +{rts 1 dtr 1 dcd 0 dsr 0 ring 0 cts 0}+. + Note that +rts+ and +dtr+ are controlled by the local system while the other signals reflect + the remote system. + If arguments are given, they must either be a dictionary, or +setting value \...+. + Abbreviations are supported for both settings and values. + Supported settings are: + + +*rts 0|1*+;; + Set the RTS (Request To Send) signal + + +*dtr 0|1*+;; + Set the DTR (Data Terminal Ready) signal + + +*break* 'duration'+;; + Generate a break condition. +duration+ is generally ignored but may be used + in a platform-dependent manner. + +$handle *ssl* ?*-server* 'cert ?key?'|*-sni* 'servername'?+:: Upgrades the stream to a SSL/TLS session and returns the handle. If +-server+ is specified, either both the certificate and private key files -- cgit v1.1