qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 1/1] chardev: enable guest socket status/crontrol via DTR and DCD
@ 2020-12-16 22:06 Darrin M. Gorski
  2020-12-17 14:16 ` Marc-André Lureau
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Darrin M. Gorski @ 2020-12-16 22:06 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

[-- Attachment #1: Type: text/plain, Size: 7893 bytes --]

This patch adds a 'modemctl' option to "-chardev socket" to enable control
of the socket via the guest serial port.
The default state of the option is disabled.

1. disconnect a connected socket when DTR transitions to low, also reject
new connections while DTR is low.
2. provide socket connection status through the carrier detect line (CD or
DCD) on the guest serial port

Buglink: https://bugs.launchpad.net/qemu/+bug/1213196

Signed-off-by: Darrin M. Gorski <darrin@gorski.net>


diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 213a4c8dd0..94dd28e0cd 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -36,6 +36,7 @@
 #include "qapi/qapi-visit-sockets.h"

 #include "chardev/char-io.h"
+#include "chardev/char-serial.h"
 #include "qom/object.h"

 /***********************************************************/
@@ -85,6 +86,9 @@ struct SocketChardev {
     bool connect_err_reported;

     QIOTask *connect_task;
+
+    bool is_modemctl;
+    int modem_state;
 };
 typedef struct SocketChardev SocketChardev;

@@ -98,12 +102,18 @@ static void tcp_chr_change_state(SocketChardev *s,
TCPChardevState state)
 {
     switch (state) {
     case TCP_CHARDEV_STATE_DISCONNECTED:
+        if(s->is_modemctl) {
+            s->modem_state &= ~(CHR_TIOCM_CAR);
+        }
         break;
     case TCP_CHARDEV_STATE_CONNECTING:
         assert(s->state == TCP_CHARDEV_STATE_DISCONNECTED);
         break;
     case TCP_CHARDEV_STATE_CONNECTED:
         assert(s->state == TCP_CHARDEV_STATE_CONNECTING);
+        if(s->is_modemctl) {
+            s->modem_state |= CHR_TIOCM_CAR;
+        }
         break;
     }
     s->state = state;
@@ -947,6 +957,12 @@ static void tcp_chr_accept(QIONetListener *listener,
     tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
     tcp_chr_set_client_ioc_name(chr, cioc);
     tcp_chr_new_client(chr, cioc);
+
+    if(s->is_modemctl) {
+        if(!(s->modem_state & CHR_TIOCM_DTR)) {
+            tcp_chr_disconnect(chr); /* disconnect if DTR is low */
+        }
+    }
 }


@@ -1322,12 +1338,17 @@ static void qmp_chardev_open_socket(Chardev *chr,
     bool is_tn3270      = sock->has_tn3270  ? sock->tn3270  : false;
     bool is_waitconnect = sock->has_wait    ? sock->wait    : false;
     bool is_websock     = sock->has_websocket ? sock->websocket : false;
+    bool is_modemctl     = sock->has_modemctl ? sock->modemctl : false;
     int64_t reconnect   = sock->has_reconnect ? sock->reconnect : 0;
     SocketAddress *addr;

     s->is_listen = is_listen;
     s->is_telnet = is_telnet;
     s->is_tn3270 = is_tn3270;
+    s->is_modemctl = is_modemctl;
+    if(is_modemctl) {
+      s->modem_state = CHR_TIOCM_CTS | CHR_TIOCM_DSR;
+    }
     s->is_websock = is_websock;
     s->do_nodelay = do_nodelay;
     if (sock->tls_creds) {
@@ -1448,6 +1469,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts,
ChardevBackend *backend,
     sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds"));
     sock->has_tls_authz = qemu_opt_get(opts, "tls-authz");
     sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz"));
+    sock->has_modemctl = qemu_opt_get(opts, "modemctl");
+    sock->modemctl = qemu_opt_get_bool(opts, "modemctl", false);

     addr = g_new0(SocketAddressLegacy, 1);
     if (path) {
@@ -1501,6 +1524,51 @@ char_socket_get_connected(Object *obj, Error **errp)
     return s->state == TCP_CHARDEV_STATE_CONNECTED;
 }

+/* ioctl support: allow guest to control/track socket state
+ * via modem control lines (DCD/DTR)
+ */
+static int
+char_socket_ioctl(Chardev *chr, int cmd, void *arg)
+{
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
+    if(!(s->is_modemctl)) {
+        return -ENOTSUP;
+    }
+
+    switch (cmd) {
+        case CHR_IOCTL_SERIAL_GET_TIOCM:
+            {
+                int *targ = (int *)arg;
+                *targ = s->modem_state;
+            }
+            break;
+        case CHR_IOCTL_SERIAL_SET_TIOCM:
+            {
+                int sarg = *(int *)arg;
+                if (sarg & CHR_TIOCM_RTS) {
+                    s->modem_state |= CHR_TIOCM_RTS;
+                } else {
+                    s->modem_state &= ~(CHR_TIOCM_RTS);
+                }
+                if (sarg & CHR_TIOCM_DTR) {
+                    s->modem_state |= CHR_TIOCM_DTR;
+                } else {
+                    s->modem_state &= ~(CHR_TIOCM_DTR);
+                    /* disconnect if DTR goes low */
+                    if(s->state == TCP_CHARDEV_STATE_CONNECTED) {
+                        tcp_chr_disconnect(chr);
+                    }
+                }
+            }
+            break;
+        default:
+            return -ENOTSUP;
+    }
+
+    return 0;
+}
+
 static void char_socket_class_init(ObjectClass *oc, void *data)
 {
     ChardevClass *cc = CHARDEV_CLASS(oc);
@@ -1516,6 +1584,7 @@ static void char_socket_class_init(ObjectClass *oc,
void *data)
     cc->chr_add_client = tcp_chr_add_client;
     cc->chr_add_watch = tcp_chr_add_watch;
     cc->chr_update_read_handler = tcp_chr_update_read_handler;
+    cc->chr_ioctl = char_socket_ioctl;

     object_class_property_add(oc, "addr", "SocketAddress",
                               char_socket_get_addr, NULL,
diff --git a/chardev/char.c b/chardev/char.c
index a9b8c5a9aa..601d818f81 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -928,6 +928,9 @@ QemuOptsList qemu_chardev_opts = {
         },{
             .name = "logappend",
             .type = QEMU_OPT_BOOL,
+        },{
+            .name = "modemctl",
+            .type = QEMU_OPT_BOOL,
 #ifdef CONFIG_LINUX
         },{
             .name = "tight",
diff --git a/qapi/char.json b/qapi/char.json
index 58338ed62d..f352bd6350 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -271,6 +271,9 @@
 #             then attempt a reconnect after the given number of seconds.
 #             Setting this to zero disables this function. (default: 0)
 #             (Since: 2.2)
+# @modemctl: allow guest to use modem control signals to control/monitor
+#            the socket state (CD follows is_connected, DTR influences
+#            connect/accept) (default: false) (Since: 5.2)
 #
 # Since: 1.4
 ##
@@ -284,6 +287,7 @@
             '*telnet': 'bool',
             '*tn3270': 'bool',
             '*websocket': 'bool',
+            '*modemctl': 'bool',
             '*reconnect': 'int' },
   'base': 'ChardevCommon' }

diff --git a/qemu-options.hx b/qemu-options.hx
index 459c916d3d..f09072afbf 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2991,11 +2991,13 @@ DEFHEADING(Character device options:)
 DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev help\n"
     "-chardev null,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
-    "-chardev
socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n"
-    "
[,server][,nowait][,telnet][,websocket][,reconnect=seconds][,mux=on|off]\n"
-    "
[,logfile=PATH][,logappend=on|off][,tls-creds=ID][,tls-authz=ID] (tcp)\n"
-    "-chardev
socket,id=id,path=path[,server][,nowait][,telnet][,websocket][,reconnect=seconds]\n"
-    "
[,mux=on|off][,logfile=PATH][,logappend=on|off][,abstract=on|off][,tight=on|off]
(unix)\n"
+    "-chardev
socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay]\n"
+    "
[,reconnect=seconds][,server][,nowait][,telnet][,websocket]\n"
+    "         [,modemctl][,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
+    "         [,tls-creds=ID][,tls-authz=ID] (tcp)\n"
+    "-chardev
socket,id=id,path=path[,server][,nowait][,telnet][,websocket]\n"
+    "
[,reconnect=seconds][,modemctl][,mux=on|off][,logfile=PATH]\n"
+    "         [,logappend=on|off][,abstract=on|off][,tight=on|off]
(unix)\n"
     "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
     "         [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
     "         [,logfile=PATH][,logappend=on|off]\n"

[-- Attachment #2: Type: text/html, Size: 10502 bytes --]

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2022-01-12 18:55 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-16 22:06 [PATCH v1 1/1] chardev: enable guest socket status/crontrol via DTR and DCD Darrin M. Gorski
2020-12-17 14:16 ` Marc-André Lureau
2020-12-17 17:54   ` Darrin M. Gorski
2020-12-18 11:57     ` Marc-André Lureau
2020-12-19  5:34       ` Darrin M. Gorski
2020-12-23 22:13 ` Eric Blake
2022-01-12 15:20 ` Aaro Koskinen
2022-01-12 18:50   ` Darrin M. Gorski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).