aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2020-05-05 22:50:27 +1000
committerSteve Bennett <steveb@workware.net.au>2024-02-04 10:08:26 +1000
commit67e32c82fa2248a70a02d0828b8785b9a25692b0 (patch)
treef5c00e2b085121e8c4965f7052e7d12819d51ab3
parent474bab96aaa42526fdd5283a925e540679ae0bfe (diff)
downloadjimtcl-67e32c82fa2248a70a02d0828b8785b9a25692b0.zip
jimtcl-67e32c82fa2248a70a02d0828b8785b9a25692b0.tar.gz
jimtcl-67e32c82fa2248a70a02d0828b8785b9a25692b0.tar.bz2
aio: Add support for modem control signals
RTS, DTR, etc. and sending a break condition Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r--jim-aio.c50
-rw-r--r--jim-tty.c92
-rw-r--r--jim-tty.h16
-rw-r--r--jim_tcl.txt21
4 files changed, 178 insertions, 1 deletions
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 <jim-tty.h>
+#include <sys/ioctl.h>
#include <termios.h>
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