From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,HTML_MESSAGE,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7CC4FC4361B for ; Thu, 17 Dec 2020 17:55:42 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id CF0992376F for ; Thu, 17 Dec 2020 17:55:41 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org CF0992376F Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=gorski.net Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:41304 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kpxV6-00013S-PG for qemu-devel@archiver.kernel.org; Thu, 17 Dec 2020 12:55:40 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:48984) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kpxU5-0000KD-Pq for qemu-devel@nongnu.org; Thu, 17 Dec 2020 12:54:38 -0500 Received: from mail-ej1-x629.google.com ([2a00:1450:4864:20::629]:36624) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kpxU2-00044d-MD for qemu-devel@nongnu.org; Thu, 17 Dec 2020 12:54:37 -0500 Received: by mail-ej1-x629.google.com with SMTP id lt17so39169693ejb.3 for ; Thu, 17 Dec 2020 09:54:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gorski-net.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=3peo1d5Eqb/mnemDcbdzwuo6To60qf6ijDpbyVv3dmg=; b=Q+ikAaIvAqs48xMbZ/r5UNvkHgp3R2IO6lkgyAwiTSx3F7VmBEpBjiTY+pp36Ixasj WvWKMhXJbif+yxMIAt2exaAJ9G7TsOgqJVaOJFBH88TYuMkq+i/lCSxbWxOJi+FvitZq AL6sbVgazheRxOnfY/QyGRRz8lETfiBMzYx/dDfKLDFHp+MNJJiuWQJy7TD43HBirCg1 R0mb3kpuf1+0zKdvRGPTKo9JZPemdORbbtoQscNrP1+MXPyd2We6aA7MrRhDFd2mt91e tDEZDCsOUzeV+1QRXKZDfB2Qvl/ihKJSq/GXBBapHUAjJc6KDWFSqQ314ZdpU5zrixDE Xl6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=3peo1d5Eqb/mnemDcbdzwuo6To60qf6ijDpbyVv3dmg=; b=POUx12AttH/2Iqn0XP8lBWZ+5FEJAWlWRHQJLiZVDo0Cn4hiA2S93ck8WJwk9s1A4w yyXT/wGR9Fx79YddWaESiyQwEqN0m2QKWizwE68SlyjpXNcma1OTQ2D/tz2GDZ5hrNMW Fqt/WwuhmrCJoyDv0y70lRd6exHW66stVkcKI/MAvrQpfEMfqKmkdssBUVZuGGJ7ssHE AIAME4JNUSbAxat4RBcsERIV/wKM4QozRcrgOeXLM3R2rIQ9nBs1WsC9Y0D1x/i286ET gaTvq481Bdm5dba071jet5sAIPEUQ7juCTXAd2IsyxhR7NufTdcajjIEX9usTsiaSxvc pHpQ== X-Gm-Message-State: AOAM532+JVLMJExGc3KIQWjXNEFnVgq7/ne+Bj3d2wX1+gBbWel1r+vW XyqzksZiCiXBO+hVwm7t2ypyLPcyIRaWNK+LmrGwFQ== X-Google-Smtp-Source: ABdhPJy4eP/P4e9eaow78GPfcZpQOfhc+25rVIYhiBeUR94EjgvDCuTEuqQGuVAgUOnmk1lDRCUZSBaT/1p6j0RYrPo= X-Received: by 2002:a17:906:40d3:: with SMTP id a19mr182479ejk.98.1608227673047; Thu, 17 Dec 2020 09:54:33 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: "Darrin M. Gorski" Date: Thu, 17 Dec 2020 12:54:21 -0500 Message-ID: Subject: Re: [PATCH v1 1/1] chardev: enable guest socket status/crontrol via DTR and DCD To: =?UTF-8?B?TWFyYy1BbmRyw6kgTHVyZWF1?= Content-Type: multipart/alternative; boundary="0000000000002b9d0f05b6acaf46" Received-SPF: pass client-ip=2a00:1450:4864:20::629; envelope-from=darrin@gorski.net; helo=mail-ej1-x629.google.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paolo Bonzini , QEMU Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" --0000000000002b9d0f05b6acaf46 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable > That sounds like a good idea to me, but others may have different opinions. My original idea was simply to allow DCD to track socket state, because this is what I need. The DTR idea came from the referenced bug. The feature defaults to disabled like many of the other edge cases (like telnet and tn3270), and it's a reasonably small non-intrusive change. > Can we make this generic over all chardevs by moving it to the base class= ? I'll take a closer look at the base class. I admit I did not spend much time studying it. This seemed like a socket specific feature to me. It seems like it might conflict with other chardevs like pty and serial as they are already using ioctl for interaction with real tty devices. > This feature will need some new tests in tests/test-char.c. I would be happy to add tests but would need some guidance. - Darrin On Thu, Dec 17, 2020 at 9:16 AM Marc-Andr=C3=A9 Lureau < marcandre.lureau@gmail.com> wrote: > Hi > > On Thu, Dec 17, 2020 at 2:54 AM Darrin M. Gorski > wrote: > >> >> 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 rejec= t >> new connections while DTR is low. >> 2. provide socket connection status through the carrier detect line (CD >> or DCD) on the guest serial port >> > > That sounds like a good idea to me, but others may have different opinion= s. > > >> Buglink: https://bugs.launchpad.net/qemu/+bug/1213196 >> >> Signed-off-by: Darrin M. Gorski >> >> >> 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; >> > > Can we make this generic over all chardevs by moving it to the base class= ? > > }; >> 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 &=3D ~(CHR_TIOCM_CAR); >> + } >> break; >> case TCP_CHARDEV_STATE_CONNECTING: >> assert(s->state =3D=3D TCP_CHARDEV_STATE_DISCONNECTED); >> break; >> case TCP_CHARDEV_STATE_CONNECTED: >> assert(s->state =3D=3D TCP_CHARDEV_STATE_CONNECTING); >> + if(s->is_modemctl) { >> + s->modem_state |=3D CHR_TIOCM_CAR; >> + } >> break; >> } >> s->state =3D 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 =3D sock->has_tn3270 ? sock->tn3270 : false; >> bool is_waitconnect =3D sock->has_wait ? sock->wait : false; >> bool is_websock =3D sock->has_websocket ? sock->websocket : fal= se; >> + bool is_modemctl =3D sock->has_modemctl ? sock->modemctl : fals= e; >> int64_t reconnect =3D sock->has_reconnect ? sock->reconnect : 0; >> SocketAddress *addr; >> >> s->is_listen =3D is_listen; >> s->is_telnet =3D is_telnet; >> s->is_tn3270 =3D is_tn3270; >> + s->is_modemctl =3D is_modemctl; >> + if(is_modemctl) { >> + s->modem_state =3D CHR_TIOCM_CTS | CHR_TIOCM_DSR; >> + } >> s->is_websock =3D is_websock; >> s->do_nodelay =3D do_nodelay; >> if (sock->tls_creds) { >> @@ -1448,6 +1469,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, >> ChardevBackend *backend, >> sock->tls_creds =3D g_strdup(qemu_opt_get(opts, "tls-creds")); >> sock->has_tls_authz =3D qemu_opt_get(opts, "tls-authz"); >> sock->tls_authz =3D g_strdup(qemu_opt_get(opts, "tls-authz")); >> + sock->has_modemctl =3D qemu_opt_get(opts, "modemctl"); >> + sock->modemctl =3D qemu_opt_get_bool(opts, "modemctl", false); >> >> addr =3D g_new0(SocketAddressLegacy, 1); >> if (path) { >> @@ -1501,6 +1524,51 @@ char_socket_get_connected(Object *obj, Error >> **errp) >> return s->state =3D=3D 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 =3D SOCKET_CHARDEV(chr); >> + >> + if(!(s->is_modemctl)) { >> + return -ENOTSUP; >> + } >> + >> + switch (cmd) { >> + case CHR_IOCTL_SERIAL_GET_TIOCM: >> + { >> + int *targ =3D (int *)arg; >> + *targ =3D s->modem_state; >> + } >> + break; >> + case CHR_IOCTL_SERIAL_SET_TIOCM: >> + { >> + int sarg =3D *(int *)arg; >> + if (sarg & CHR_TIOCM_RTS) { >> + s->modem_state |=3D CHR_TIOCM_RTS; >> + } else { >> + s->modem_state &=3D ~(CHR_TIOCM_RTS); >> + } >> + if (sarg & CHR_TIOCM_DTR) { >> + s->modem_state |=3D CHR_TIOCM_DTR; >> + } else { >> + s->modem_state &=3D ~(CHR_TIOCM_DTR); >> + /* disconnect if DTR goes low */ >> + if(s->state =3D=3D 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 =3D CHARDEV_CLASS(oc); >> @@ -1516,6 +1584,7 @@ static void char_socket_class_init(ObjectClass *oc= , >> void *data) >> cc->chr_add_client =3D tcp_chr_add_client; >> cc->chr_add_watch =3D tcp_chr_add_watch; >> cc->chr_update_read_handler =3D tcp_chr_update_read_handler; >> + cc->chr_ioctl =3D 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 =3D { >> },{ >> .name =3D "logappend", >> .type =3D QEMU_OPT_BOOL, >> + },{ >> + .name =3D "modemctl", >> + .type =3D QEMU_OPT_BOOL, >> #ifdef CONFIG_LINUX >> },{ >> .name =3D "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 second= s. >> # Setting this to zero disables this function. (default: 0) >> # (Since: 2.2) >> +# @modemctl: allow guest to use modem control signals to control/monito= r >> +# 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=3Did[,mux=3Don|off][,logfile=3DPATH][,logappend=3Don|off]\n" >> - "-chardev >> socket,id=3Did[,host=3Dhost],port=3Dport[,to=3Dto][,ipv4][,ipv6][,nodela= y][,reconnect=3Dseconds]\n" >> - " >> [,server][,nowait][,telnet][,websocket][,reconnect=3Dseconds][,mux=3Don|= off]\n" >> - " >> [,logfile=3DPATH][,logappend=3Don|off][,tls-creds=3DID][,tls-authz=3DID]= (tcp)\n" >> - "-chardev >> socket,id=3Did,path=3Dpath[,server][,nowait][,telnet][,websocket][,recon= nect=3Dseconds]\n" >> - " >> [,mux=3Don|off][,logfile=3DPATH][,logappend=3Don|off][,abstract=3Don|off= ][,tight=3Don|off] >> (unix)\n" >> + "-chardev >> socket,id=3Did[,host=3Dhost],port=3Dport[,to=3Dto][,ipv4][,ipv6][,nodela= y]\n" >> + " >> [,reconnect=3Dseconds][,server][,nowait][,telnet][,websocket]\n" >> + " >> [,modemctl][,mux=3Don|off][,logfile=3DPATH][,logappend=3Don|off]\n" >> + " [,tls-creds=3DID][,tls-authz=3DID] (tcp)\n" >> + "-chardev >> socket,id=3Did,path=3Dpath[,server][,nowait][,telnet][,websocket]\n" >> + " >> [,reconnect=3Dseconds][,modemctl][,mux=3Don|off][,logfile=3DPATH]\n" >> + " [,logappend=3Don|off][,abstract=3Don|off][,tight=3Don|off= ] >> (unix)\n" >> "-chardev udp,id=3Did[,host=3Dhost],port=3Dport[,localaddr=3Dlocala= ddr]\n" >> " [,localport=3Dlocalport][,ipv4][,ipv6][,mux=3Don|off]\n" >> " [,logfile=3DPATH][,logappend=3Don|off]\n" >> > > This feature will need some new tests in tests/test-char.c. > > -- > Marc-Andr=C3=A9 Lureau > --0000000000002b9d0f05b6acaf46 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable

> That soun= ds like a good idea to me, but others may have different opinions.

My original idea was simply to allow DCD to tra= ck socket state,=C2=A0because this is what I need.=C2=A0 The DTR idea came = from the referenced bug.=C2=A0 The feature defaults to disabled like many o= f the other edge cases (like telnet and tn3270), and it's a reasonably = small non-intrusive change.

> Can we make this = generic over all chardevs by moving it to the base class?
<= div>
I'll take a closer look at the base class. I admit I= did not spend much time studying it.=C2=A0 This seemed like a socket=C2=A0= specific feature to me.=C2=A0 It seems like it might conflict=C2=A0with oth= er chardevs like pty and serial as they are already using ioctl for interac= tion with real tty devices.

> This feature will= need some new tests in tests/test-char.c.

I would= be happy to add tests but would need some guidance.

- Darrin
On Thu, Dec 17, 2020 at 9:16 AM Marc-Andr=C3=A9 Lureau <marcandre.lureau@gmail.com>= wrote:
Hi

On Thu, Dec 17, 2020 at 2:54 AM Darrin M. G= orski <darrin@gor= ski.net> wrote:

This patch adds a 'modemctl' option to &q= uot;-chardev socket" to enable control of the socket via the guest ser= ial port.
The default state = of the option is disabled.

1. d= isconnect a connected socket when DTR transitions to low, also reject new c= onnections while DTR is low.
2. provide socket connection status through the carrier detect line (C= D or DCD) on the guest serial port

That sounds like a good idea to me, but others may have di= fferent opinions.

<= /blockquote>

Can we make this generic over all chardevs = by moving it to the base class?

= =C2=A0};
=C2=A0typedef struct SocketChardev SocketChardev;

@@ -98= ,12 +102,18 @@ static void tcp_chr_change_state(SocketChardev *s, TCPCharde= vState state)
=C2=A0{
=C2=A0 =C2=A0 =C2=A0switch (state) {
=C2=A0 = =C2=A0 =C2=A0case TCP_CHARDEV_STATE_DISCONNECTED:
+ =C2=A0 =C2=A0 =C2=A0= =C2=A0if(s->is_modemctl) {
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0s->modem_state &=3D ~(CHR_TIOCM_CAR);
+ =C2=A0 =C2=A0 =C2=A0 = =C2=A0}
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
=C2=A0 =C2=A0 =C2=A0= case TCP_CHARDEV_STATE_CONNECTING:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ass= ert(s->state =3D=3D TCP_CHARDEV_STATE_DISCONNECTED);
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0break;
=C2=A0 =C2=A0 =C2=A0case TCP_CHARDEV_STATE_CO= NNECTED:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0assert(s->state =3D=3D TCP= _CHARDEV_STATE_CONNECTING);
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0if(s->is_mod= emctl) {
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->modem_state |= =3D CHR_TIOCM_CAR;
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0break;
=C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0s-&= gt;state =3D state;
@@ -947,6 +957,12 @@ static void tcp_chr_accept(QION= etListener *listener,
=C2=A0 =C2=A0 =C2=A0tcp_chr_change_state(s, TCP_CH= ARDEV_STATE_CONNECTING);
=C2=A0 =C2=A0 =C2=A0tcp_chr_set_client_ioc_name= (chr, cioc);
=C2=A0 =C2=A0 =C2=A0tcp_chr_new_client(chr, cioc);
+
= + =C2=A0 =C2=A0if(s->is_modemctl) {
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0if(!= (s->modem_state & CHR_TIOCM_DTR)) {
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0tcp_chr_disconnect(chr); /* disconnect if DTR is low */
+ = =C2=A0 =C2=A0 =C2=A0 =C2=A0}
+ =C2=A0 =C2=A0}
=C2=A0}


@@ -= 1322,12 +1338,17 @@ static void qmp_chardev_open_socket(Chardev *chr,
= =C2=A0 =C2=A0 =C2=A0bool is_tn3270 =C2=A0 =C2=A0 =C2=A0=3D sock->has_tn3= 270 =C2=A0? sock->tn3270 =C2=A0: false;
=C2=A0 =C2=A0 =C2=A0bool is_w= aitconnect =3D sock->has_wait =C2=A0 =C2=A0? sock->wait =C2=A0 =C2=A0= : false;
=C2=A0 =C2=A0 =C2=A0bool is_websock =C2=A0 =C2=A0 =3D sock->= has_websocket ? sock->websocket : false;
+ =C2=A0 =C2=A0bool is_modem= ctl =C2=A0 =C2=A0 =3D sock->has_modemctl ? sock->modemctl : false;=C2=A0 =C2=A0 =C2=A0int64_t reconnect =C2=A0 =3D sock->has_reconnect ? = sock->reconnect : 0;
=C2=A0 =C2=A0 =C2=A0SocketAddress *addr;

= =C2=A0 =C2=A0 =C2=A0s->is_listen =3D is_listen;
=C2=A0 =C2=A0 =C2=A0s= ->is_telnet =3D is_telnet;
=C2=A0 =C2=A0 =C2=A0s->is_tn3270 =3D is= _tn3270;
+ =C2=A0 =C2=A0s->is_modemctl =3D is_modemctl;
+ =C2=A0 = =C2=A0if(is_modemctl) {
+ =C2=A0 =C2=A0 =C2=A0s->modem_state =3D CHR_= TIOCM_CTS | CHR_TIOCM_DSR;
+ =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0s->= ;is_websock =3D is_websock;
=C2=A0 =C2=A0 =C2=A0s->do_nodelay =3D do_= nodelay;
=C2=A0 =C2=A0 =C2=A0if (sock->tls_creds) {
@@ -1448,6 +14= 69,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *b= ackend,
=C2=A0 =C2=A0 =C2=A0sock->tls_creds =3D g_strdup(qemu_opt_get= (opts, "tls-creds"));
=C2=A0 =C2=A0 =C2=A0sock->has_tls_aut= hz =3D qemu_opt_get(opts, "tls-authz");
=C2=A0 =C2=A0 =C2=A0so= ck->tls_authz =3D g_strdup(qemu_opt_get(opts, "tls-authz"));+ =C2=A0 =C2=A0sock->has_modemctl =3D qemu_opt_get(opts, "modemct= l");
+ =C2=A0 =C2=A0sock->modemctl =3D qemu_opt_get_bool(opts, &= quot;modemctl", false);

=C2=A0 =C2=A0 =C2=A0addr =3D g_new0(Soc= ketAddressLegacy, 1);
=C2=A0 =C2=A0 =C2=A0if (path) {
@@ -1501,6 +152= 4,51 @@ char_socket_get_connected(Object *obj, Error **errp)
=C2=A0 =C2= =A0 =C2=A0return s->state =3D=3D TCP_CHARDEV_STATE_CONNECTED;
=C2=A0}=

+/* ioctl support: allow guest to control/track socket state
+ *= via modem control lines (DCD/DTR)
+ */
+static int
+char_socket_i= octl(Chardev *chr, int cmd, void *arg)
+{
+ =C2=A0 =C2=A0SocketCharde= v *s =3D SOCKET_CHARDEV(chr);
+
+ =C2=A0 =C2=A0if(!(s->is_modemctl= )) {
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0return -ENOTSUP;
+ =C2=A0 =C2=A0}+
+ =C2=A0 =C2=A0switch (cmd) {
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0case C= HR_IOCTL_SERIAL_GET_TIOCM:
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{<= br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int *targ =3D (= int *)arg;
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*tar= g =3D s->modem_state;
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
+ =C2=A0 =C2=A0 =C2= =A0 =C2=A0case CHR_IOCTL_SERIAL_SET_TIOCM:
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0{
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0int sarg =3D *(int *)arg;
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0if (sarg & CHR_TIOCM_RTS) {
+ =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->modem_state |=3D CHR_TIO= CM_RTS;
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else = {
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0s->modem_state &=3D ~(CHR_TIOCM_RTS);
+ =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0if (sarg & CHR_TIOCM_DTR) {
+ =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->modem_state |=3D = CHR_TIOCM_DTR;
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= } else {
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0s->modem_state &=3D ~(CHR_TIOCM_DTR);
+ =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* disconnect if DTR= goes low */
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0if(s->state =3D=3D TCP_CHARDEV_STATE_CONNECTED) {
+ =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0tcp_chr_disconnect(chr);
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0}
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
+ = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
+ =C2=A0 =C2=A0 =C2=A0 = =C2=A0default:
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return -ENOTSU= P;
+ =C2=A0 =C2=A0}
+
+ =C2=A0 =C2=A0return 0;
+}
+
=C2= =A0static void char_socket_class_init(ObjectClass *oc, void *data)
=C2= =A0{
=C2=A0 =C2=A0 =C2=A0ChardevClass *cc =3D CHARDEV_CLASS(oc);
@@ -= 1516,6 +1584,7 @@ static void char_socket_class_init(ObjectClass *oc, void = *data)
=C2=A0 =C2=A0 =C2=A0cc->chr_add_client =3D tcp_chr_add_client;=
=C2=A0 =C2=A0 =C2=A0cc->chr_add_watch =3D tcp_chr_add_watch;
=C2= =A0 =C2=A0 =C2=A0cc->chr_update_read_handler =3D tcp_chr_update_read_han= dler;
+ =C2=A0 =C2=A0cc->chr_ioctl =3D char_socket_ioctl;

=C2= =A0 =C2=A0 =C2=A0object_class_property_add(oc, "addr", "Sock= etAddress",
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0char_socket_get_add= r, NULL,
diff --git a/chardev/char.c b/chardev/char.c
index a9b8c5a9a= a..601d818f81 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ = -928,6 +928,9 @@ QemuOptsList qemu_chardev_opts =3D {
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0},{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.na= me =3D "logappend",
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0.type =3D QEMU_OPT_BOOL,
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0},{
+ =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =3D "modemctl",
+ = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.type =3D QEMU_OPT_BOOL,
=C2=A0= #ifdef CONFIG_LINUX
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0},{
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.name =3D "tight",
diff = --git a/qapi/char.json b/qapi/char.json
index 58338ed62d..f352bd6350 100= 644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -271,6 +271,9 @@<= br>=C2=A0# =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 then attempt a reconne= ct after the given number of seconds.
=C2=A0# =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 Setting this to zero disables this function. (default: 0)=
=C2=A0# =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (Since: 2.2)
+# @m= odemctl: allow guest to use modem control signals to control/monitor
+# = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0the socket state (CD follows is_co= nnected, DTR influences
+# =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0conn= ect/accept) (default: false) (Since: 5.2)
=C2=A0#
=C2=A0# Since: 1.4<= br>=C2=A0##
@@ -284,6 +287,7 @@
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0'*telnet': 'bool',
=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0'*tn3270': 'bool',
=C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'*websocket': 'bool',+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'*modemctl': 'bool&= #39;,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'*reconnect= 9;: 'int' },
=C2=A0 =C2=A0'base': 'ChardevCommon'= ; }

diff --git a/qemu-options.hx b/qemu-options.hx
index 459c916d= 3d..f09072afbf 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
= @@ -2991,11 +2991,13 @@ DEFHEADING(Character device options:)
=C2=A0DEF(= "chardev", HAS_ARG, QEMU_OPTION_chardev,
=C2=A0 =C2=A0 =C2=A0&= quot;-chardev help\n"
=C2=A0 =C2=A0 =C2=A0"-chardev null,id=3D= id[,mux=3Don|off][,logfile=3DPATH][,logappend=3Don|off]\n"
- =C2=A0= =C2=A0"-chardev socket,id=3Did[,host=3Dhost],port=3Dport[,to=3Dto][,i= pv4][,ipv6][,nodelay][,reconnect=3Dseconds]\n"
- =C2=A0 =C2=A0"= ; =C2=A0 =C2=A0 =C2=A0 =C2=A0 [,server][,nowait][,telnet][,websocket][,reco= nnect=3Dseconds][,mux=3Don|off]\n"
- =C2=A0 =C2=A0" =C2=A0 =C2= =A0 =C2=A0 =C2=A0 [,logfile=3DPATH][,logappend=3Don|off][,tls-creds=3DID][,= tls-authz=3DID] (tcp)\n"
- =C2=A0 =C2=A0"-chardev socket,id=3D= id,path=3Dpath[,server][,nowait][,telnet][,websocket][,reconnect=3Dseconds]= \n"
- =C2=A0 =C2=A0" =C2=A0 =C2=A0 =C2=A0 =C2=A0 [,mux=3Don|of= f][,logfile=3DPATH][,logappend=3Don|off][,abstract=3Don|off][,tight=3Don|of= f] (unix)\n"
+ =C2=A0 =C2=A0"-chardev socket,id=3Did[,host=3Dh= ost],port=3Dport[,to=3Dto][,ipv4][,ipv6][,nodelay]\n"
+ =C2=A0 =C2= =A0" =C2=A0 =C2=A0 =C2=A0 =C2=A0 [,reconnect=3Dseconds][,server][,nowa= it][,telnet][,websocket]\n"
+ =C2=A0 =C2=A0" =C2=A0 =C2=A0 =C2= =A0 =C2=A0 [,modemctl][,mux=3Don|off][,logfile=3DPATH][,logappend=3Don|off]= \n"
+ =C2=A0 =C2=A0" =C2=A0 =C2=A0 =C2=A0 =C2=A0 [,tls-creds= =3DID][,tls-authz=3DID] (tcp)\n"
+ =C2=A0 =C2=A0"-chardev sock= et,id=3Did,path=3Dpath[,server][,nowait][,telnet][,websocket]\n"
+ = =C2=A0 =C2=A0" =C2=A0 =C2=A0 =C2=A0 =C2=A0 [,reconnect=3Dseconds][,mod= emctl][,mux=3Don|off][,logfile=3DPATH]\n"
+ =C2=A0 =C2=A0" =C2= =A0 =C2=A0 =C2=A0 =C2=A0 [,logappend=3Don|off][,abstract=3Don|off][,tight= =3Don|off] (unix)\n"
=C2=A0 =C2=A0 =C2=A0"-chardev udp,id=3Did= [,host=3Dhost],port=3Dport[,localaddr=3Dlocaladdr]\n"
=C2=A0 =C2=A0= =C2=A0" =C2=A0 =C2=A0 =C2=A0 =C2=A0 [,localport=3Dlocalport][,ipv4][,= ipv6][,mux=3Don|off]\n"
=C2=A0 =C2=A0 =C2=A0" =C2=A0 =C2=A0 = =C2=A0 =C2=A0 [,logfile=3DPATH][,logappend=3Don|off]\n"



--
Marc-Andr=C3= =A9 Lureau
--0000000000002b9d0f05b6acaf46--