All of lore.kernel.org
 help / color / mirror / Atom feed
From: Peter Hunt <peter.hunt@opengear.com>
To: loic.poulain@oss.qualcomm.com, ryazanov.s.a@gmail.com, mani@kernel.org
Cc: johannes@sipsolutions.net, netdev@vger.kernel.org,
	mhi@lists.linux.dev, linux-arm-msm@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Peter Hunt <peter.hunt@opengear.com>
Subject: [PATCH net-next v4 1/3] net: wwan: core: propagate modem control signals to port drivers
Date: Sun, 16 Aug 2026 06:17:03 -0600	[thread overview]
Message-ID: <20260816121705.858013-2-peter.hunt@opengear.com> (raw)
In-Reply-To: <20260816121705.858013-1-peter.hunt@opengear.com>

The WWAN character device emulates the TTY modem-control ioctls
(TIOCMGET/TIOCMSET/TIOCMBIC/TIOCMBIS) for AT and QCDM ports, but the
result is only stored in port->at_data.mdmbits and never reaches the port
driver. A driver therefore cannot act on the host raising or dropping
DTR/RTS, even though some modems depend on it (e.g. they withhold
unsolicited AT result codes until the host asserts DTR).

Add an optional ->dtr_rts(port, on) operation to struct wwan_port_ops,
mirroring tty_port_operations.dtr_rts. Drivers that implement it receive
a simple assert/de-assert signal while the TIOCM bitmask state is tracked
by the wwan core. TIOCMSET/TIOCMBIC/TIOCMBIS resolve the new bitmask and
call ->dtr_rts when the driver implements it, gated on WWAN_PORT_AT to
match the open/close raise/drop behaviour below. The boolean passed
reflects DTR only, since DTR is the line that governs URC gating on
modems supporting this signalling; RTS is tracked in mdmbits but driven
together with DTR on open/close.

Also raise DTR/RTS in wwan_port_op_start on first open of an AT port when
the driver implements ->dtr_rts, and drop them in wwan_port_op_stop on
last close. This mirrors TTY semantics (DTR is asserted on open) and means
individual drivers do not need to implement this themselves.

at_data.mdmbits is protected by data_lock; the ->dtr_rts call is made
after releasing data_lock and re-acquiring ops_lock so that it is
serialised against port removal (which nulls port->ops under ops_lock).

Signed-off-by: Peter Hunt <peter.hunt@opengear.com>
---
v4: Protect at_data.mdmbits in wwan_port_op_start/stop under data_lock;
    release data_lock and acquire ops_lock with a NULL check before calling
    ->dtr_rts from the ioctl path; gate ioctl ->dtr_rts on WWAN_PORT_AT to
    match open/close behaviour; reduce boolean to TIOCM_DTR only
v3: Replace ->tiocmget/->tiocmset with ->dtr_rts(port, bool on) modelled
    on tty_port_operations.dtr_rts; raise/drop DTR/RTS in
    wwan_port_op_start/stop rather than in the driver (Loic Poulain)
---
 drivers/net/wwan/wwan_core.c | 31 ++++++++++++++++++++++++++++++-
 include/linux/wwan.h         |  3 +++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index ffbcf11e4e68..90ca85a63b5a 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -759,8 +759,17 @@ static int wwan_port_op_start(struct wwan_port *port)
 	if (!port->start_count)
 		ret = port->ops->start(port);
 
-	if (!ret)
+	if (!ret) {
 		port->start_count++;
+		/* Mirror TTY semantics: raise DTR/RTS on first open of an AT port */
+		if (port->start_count == 1 && port->type == WWAN_PORT_AT &&
+		    port->ops->dtr_rts) {
+			mutex_lock(&port->data_lock);
+			port->at_data.mdmbits |= TIOCM_DTR | TIOCM_RTS;
+			mutex_unlock(&port->data_lock);
+			port->ops->dtr_rts(port, true);
+		}
+	}
 
 out_unlock:
 	mutex_unlock(&port->ops_lock);
@@ -773,6 +782,13 @@ static void wwan_port_op_stop(struct wwan_port *port)
 	mutex_lock(&port->ops_lock);
 	port->start_count--;
 	if (!port->start_count) {
+		/* Mirror TTY semantics: drop DTR/RTS on last close of an AT port */
+		if (port->ops && port->type == WWAN_PORT_AT && port->ops->dtr_rts) {
+			mutex_lock(&port->data_lock);
+			port->at_data.mdmbits &= ~(TIOCM_DTR | TIOCM_RTS);
+			mutex_unlock(&port->data_lock);
+			port->ops->dtr_rts(port, false);
+		}
 		if (port->ops)
 			port->ops->stop(port);
 		skb_queue_purge(&port->rxq);
@@ -980,6 +996,8 @@ static long wwan_port_fops_at_ioctl(struct wwan_port *port, unsigned int cmd,
 				    unsigned long arg)
 {
 	int ret = 0;
+	bool call_dtr_rts = false;
+	bool dtr_on = false;
 
 	mutex_lock(&port->data_lock);
 
@@ -1036,6 +1054,10 @@ static long wwan_port_fops_at_ioctl(struct wwan_port *port, unsigned int cmd,
 			port->at_data.mdmbits |= mdmbits;
 		else
 			port->at_data.mdmbits = mdmbits;
+		if (port->type == WWAN_PORT_AT) {
+			dtr_on = !!(port->at_data.mdmbits & TIOCM_DTR);
+			call_dtr_rts = true;
+		}
 		break;
 	}
 
@@ -1061,6 +1083,13 @@ static long wwan_port_fops_at_ioctl(struct wwan_port *port, unsigned int cmd,
 
 	mutex_unlock(&port->data_lock);
 
+	if (call_dtr_rts) {
+		mutex_lock(&port->ops_lock);
+		if (port->ops && port->ops->dtr_rts)
+			port->ops->dtr_rts(port, dtr_on);
+		mutex_unlock(&port->ops_lock);
+	}
+
 	return ret;
 }
 
diff --git a/include/linux/wwan.h b/include/linux/wwan.h
index 1e0e2cb53579..a7684950d73b 100644
--- a/include/linux/wwan.h
+++ b/include/linux/wwan.h
@@ -57,6 +57,8 @@ struct wwan_port;
  * @tx_blocking: Optional blocking routine that sends WWAN port protocol data
  *               to the device.
  * @tx_poll: Optional routine that sets additional TX poll flags.
+ * @dtr_rts: Optional routine that asserts (on=true) or de-asserts (on=false)
+ *           the DTR and RTS modem control lines.
  *
  * The wwan_port_ops structure contains a list of low-level operations
  * that control a WWAN port device. All functions are mandatory unless specified.
@@ -70,6 +72,7 @@ struct wwan_port_ops {
 	int (*tx_blocking)(struct wwan_port *port, struct sk_buff *skb);
 	__poll_t (*tx_poll)(struct wwan_port *port, struct file *filp,
 			    poll_table *wait);
+	void (*dtr_rts)(struct wwan_port *port, bool on);
 };
 
 /** struct wwan_port_caps - The WWAN port capbilities
-- 
2.43.0


  reply	other threads:[~2026-08-16 12:18 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-16 12:17 [PATCH net-next v4 0/3] net: wwan: support DTR/RTS on AT ports via MHI IP_CTRL Peter Hunt
2026-08-16 12:17 ` Peter Hunt [this message]
2026-08-18 17:09   ` [PATCH net-next v4 1/3] net: wwan: core: propagate modem control signals to port drivers Jakub Kicinski
2026-08-16 12:17 ` [PATCH net-next v4 2/3] bus: mhi: host: pci_generic: enumerate IP_CTRL channel for Sierra EM919x/EM929x Peter Hunt
2026-08-16 12:17 ` [PATCH net-next v4 3/3] net: wwan: mhi_wwan_ctrl: drive DTR/RTS via the IP_CTRL channel Peter Hunt
2026-08-18 17:09   ` Jakub Kicinski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260816121705.858013-2-peter.hunt@opengear.com \
    --to=peter.hunt@opengear.com \
    --cc=johannes@sipsolutions.net \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=loic.poulain@oss.qualcomm.com \
    --cc=mani@kernel.org \
    --cc=mhi@lists.linux.dev \
    --cc=netdev@vger.kernel.org \
    --cc=ryazanov.s.a@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.