public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v5 0/7] net: wwan: add NMEA port type support
@ 2026-01-09  1:09 Sergey Ryazanov
  2026-01-09  1:09 ` [RFC PATCH v5 1/7] net: wwan: core: remove unused port_id field Sergey Ryazanov
                   ` (7 more replies)
  0 siblings, 8 replies; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-09  1:09 UTC (permalink / raw)
  To: Loic Poulain, Johannes Berg
  Cc: Andrew Lunn, Eric Dumazet, David S . Miller, Jakub Kicinski,
	Paolo Abeni, netdev, Slark Xiao, Muhammad Nuzaihan,
	Daniele Palmas, Qiang Yu, Manivannan Sadhasivam, Johan Hovold

The series introduces a long discussed NMEA port type support for the
WWAN subsystem. There are two goals. From the WWAN driver perspective,
NMEA exported as any other port type (e.g. AT, MBIM, QMI, etc.). From
user space software perspective, the exported chardev belongs to the
GNSS class what makes it easy to distinguish desired port and the WWAN
device common to both NMEA and control (AT, MBIM, etc.) ports makes it
easy to locate a control port for the GNSS receiver activation.

Done by exporting the NMEA port via the GNSS subsystem with the WWAN
core acting as proxy between the WWAN modem driver and the GNSS
subsystem.

The series starts from a cleanup patch. Then three patches prepares the
WWAN core for the proxy style operation. Followed by a patch introding a
new WWNA port type, integration with the GNSS subsystem and demux. The
series ends with a couple of patches that introduce emulated EMEA port
to the WWAN HW simulator.

The series is the product of the discussion with Loic about the pros and
cons of possible models and implementation. Also Muhammad and Slark did
a great job defining the problem, sharing the code and pushing me to
finish the implementation. Daniele has caught an issue on driver
unloading and suggested an investigation direction. What was concluded
by Loic. Many thanks.

Slark, if this series with the unregister fix suits you, please bundle
it with your MHI patch, and (re-)send for final inclusion.

Changes RFCv1->RFCv2:
* Uniformly use put_device() to release port memory. This made code less
  weird and way more clear. Thank you, Loic, for noticing and the fix
  discussion!
Changes RFCv2->RFCv5:
* Fix premature WWAN device unregister; new patch 2/7, thus, all
  subsequent patches have been renumbered
* Minor adjustments here and there

CC: Slark Xiao <slark_xiao@163.com>
CC: Muhammad Nuzaihan <zaihan@unrealasia.net>
CC: Daniele Palmas <dnlplm@gmail.com>
CC: Qiang Yu <quic_qianyu@quicinc.com>
CC: Manivannan Sadhasivam <mani@kernel.org>
CC: Johan Hovold <johan@kernel.org>

Sergey Ryazanov (7):
  net: wwan: core: remove unused port_id field
  net: wwan: core: explicit WWAN device reference counting
  net: wwan: core: split port creation and registration
  net: wwan: core: split port unregister and stop
  net: wwan: add NMEA port support
  net: wwan: hwsim: refactor to support more port types
  net: wwan: hwsim: support NMEA port emulation

 drivers/net/wwan/Kconfig      |   1 +
 drivers/net/wwan/wwan_core.c  | 280 +++++++++++++++++++++++++++-------
 drivers/net/wwan/wwan_hwsim.c | 201 +++++++++++++++++++-----
 include/linux/wwan.h          |   2 +
 4 files changed, 396 insertions(+), 88 deletions(-)

-- 
2.52.0


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

* [RFC PATCH v5 1/7] net: wwan: core: remove unused port_id field
  2026-01-09  1:09 [RFC PATCH v5 0/7] net: wwan: add NMEA port type support Sergey Ryazanov
@ 2026-01-09  1:09 ` Sergey Ryazanov
  2026-01-09  1:09 ` [RFC PATCH v5 2/7] net: wwan: core: explicit WWAN device reference counting Sergey Ryazanov
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-09  1:09 UTC (permalink / raw)
  To: Loic Poulain, Johannes Berg
  Cc: Andrew Lunn, Eric Dumazet, David S . Miller, Jakub Kicinski,
	Paolo Abeni, netdev

It was used initially for a port id allocation, then removed, and then
accidently introduced again, but it is still unused. Drop it again to
keep code clean.

Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
 drivers/net/wwan/wwan_core.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index 63a47d420bc5..ade8bbffc93e 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -43,7 +43,6 @@ static struct dentry *wwan_debugfs_dir;
  *
  * @id: WWAN device unique ID.
  * @dev: Underlying device.
- * @port_id: Current available port ID to pick.
  * @ops: wwan device ops
  * @ops_ctxt: context to pass to ops
  * @debugfs_dir:  WWAN device debugfs dir
@@ -51,7 +50,6 @@ static struct dentry *wwan_debugfs_dir;
 struct wwan_device {
 	unsigned int id;
 	struct device dev;
-	atomic_t port_id;
 	const struct wwan_ops *ops;
 	void *ops_ctxt;
 #ifdef CONFIG_WWAN_DEBUGFS
-- 
2.52.0


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

* [RFC PATCH v5 2/7] net: wwan: core: explicit WWAN device reference counting
  2026-01-09  1:09 [RFC PATCH v5 0/7] net: wwan: add NMEA port type support Sergey Ryazanov
  2026-01-09  1:09 ` [RFC PATCH v5 1/7] net: wwan: core: remove unused port_id field Sergey Ryazanov
@ 2026-01-09  1:09 ` Sergey Ryazanov
  2026-01-14 21:31   ` Loic Poulain
  2026-01-09  1:09 ` [RFC PATCH v5 3/7] net: wwan: core: split port creation and registration Sergey Ryazanov
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-09  1:09 UTC (permalink / raw)
  To: Loic Poulain, Johannes Berg
  Cc: Andrew Lunn, Eric Dumazet, David S . Miller, Jakub Kicinski,
	Paolo Abeni, netdev, Daniele Palmas

We need information about existing WWAN device children since we remove
the device after removing the last child. Previously, we tracked users
implicitly by checking whether ops was registered and existence of a
child device of the wwan_class class. Upcoming GNSS (NMEA) port type
support breaks this approach by introducing a child device of the
gnss_class class.

And a modem driver can easily trigger a kernel Oops by removing regular
(e.g., MBIM, AT) ports first and then removing a GNSS port. The WWAN
device will be unregistered on removal of a last regular WWAN port. And
subsequent GNSS port removal will cause NULL pointer dereference in
simple_recursive_removal().

In order to support ports of classes other than wwan_class, switch to
explicit references counting. Introduce a dedicated counter to the WWAN
device struct, increment it on every wwan_create_dev() call, decrement
on wwan_remove_dev(), and actually unregister the WWAN device when there
are no more references.

Run tested with wwan_hwsim with NMEA support patches applied and
different port removing sequences.

Reported-by: Daniele Palmas <dnlplm@gmail.com>
Closes: https://lore.kernel.org/netdev/CAGRyCJE28yf-rrfkFbzu44ygLEvoUM7fecK1vnrghjG_e9UaRA@mail.gmail.com/
Suggested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
Changes:
* RFCv2->RFCv5: new patch to address modem disconnection / system
  shutdown issues
---
 drivers/net/wwan/wwan_core.c | 37 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index ade8bbffc93e..33f7a140fba9 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -42,6 +42,9 @@ static struct dentry *wwan_debugfs_dir;
  * struct wwan_device - The structure that defines a WWAN device
  *
  * @id: WWAN device unique ID.
+ * @refcount: Reference count of this WWAN device. When this refcount reaches
+ * zero, the device is deleted. NB: access is protected by global
+ * wwan_register_lock mutex.
  * @dev: Underlying device.
  * @ops: wwan device ops
  * @ops_ctxt: context to pass to ops
@@ -49,6 +52,7 @@ static struct dentry *wwan_debugfs_dir;
  */
 struct wwan_device {
 	unsigned int id;
+	int refcount;
 	struct device dev;
 	const struct wwan_ops *ops;
 	void *ops_ctxt;
@@ -222,8 +226,10 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
 
 	/* If wwandev already exists, return it */
 	wwandev = wwan_dev_get_by_parent(parent);
-	if (!IS_ERR(wwandev))
+	if (!IS_ERR(wwandev)) {
+		wwandev->refcount++;
 		goto done_unlock;
+	}
 
 	id = ida_alloc(&wwan_dev_ids, GFP_KERNEL);
 	if (id < 0) {
@@ -242,6 +248,7 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
 	wwandev->dev.class = &wwan_class;
 	wwandev->dev.type = &wwan_dev_type;
 	wwandev->id = id;
+	wwandev->refcount = 1;
 	dev_set_name(&wwandev->dev, "wwan%d", wwandev->id);
 
 	err = device_register(&wwandev->dev);
@@ -263,30 +270,21 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
 	return wwandev;
 }
 
-static int is_wwan_child(struct device *dev, void *data)
-{
-	return dev->class == &wwan_class;
-}
-
 static void wwan_remove_dev(struct wwan_device *wwandev)
 {
-	int ret;
-
 	/* Prevent concurrent picking from wwan_create_dev */
 	mutex_lock(&wwan_register_lock);
 
-	/* WWAN device is created and registered (get+add) along with its first
-	 * child port, and subsequent port registrations only grab a reference
-	 * (get). The WWAN device must then be unregistered (del+put) along with
-	 * its last port, and reference simply dropped (put) otherwise. In the
-	 * same fashion, we must not unregister it when the ops are still there.
-	 */
-	if (wwandev->ops)
-		ret = 1;
-	else
-		ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child);
+	if (--wwandev->refcount <= 0) {
+		struct device *child = device_find_any_child(&wwandev->dev);
+
+		if (WARN_ON(wwandev->ops))	/* Paranoid */
+			goto out_unlock;
+		if (WARN_ON(child)) {		/* Paranoid */
+			put_device(child);
+			goto out_unlock;
+		}
 
-	if (!ret) {
 #ifdef CONFIG_WWAN_DEBUGFS
 		debugfs_remove_recursive(wwandev->debugfs_dir);
 #endif
@@ -295,6 +293,7 @@ static void wwan_remove_dev(struct wwan_device *wwandev)
 		put_device(&wwandev->dev);
 	}
 
+out_unlock:
 	mutex_unlock(&wwan_register_lock);
 }
 
-- 
2.52.0


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

* [RFC PATCH v5 3/7] net: wwan: core: split port creation and registration
  2026-01-09  1:09 [RFC PATCH v5 0/7] net: wwan: add NMEA port type support Sergey Ryazanov
  2026-01-09  1:09 ` [RFC PATCH v5 1/7] net: wwan: core: remove unused port_id field Sergey Ryazanov
  2026-01-09  1:09 ` [RFC PATCH v5 2/7] net: wwan: core: explicit WWAN device reference counting Sergey Ryazanov
@ 2026-01-09  1:09 ` Sergey Ryazanov
  2026-01-09  1:09 ` [RFC PATCH v5 4/7] net: wwan: core: split port unregister and stop Sergey Ryazanov
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-09  1:09 UTC (permalink / raw)
  To: Loic Poulain, Johannes Berg
  Cc: Andrew Lunn, Eric Dumazet, David S . Miller, Jakub Kicinski,
	Paolo Abeni, netdev

Upcoming GNSS (NMEA) port type support requires exporting it via the
GNSS subsystem. On another hand, we still need to do basic WWAN core
work: find or allocate the WWAN device, make it the port parent, etc. To
reuse as much code as possible, split the port creation function into
the registration of a regular WWAN port device, and basic port struct
initialization.

To be able to use put_device() uniformly, break the device_register()
call into device_initialize() and device_add() and call device
initialization earlier.

While at it, fix a minor number leak upon WWAN port registration
failure.

Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
Changes:
* RFCv1->RFCv2: break device_register() into device_initialize() and
  device_add() and use put_device() uniformly. Now, in case of error,
  memory is released in wwan_create_port().
* RFCv2->RFCv5: became 3/7 (was 2/6); fix a minor number leak on
  device_add() failure - thanks Sai!
---
 drivers/net/wwan/wwan_core.c | 68 ++++++++++++++++++++++--------------
 1 file changed, 42 insertions(+), 26 deletions(-)

diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index 33f7a140fba9..4c6d315f4847 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -360,7 +360,8 @@ static void wwan_port_destroy(struct device *dev)
 {
 	struct wwan_port *port = to_wwan_port(dev);
 
-	ida_free(&minors, MINOR(port->dev.devt));
+	if (dev->class == &wwan_class)
+		ida_free(&minors, MINOR(dev->devt));
 	mutex_destroy(&port->data_lock);
 	mutex_destroy(&port->ops_lock);
 	kfree(port);
@@ -439,6 +440,43 @@ static int __wwan_port_dev_assign_name(struct wwan_port *port, const char *fmt)
 	return dev_set_name(&port->dev, "%s", buf);
 }
 
+/* Register a regular WWAN port device (e.g. AT, MBIM, etc.) */
+static int wwan_port_register_wwan(struct wwan_port *port)
+{
+	struct wwan_device *wwandev = to_wwan_dev(port->dev.parent);
+	char namefmt[0x20];
+	int minor, err;
+
+	/* A port is exposed as character device, get a minor */
+	minor = ida_alloc_range(&minors, 0, WWAN_MAX_MINORS - 1, GFP_KERNEL);
+	if (minor < 0)
+		return minor;
+
+	port->dev.class = &wwan_class;
+	port->dev.devt = MKDEV(wwan_major, minor);
+
+	/* allocate unique name based on wwan device id, port type and number */
+	snprintf(namefmt, sizeof(namefmt), "wwan%u%s%%d", wwandev->id,
+		 wwan_port_types[port->type].devsuf);
+
+	/* Serialize ports registration */
+	mutex_lock(&wwan_register_lock);
+
+	__wwan_port_dev_assign_name(port, namefmt);
+	err = device_add(&port->dev);
+
+	mutex_unlock(&wwan_register_lock);
+
+	if (err) {
+		ida_free(&minors, minor);
+		return err;
+	}
+
+	dev_info(&wwandev->dev, "port %s attached\n", dev_name(&port->dev));
+
+	return 0;
+}
+
 struct wwan_port *wwan_create_port(struct device *parent,
 				   enum wwan_port_type type,
 				   const struct wwan_port_ops *ops,
@@ -447,8 +485,7 @@ struct wwan_port *wwan_create_port(struct device *parent,
 {
 	struct wwan_device *wwandev;
 	struct wwan_port *port;
-	char namefmt[0x20];
-	int minor, err;
+	int err;
 
 	if (type > WWAN_PORT_MAX || !ops)
 		return ERR_PTR(-EINVAL);
@@ -460,17 +497,9 @@ struct wwan_port *wwan_create_port(struct device *parent,
 	if (IS_ERR(wwandev))
 		return ERR_CAST(wwandev);
 
-	/* A port is exposed as character device, get a minor */
-	minor = ida_alloc_range(&minors, 0, WWAN_MAX_MINORS - 1, GFP_KERNEL);
-	if (minor < 0) {
-		err = minor;
-		goto error_wwandev_remove;
-	}
-
 	port = kzalloc(sizeof(*port), GFP_KERNEL);
 	if (!port) {
 		err = -ENOMEM;
-		ida_free(&minors, minor);
 		goto error_wwandev_remove;
 	}
 
@@ -484,27 +513,14 @@ struct wwan_port *wwan_create_port(struct device *parent,
 	mutex_init(&port->data_lock);
 
 	port->dev.parent = &wwandev->dev;
-	port->dev.class = &wwan_class;
 	port->dev.type = &wwan_port_dev_type;
-	port->dev.devt = MKDEV(wwan_major, minor);
 	dev_set_drvdata(&port->dev, drvdata);
+	device_initialize(&port->dev);
 
-	/* allocate unique name based on wwan device id, port type and number */
-	snprintf(namefmt, sizeof(namefmt), "wwan%u%s%%d", wwandev->id,
-		 wwan_port_types[port->type].devsuf);
-
-	/* Serialize ports registration */
-	mutex_lock(&wwan_register_lock);
-
-	__wwan_port_dev_assign_name(port, namefmt);
-	err = device_register(&port->dev);
-
-	mutex_unlock(&wwan_register_lock);
-
+	err = wwan_port_register_wwan(port);
 	if (err)
 		goto error_put_device;
 
-	dev_info(&wwandev->dev, "port %s attached\n", dev_name(&port->dev));
 	return port;
 
 error_put_device:
-- 
2.52.0


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

* [RFC PATCH v5 4/7] net: wwan: core: split port unregister and stop
  2026-01-09  1:09 [RFC PATCH v5 0/7] net: wwan: add NMEA port type support Sergey Ryazanov
                   ` (2 preceding siblings ...)
  2026-01-09  1:09 ` [RFC PATCH v5 3/7] net: wwan: core: split port creation and registration Sergey Ryazanov
@ 2026-01-09  1:09 ` Sergey Ryazanov
  2026-01-09  1:09 ` [RFC PATCH v5 5/7] net: wwan: add NMEA port support Sergey Ryazanov
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-09  1:09 UTC (permalink / raw)
  To: Loic Poulain, Johannes Berg
  Cc: Andrew Lunn, Eric Dumazet, David S . Miller, Jakub Kicinski,
	Paolo Abeni, netdev

Upcoming GNSS (NMEA) port type support requires exporting it via the
GNSS subsystem. On another hand, we still need to do basic WWAN core
work: call the port stop operation, purge queues, release the parent
WWAN device, etc. To reuse as much code as possible, split the port
unregistering function into the deregistration of a regular WWAN port
device, and the common port tearing down code.

In order to keep more code generic, break the device_unregister() call
into device_del() and put_device(), which release the port memory
uniformly.

Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
Changes:
* RFCv1->RFCv2: break device_unregister() into device_del() and
  put_device() to use later uniformly.
* RFCv2->RFCv5: became 4/7 (was 3/6)
---
 drivers/net/wwan/wwan_core.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index 4c6d315f4847..798b7ef0549e 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -477,6 +477,18 @@ static int wwan_port_register_wwan(struct wwan_port *port)
 	return 0;
 }
 
+/* Unregister a regular WWAN port (e.g. AT, MBIM, etc) */
+static void wwan_port_unregister_wwan(struct wwan_port *port)
+{
+	struct wwan_device *wwandev = to_wwan_dev(port->dev.parent);
+
+	dev_set_drvdata(&port->dev, NULL);
+
+	dev_info(&wwandev->dev, "port %s disconnected\n", dev_name(&port->dev));
+
+	device_del(&port->dev);
+}
+
 struct wwan_port *wwan_create_port(struct device *parent,
 				   enum wwan_port_type type,
 				   const struct wwan_port_ops *ops,
@@ -537,18 +549,19 @@ void wwan_remove_port(struct wwan_port *port)
 	struct wwan_device *wwandev = to_wwan_dev(port->dev.parent);
 
 	mutex_lock(&port->ops_lock);
-	if (port->start_count)
+	if (port->start_count) {
 		port->ops->stop(port);
+		port->start_count = 0;
+	}
 	port->ops = NULL; /* Prevent any new port operations (e.g. from fops) */
 	mutex_unlock(&port->ops_lock);
 
 	wake_up_interruptible(&port->waitqueue);
-
 	skb_queue_purge(&port->rxq);
-	dev_set_drvdata(&port->dev, NULL);
 
-	dev_info(&wwandev->dev, "port %s disconnected\n", dev_name(&port->dev));
-	device_unregister(&port->dev);
+	wwan_port_unregister_wwan(port);
+
+	put_device(&port->dev);
 
 	/* Release related wwan device */
 	wwan_remove_dev(wwandev);
-- 
2.52.0


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

* [RFC PATCH v5 5/7] net: wwan: add NMEA port support
  2026-01-09  1:09 [RFC PATCH v5 0/7] net: wwan: add NMEA port type support Sergey Ryazanov
                   ` (3 preceding siblings ...)
  2026-01-09  1:09 ` [RFC PATCH v5 4/7] net: wwan: core: split port unregister and stop Sergey Ryazanov
@ 2026-01-09  1:09 ` Sergey Ryazanov
  2026-01-09  1:09 ` [RFC PATCH v5 6/7] net: wwan: hwsim: refactor to support more port types Sergey Ryazanov
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-09  1:09 UTC (permalink / raw)
  To: Loic Poulain, Johannes Berg
  Cc: Andrew Lunn, Eric Dumazet, David S . Miller, Jakub Kicinski,
	Paolo Abeni, netdev, Slark Xiao, Muhammad Nuzaihan, Qiang Yu,
	Manivannan Sadhasivam, Johan Hovold

Many WWAN modems come with embedded GNSS receiver inside and have a
dedicated port to output geopositioning data. On the one hand, the
GNSS receiver has little in common with WWAN modem and just shares a
host interface and should be exported using the GNSS subsystem. On the
other hand, GNSS receiver is not automatically activated and needs a
generic WWAN control port (AT, MBIM, etc.) to be turned on. And a user
space software needs extra information to find the control port.

Introduce the new type of WWAN port - NMEA. When driver asks to register
a NMEA port, the core allocates common parent WWAN device as usual, but
exports the NMEA port via the GNSS subsystem and acts as a proxy between
the device driver and the GNSS subsystem.

From the WWAN device driver perspective, a NMEA port is registered as a
regular WWAN port without any difference. And the driver interacts only
with the WWAN core. From the user space perspective, the NMEA port is a
GNSS device which parent can be used to enumerate and select the proper
control port for the GNSS receiver management.

CC: Slark Xiao <slark_xiao@163.com>
CC: Muhammad Nuzaihan <zaihan@unrealasia.net>
CC: Qiang Yu <quic_qianyu@quicinc.com>
CC: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
CC: Johan Hovold <johan@kernel.org>
Suggested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
Changes:
* RFCv2->RFCv5: became 5/7 (was 4/6); add missed gnss field
  documentation
---
 drivers/net/wwan/Kconfig     |   1 +
 drivers/net/wwan/wwan_core.c | 156 +++++++++++++++++++++++++++++++++--
 include/linux/wwan.h         |   2 +
 3 files changed, 154 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig
index 410b0245114e..88df55d78d90 100644
--- a/drivers/net/wwan/Kconfig
+++ b/drivers/net/wwan/Kconfig
@@ -7,6 +7,7 @@ menu "Wireless WAN"
 
 config WWAN
 	tristate "WWAN Driver Core"
+	depends on GNSS || GNSS = n
 	help
 	  Say Y here if you want to use the WWAN driver core. This driver
 	  provides a common framework for WWAN drivers.
diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index 798b7ef0549e..2544caa1ff91 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -1,5 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2021, Linaro Ltd <loic.poulain@linaro.org> */
+/* WWAN Driver Core
+ *
+ * Copyright (c) 2021, Linaro Ltd <loic.poulain@linaro.org>
+ * Copyright (c) 2025, Sergey Ryazanov <ryazanov.s.a@gmail.com>
+ */
 
 #include <linux/bitmap.h>
 #include <linux/err.h>
@@ -16,6 +20,7 @@
 #include <linux/types.h>
 #include <linux/uaccess.h>
 #include <linux/termios.h>
+#include <linux/gnss.h>
 #include <linux/wwan.h>
 #include <net/rtnetlink.h>
 #include <uapi/linux/wwan.h>
@@ -75,6 +80,7 @@ struct wwan_device {
  * @headroom_len: SKB reserved headroom size
  * @frag_len: Length to fragment packet
  * @at_data: AT port specific data
+ * @gnss: Pointer to GNSS device associated with this port
  */
 struct wwan_port {
 	enum wwan_port_type type;
@@ -93,9 +99,16 @@ struct wwan_port {
 			struct ktermios termios;
 			int mdmbits;
 		} at_data;
+		struct gnss_device *gnss;
 	};
 };
 
+static int wwan_port_op_start(struct wwan_port *port);
+static void wwan_port_op_stop(struct wwan_port *port);
+static int wwan_port_op_tx(struct wwan_port *port, struct sk_buff *skb,
+			   bool nonblock);
+static int wwan_wait_tx(struct wwan_port *port, bool nonblock);
+
 static ssize_t index_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct wwan_device *wwan = to_wwan_dev(dev);
@@ -339,6 +352,7 @@ static const struct {
 		.name = "MIPC",
 		.devsuf = "mipc",
 	},
+	/* WWAN_PORT_NMEA is exported via the GNSS subsystem */
 };
 
 static ssize_t type_show(struct device *dev, struct device_attribute *attr,
@@ -489,6 +503,124 @@ static void wwan_port_unregister_wwan(struct wwan_port *port)
 	device_del(&port->dev);
 }
 
+#if IS_ENABLED(CONFIG_GNSS)
+static int wwan_gnss_open(struct gnss_device *gdev)
+{
+	return wwan_port_op_start(gnss_get_drvdata(gdev));
+}
+
+static void wwan_gnss_close(struct gnss_device *gdev)
+{
+	wwan_port_op_stop(gnss_get_drvdata(gdev));
+}
+
+static int wwan_gnss_write(struct gnss_device *gdev, const unsigned char *buf,
+			   size_t count)
+{
+	struct wwan_port *port = gnss_get_drvdata(gdev);
+	struct sk_buff *skb, *head = NULL, *tail = NULL;
+	size_t frag_len, remain = count;
+	int ret;
+
+	ret = wwan_wait_tx(port, false);
+	if (ret)
+		return ret;
+
+	do {
+		frag_len = min(remain, port->frag_len);
+		skb = alloc_skb(frag_len + port->headroom_len, GFP_KERNEL);
+		if (!skb) {
+			ret = -ENOMEM;
+			goto freeskb;
+		}
+		skb_reserve(skb, port->headroom_len);
+		memcpy(skb_put(skb, frag_len), buf + count - remain, frag_len);
+
+		if (!head) {
+			head = skb;
+		} else {
+			if (!tail)
+				skb_shinfo(head)->frag_list = skb;
+			else
+				tail->next = skb;
+
+			tail = skb;
+			head->data_len += skb->len;
+			head->len += skb->len;
+			head->truesize += skb->truesize;
+		}
+	} while (remain -= frag_len);
+
+	ret = wwan_port_op_tx(port, head, false);
+	if (!ret)
+		return count;
+
+freeskb:
+	kfree_skb(head);
+	return ret;
+}
+
+static struct gnss_operations wwan_gnss_ops = {
+	.open = wwan_gnss_open,
+	.close = wwan_gnss_close,
+	.write_raw = wwan_gnss_write,
+};
+
+/* GNSS port specific device registration */
+static int wwan_port_register_gnss(struct wwan_port *port)
+{
+	struct wwan_device *wwandev = to_wwan_dev(port->dev.parent);
+	struct gnss_device *gdev;
+	int err;
+
+	gdev = gnss_allocate_device(&wwandev->dev);
+	if (!gdev)
+		return -ENOMEM;
+
+	/* NB: for now we support only NMEA WWAN port type, so hardcode
+	 * the GNSS port type. If more GNSS WWAN port types will be added,
+	 * then we should dynamically map WWAN port type to GNSS type.
+	 */
+	gdev->type = GNSS_TYPE_NMEA;
+	gdev->ops = &wwan_gnss_ops;
+	gnss_set_drvdata(gdev, port);
+
+	port->gnss = gdev;
+
+	err = gnss_register_device(gdev);
+	if (err) {
+		gnss_put_device(gdev);
+		return err;
+	}
+
+	dev_info(&wwandev->dev, "port %s attached\n", dev_name(&gdev->dev));
+
+	return 0;
+}
+
+/* GNSS port specific device unregistration */
+static void wwan_port_unregister_gnss(struct wwan_port *port)
+{
+	struct wwan_device *wwandev = to_wwan_dev(port->dev.parent);
+	struct gnss_device *gdev = port->gnss;
+
+	dev_info(&wwandev->dev, "port %s disconnected\n", dev_name(&gdev->dev));
+
+	gnss_deregister_device(gdev);
+	gnss_put_device(gdev);
+}
+#else
+static inline int wwan_port_register_gnss(struct wwan_port *port)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void wwan_port_unregister_gnss(struct wwan_port *port)
+{
+	WARN_ON(1);	/* This handler cannot be called */
+}
+#endif
+
 struct wwan_port *wwan_create_port(struct device *parent,
 				   enum wwan_port_type type,
 				   const struct wwan_port_ops *ops,
@@ -529,7 +661,11 @@ struct wwan_port *wwan_create_port(struct device *parent,
 	dev_set_drvdata(&port->dev, drvdata);
 	device_initialize(&port->dev);
 
-	err = wwan_port_register_wwan(port);
+	if (port->type == WWAN_PORT_NMEA)
+		err = wwan_port_register_gnss(port);
+	else
+		err = wwan_port_register_wwan(port);
+
 	if (err)
 		goto error_put_device;
 
@@ -559,7 +695,10 @@ void wwan_remove_port(struct wwan_port *port)
 	wake_up_interruptible(&port->waitqueue);
 	skb_queue_purge(&port->rxq);
 
-	wwan_port_unregister_wwan(port);
+	if (port->type == WWAN_PORT_NMEA)
+		wwan_port_unregister_gnss(port);
+	else
+		wwan_port_unregister_wwan(port);
 
 	put_device(&port->dev);
 
@@ -570,8 +709,15 @@ EXPORT_SYMBOL_GPL(wwan_remove_port);
 
 void wwan_port_rx(struct wwan_port *port, struct sk_buff *skb)
 {
-	skb_queue_tail(&port->rxq, skb);
-	wake_up_interruptible(&port->waitqueue);
+	if (port->type == WWAN_PORT_NMEA) {
+#if IS_ENABLED(CONFIG_GNSS)
+		gnss_insert_raw(port->gnss, skb->data, skb->len);
+#endif
+		consume_skb(skb);
+	} else {
+		skb_queue_tail(&port->rxq, skb);
+		wake_up_interruptible(&port->waitqueue);
+	}
 }
 EXPORT_SYMBOL_GPL(wwan_port_rx);
 
diff --git a/include/linux/wwan.h b/include/linux/wwan.h
index a4d6cc0c9f68..1e0e2cb53579 100644
--- a/include/linux/wwan.h
+++ b/include/linux/wwan.h
@@ -19,6 +19,7 @@
  * @WWAN_PORT_FASTBOOT: Fastboot protocol control
  * @WWAN_PORT_ADB: ADB protocol control
  * @WWAN_PORT_MIPC: MTK MIPC diagnostic interface
+ * @WWAN_PORT_NMEA: embedded GNSS receiver with NMEA output
  *
  * @WWAN_PORT_MAX: Highest supported port types
  * @WWAN_PORT_UNKNOWN: Special value to indicate an unknown port type
@@ -34,6 +35,7 @@ enum wwan_port_type {
 	WWAN_PORT_FASTBOOT,
 	WWAN_PORT_ADB,
 	WWAN_PORT_MIPC,
+	WWAN_PORT_NMEA,
 
 	/* Add new port types above this line */
 
-- 
2.52.0


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

* [RFC PATCH v5 6/7] net: wwan: hwsim: refactor to support more port types
  2026-01-09  1:09 [RFC PATCH v5 0/7] net: wwan: add NMEA port type support Sergey Ryazanov
                   ` (4 preceding siblings ...)
  2026-01-09  1:09 ` [RFC PATCH v5 5/7] net: wwan: add NMEA port support Sergey Ryazanov
@ 2026-01-09  1:09 ` Sergey Ryazanov
  2026-01-09  1:09 ` [RFC PATCH v5 7/7] net: wwan: hwsim: support NMEA port emulation Sergey Ryazanov
  2026-01-09  3:21 ` Re:[RFC PATCH v5 0/7] net: wwan: add NMEA port type support Slark Xiao
  7 siblings, 0 replies; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-09  1:09 UTC (permalink / raw)
  To: Loic Poulain, Johannes Berg
  Cc: Andrew Lunn, Eric Dumazet, David S . Miller, Jakub Kicinski,
	Paolo Abeni, netdev

Just introduced WWAN NMEA port type needs a testing option. The WWAN HW
simulator was developed with the AT port type in mind and cannot be
easily extended. Refactor it now to make it capable to support more port
types.

No big functional changes, mostly renaming with a little code
rearrangement.

Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
Changes:
* RFCv2->RFCv5: became 6/7 (was 5/6)
---
 drivers/net/wwan/wwan_hwsim.c | 73 ++++++++++++++++++++---------------
 1 file changed, 41 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wwan/wwan_hwsim.c b/drivers/net/wwan/wwan_hwsim.c
index b02befd1b6fb..20277ba88433 100644
--- a/drivers/net/wwan/wwan_hwsim.c
+++ b/drivers/net/wwan/wwan_hwsim.c
@@ -56,12 +56,16 @@ struct wwan_hwsim_port {
 	struct wwan_port *wwan;
 	struct work_struct del_work;
 	struct dentry *debugfs_topdir;
-	enum {			/* AT command parser state */
-		AT_PARSER_WAIT_A,
-		AT_PARSER_WAIT_T,
-		AT_PARSER_WAIT_TERM,
-		AT_PARSER_SKIP_LINE,
-	} pstate;
+	union {
+		struct {
+			enum {	/* AT command parser state */
+				AT_PARSER_WAIT_A,
+				AT_PARSER_WAIT_T,
+				AT_PARSER_WAIT_TERM,
+				AT_PARSER_SKIP_LINE,
+			} pstate;
+		} at_emul;
+	};
 };
 
 static const struct file_operations wwan_hwsim_debugfs_portdestroy_fops;
@@ -101,16 +105,16 @@ static const struct wwan_ops wwan_hwsim_wwan_rtnl_ops = {
 	.setup = wwan_hwsim_netdev_setup,
 };
 
-static int wwan_hwsim_port_start(struct wwan_port *wport)
+static int wwan_hwsim_at_emul_start(struct wwan_port *wport)
 {
 	struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport);
 
-	port->pstate = AT_PARSER_WAIT_A;
+	port->at_emul.pstate = AT_PARSER_WAIT_A;
 
 	return 0;
 }
 
-static void wwan_hwsim_port_stop(struct wwan_port *wport)
+static void wwan_hwsim_at_emul_stop(struct wwan_port *wport)
 {
 }
 
@@ -120,7 +124,7 @@ static void wwan_hwsim_port_stop(struct wwan_port *wport)
  *
  * Be aware that this processor is not fully V.250 compliant.
  */
-static int wwan_hwsim_port_tx(struct wwan_port *wport, struct sk_buff *in)
+static int wwan_hwsim_at_emul_tx(struct wwan_port *wport, struct sk_buff *in)
 {
 	struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport);
 	struct sk_buff *out;
@@ -142,17 +146,17 @@ static int wwan_hwsim_port_tx(struct wwan_port *wport, struct sk_buff *in)
 	for (i = 0, s = 0; i < in->len; ++i) {
 		char c = in->data[i];
 
-		if (port->pstate == AT_PARSER_WAIT_A) {
+		if (port->at_emul.pstate == AT_PARSER_WAIT_A) {
 			if (c == 'A' || c == 'a')
-				port->pstate = AT_PARSER_WAIT_T;
+				port->at_emul.pstate = AT_PARSER_WAIT_T;
 			else if (c != '\n')	/* Ignore formating char */
-				port->pstate = AT_PARSER_SKIP_LINE;
-		} else if (port->pstate == AT_PARSER_WAIT_T) {
+				port->at_emul.pstate = AT_PARSER_SKIP_LINE;
+		} else if (port->at_emul.pstate == AT_PARSER_WAIT_T) {
 			if (c == 'T' || c == 't')
-				port->pstate = AT_PARSER_WAIT_TERM;
+				port->at_emul.pstate = AT_PARSER_WAIT_TERM;
 			else
-				port->pstate = AT_PARSER_SKIP_LINE;
-		} else if (port->pstate == AT_PARSER_WAIT_TERM) {
+				port->at_emul.pstate = AT_PARSER_SKIP_LINE;
+		} else if (port->at_emul.pstate == AT_PARSER_WAIT_TERM) {
 			if (c != '\r')
 				continue;
 			/* Consume the trailing formatting char as well */
@@ -162,11 +166,11 @@ static int wwan_hwsim_port_tx(struct wwan_port *wport, struct sk_buff *in)
 			skb_put_data(out, &in->data[s], n);/* Echo */
 			skb_put_data(out, "\r\nOK\r\n", 6);
 			s = i + 1;
-			port->pstate = AT_PARSER_WAIT_A;
-		} else if (port->pstate == AT_PARSER_SKIP_LINE) {
+			port->at_emul.pstate = AT_PARSER_WAIT_A;
+		} else if (port->at_emul.pstate == AT_PARSER_SKIP_LINE) {
 			if (c != '\r')
 				continue;
-			port->pstate = AT_PARSER_WAIT_A;
+			port->at_emul.pstate = AT_PARSER_WAIT_A;
 		}
 	}
 
@@ -183,18 +187,25 @@ static int wwan_hwsim_port_tx(struct wwan_port *wport, struct sk_buff *in)
 	return 0;
 }
 
-static const struct wwan_port_ops wwan_hwsim_port_ops = {
-	.start = wwan_hwsim_port_start,
-	.stop = wwan_hwsim_port_stop,
-	.tx = wwan_hwsim_port_tx,
+static const struct wwan_port_ops wwan_hwsim_at_emul_port_ops = {
+	.start = wwan_hwsim_at_emul_start,
+	.stop = wwan_hwsim_at_emul_stop,
+	.tx = wwan_hwsim_at_emul_tx,
 };
 
-static struct wwan_hwsim_port *wwan_hwsim_port_new(struct wwan_hwsim_dev *dev)
+static struct wwan_hwsim_port *wwan_hwsim_port_new(struct wwan_hwsim_dev *dev,
+						   enum wwan_port_type type)
 {
+	const struct wwan_port_ops *ops;
 	struct wwan_hwsim_port *port;
 	char name[0x10];
 	int err;
 
+	if (type == WWAN_PORT_AT)
+		ops = &wwan_hwsim_at_emul_port_ops;
+	else
+		return ERR_PTR(-EINVAL);
+
 	port = kzalloc(sizeof(*port), GFP_KERNEL);
 	if (!port)
 		return ERR_PTR(-ENOMEM);
@@ -205,9 +216,7 @@ static struct wwan_hwsim_port *wwan_hwsim_port_new(struct wwan_hwsim_dev *dev)
 	port->id = dev->port_idx++;
 	spin_unlock(&dev->ports_lock);
 
-	port->wwan = wwan_create_port(&dev->dev, WWAN_PORT_AT,
-				      &wwan_hwsim_port_ops,
-				      NULL, port);
+	port->wwan = wwan_create_port(&dev->dev, type, ops, NULL, port);
 	if (IS_ERR(port->wwan)) {
 		err = PTR_ERR(port->wwan);
 		goto err_free_port;
@@ -392,7 +401,7 @@ static ssize_t wwan_hwsim_debugfs_portcreate_write(struct file *file,
 	struct wwan_hwsim_dev *dev = file->private_data;
 	struct wwan_hwsim_port *port;
 
-	port = wwan_hwsim_port_new(dev);
+	port = wwan_hwsim_port_new(dev, WWAN_PORT_AT);
 	if (IS_ERR(port))
 		return PTR_ERR(port);
 
@@ -459,6 +468,8 @@ static int __init wwan_hwsim_init_devs(void)
 	int i, j;
 
 	for (i = 0; i < wwan_hwsim_devsnum; ++i) {
+		struct wwan_hwsim_port *port;
+
 		dev = wwan_hwsim_dev_new();
 		if (IS_ERR(dev))
 			return PTR_ERR(dev);
@@ -471,9 +482,7 @@ static int __init wwan_hwsim_init_devs(void)
 		 * the simulator readiness time.
 		 */
 		for (j = 0; j < 2; ++j) {
-			struct wwan_hwsim_port *port;
-
-			port = wwan_hwsim_port_new(dev);
+			port = wwan_hwsim_port_new(dev, WWAN_PORT_AT);
 			if (IS_ERR(port))
 				return PTR_ERR(port);
 
-- 
2.52.0


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

* [RFC PATCH v5 7/7] net: wwan: hwsim: support NMEA port emulation
  2026-01-09  1:09 [RFC PATCH v5 0/7] net: wwan: add NMEA port type support Sergey Ryazanov
                   ` (5 preceding siblings ...)
  2026-01-09  1:09 ` [RFC PATCH v5 6/7] net: wwan: hwsim: refactor to support more port types Sergey Ryazanov
@ 2026-01-09  1:09 ` Sergey Ryazanov
  2026-01-09  3:21 ` Re:[RFC PATCH v5 0/7] net: wwan: add NMEA port type support Slark Xiao
  7 siblings, 0 replies; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-09  1:09 UTC (permalink / raw)
  To: Loic Poulain, Johannes Berg
  Cc: Andrew Lunn, Eric Dumazet, David S . Miller, Jakub Kicinski,
	Paolo Abeni, netdev

Support NMEA port emulation for the WWAN core GNSS port testing purpose.
Emulator produces pair of GGA + RMC sentences every second what should
be enough to fool gpsd into believing it is working with a NMEA GNSS
receiver.

If the GNSS system is enabled then one NMEA port will be created
automatically for the simulated WWAN device. Manual NMEA port creation
is not supported at the moment.

Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
Changes:
* RFCv2->RFCv5: became 7/7 (was 6/6)
---
 drivers/net/wwan/wwan_hwsim.c | 128 +++++++++++++++++++++++++++++++++-
 1 file changed, 126 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wwan/wwan_hwsim.c b/drivers/net/wwan/wwan_hwsim.c
index 20277ba88433..5b5af2c9a63d 100644
--- a/drivers/net/wwan/wwan_hwsim.c
+++ b/drivers/net/wwan/wwan_hwsim.c
@@ -2,7 +2,7 @@
 /*
  * WWAN device simulator for WWAN framework testing.
  *
- * Copyright (c) 2021, Sergey Ryazanov <ryazanov.s.a@gmail.com>
+ * Copyright (c) 2021, 2025, Sergey Ryazanov <ryazanov.s.a@gmail.com>
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -12,8 +12,10 @@
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/spinlock.h>
+#include <linux/time.h>
 #include <linux/list.h>
 #include <linux/skbuff.h>
+#include <linux/timer.h>
 #include <linux/netdevice.h>
 #include <linux/wwan.h>
 #include <linux/debugfs.h>
@@ -65,6 +67,9 @@ struct wwan_hwsim_port {
 				AT_PARSER_SKIP_LINE,
 			} pstate;
 		} at_emul;
+		struct {
+			struct timer_list timer;
+		} nmea_emul;
 	};
 };
 
@@ -193,6 +198,108 @@ static const struct wwan_port_ops wwan_hwsim_at_emul_port_ops = {
 	.tx = wwan_hwsim_at_emul_tx,
 };
 
+#if IS_ENABLED(CONFIG_GNSS)
+#define NMEA_MAX_LEN		82	/* Max sentence length */
+#define NMEA_TRAIL_LEN		5	/* '*' + Checksum + <CR><LF> */
+#define NMEA_MAX_DATA_LEN	(NMEA_MAX_LEN - NMEA_TRAIL_LEN)
+
+static __printf(2, 3)
+void wwan_hwsim_nmea_skb_push_sentence(struct sk_buff *skb,
+				       const char *fmt, ...)
+{
+	unsigned char *s, *p;
+	va_list ap;
+	u8 cs = 0;
+	int len;
+
+	s = skb_put(skb, NMEA_MAX_LEN + 1);	/* +'\0' */
+	if (!s)
+		return;
+
+	va_start(ap, fmt);
+	len = vsnprintf(s, NMEA_MAX_DATA_LEN + 1, fmt, ap);
+	va_end(ap);
+	if (WARN_ON_ONCE(len > NMEA_MAX_DATA_LEN))/* No space for trailer */
+		return;
+
+	for (p = s + 1; *p != '\0'; ++p)/* Skip leading '$' or '!' */
+		cs ^= *p;
+	p += snprintf(p, 5 + 1, "*%02X\r\n", cs);
+
+	len = (p - s) - (NMEA_MAX_LEN + 1);	/* exp. vs real length diff */
+	skb->tail += len;			/* Adjust tail to real length */
+	skb->len += len;
+}
+
+static void wwan_hwsim_nmea_emul_timer(struct timer_list *t)
+{
+	/* 43.74754722298909 N 11.25759835922875 E in DMM format */
+	static const unsigned int coord[4 * 2] = { 43, 44, 8528, 0,
+						   11, 15, 4559, 0 };
+	struct wwan_hwsim_port *port = from_timer(port, t, nmea_emul.timer);
+	struct sk_buff *skb;
+	struct tm tm;
+
+	time64_to_tm(ktime_get_real_seconds(), 0, &tm);
+
+	mod_timer(&port->nmea_emul.timer, jiffies + HZ);	/* 1 second */
+
+	skb = alloc_skb(NMEA_MAX_LEN * 2, GFP_KERNEL);	/* GGA + RMC */
+	if (!skb)
+		return;
+
+	wwan_hwsim_nmea_skb_push_sentence(skb,
+					  "$GPGGA,%02u%02u%02u.000,%02u%02u.%04u,%c,%03u%02u.%04u,%c,1,7,1.03,176.2,M,55.2,M,,",
+					  tm.tm_hour, tm.tm_min, tm.tm_sec,
+					  coord[0], coord[1], coord[2],
+					  coord[3] ? 'S' : 'N',
+					  coord[4], coord[5], coord[6],
+					  coord[7] ? 'W' : 'E');
+
+	wwan_hwsim_nmea_skb_push_sentence(skb,
+					  "$GPRMC,%02u%02u%02u.000,A,%02u%02u.%04u,%c,%03u%02u.%04u,%c,0.02,31.66,%02u%02u%02u,,,A",
+					  tm.tm_hour, tm.tm_min, tm.tm_sec,
+					  coord[0], coord[1], coord[2],
+					  coord[3] ? 'S' : 'N',
+					  coord[4], coord[5], coord[6],
+					  coord[7] ? 'W' : 'E',
+					  tm.tm_mday, tm.tm_mon + 1,
+					  (unsigned int)tm.tm_year - 100);
+
+	wwan_port_rx(port->wwan, skb);
+}
+
+static int wwan_hwsim_nmea_emul_start(struct wwan_port *wport)
+{
+	struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport);
+
+	timer_setup(&port->nmea_emul.timer, wwan_hwsim_nmea_emul_timer, 0);
+	wwan_hwsim_nmea_emul_timer(&port->nmea_emul.timer);
+
+	return 0;
+}
+
+static void wwan_hwsim_nmea_emul_stop(struct wwan_port *wport)
+{
+	struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport);
+
+	timer_delete_sync(&port->nmea_emul.timer);
+}
+
+static int wwan_hwsim_nmea_emul_tx(struct wwan_port *wport, struct sk_buff *in)
+{
+	consume_skb(in);
+
+	return 0;
+}
+
+static const struct wwan_port_ops wwan_hwsim_nmea_emul_port_ops = {
+	.start = wwan_hwsim_nmea_emul_start,
+	.stop = wwan_hwsim_nmea_emul_stop,
+	.tx = wwan_hwsim_nmea_emul_tx,
+};
+#endif
+
 static struct wwan_hwsim_port *wwan_hwsim_port_new(struct wwan_hwsim_dev *dev,
 						   enum wwan_port_type type)
 {
@@ -203,6 +310,10 @@ static struct wwan_hwsim_port *wwan_hwsim_port_new(struct wwan_hwsim_dev *dev,
 
 	if (type == WWAN_PORT_AT)
 		ops = &wwan_hwsim_at_emul_port_ops;
+#if IS_ENABLED(CONFIG_GNSS)
+	else if (type == WWAN_PORT_NMEA)
+		ops = &wwan_hwsim_nmea_emul_port_ops;
+#endif
 	else
 		return ERR_PTR(-EINVAL);
 
@@ -478,9 +589,10 @@ static int __init wwan_hwsim_init_devs(void)
 		list_add_tail(&dev->list, &wwan_hwsim_devs);
 		spin_unlock(&wwan_hwsim_devs_lock);
 
-		/* Create a couple of ports per each device to accelerate
+		/* Create a few various ports per each device to accelerate
 		 * the simulator readiness time.
 		 */
+
 		for (j = 0; j < 2; ++j) {
 			port = wwan_hwsim_port_new(dev, WWAN_PORT_AT);
 			if (IS_ERR(port))
@@ -490,6 +602,18 @@ static int __init wwan_hwsim_init_devs(void)
 			list_add_tail(&port->list, &dev->ports);
 			spin_unlock(&dev->ports_lock);
 		}
+
+#if IS_ENABLED(CONFIG_GNSS)
+		port = wwan_hwsim_port_new(dev, WWAN_PORT_NMEA);
+		if (IS_ERR(port)) {
+			dev_warn(&dev->dev, "failed to create initial NMEA port: %d\n",
+				 (int)PTR_ERR(port));
+		} else {
+			spin_lock(&dev->ports_lock);
+			list_add_tail(&port->list, &dev->ports);
+			spin_unlock(&dev->ports_lock);
+		}
+#endif
 	}
 
 	return 0;
-- 
2.52.0


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

* Re:[RFC PATCH v5 0/7] net: wwan: add NMEA port type support
  2026-01-09  1:09 [RFC PATCH v5 0/7] net: wwan: add NMEA port type support Sergey Ryazanov
                   ` (6 preceding siblings ...)
  2026-01-09  1:09 ` [RFC PATCH v5 7/7] net: wwan: hwsim: support NMEA port emulation Sergey Ryazanov
@ 2026-01-09  3:21 ` Slark Xiao
  2026-01-09  7:11   ` Sergey Ryazanov
  7 siblings, 1 reply; 17+ messages in thread
From: Slark Xiao @ 2026-01-09  3:21 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Loic Poulain, Johannes Berg, Andrew Lunn, Eric Dumazet,
	David S . Miller, Jakub Kicinski, Paolo Abeni, netdev,
	Muhammad Nuzaihan, Daniele Palmas, Qiang Yu,
	Manivannan Sadhasivam, Johan Hovold



At 2026-01-09 09:09:02, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>The series introduces a long discussed NMEA port type support for the
>WWAN subsystem. There are two goals. From the WWAN driver perspective,
>NMEA exported as any other port type (e.g. AT, MBIM, QMI, etc.). From
>user space software perspective, the exported chardev belongs to the
>GNSS class what makes it easy to distinguish desired port and the WWAN
>device common to both NMEA and control (AT, MBIM, etc.) ports makes it
>easy to locate a control port for the GNSS receiver activation.
>
>Done by exporting the NMEA port via the GNSS subsystem with the WWAN
>core acting as proxy between the WWAN modem driver and the GNSS
>subsystem.
>
>The series starts from a cleanup patch. Then three patches prepares the
>WWAN core for the proxy style operation. Followed by a patch introding a
>new WWNA port type, integration with the GNSS subsystem and demux. The
>series ends with a couple of patches that introduce emulated EMEA port
>to the WWAN HW simulator.
>
>The series is the product of the discussion with Loic about the pros and
>cons of possible models and implementation. Also Muhammad and Slark did
>a great job defining the problem, sharing the code and pushing me to
>finish the implementation. Daniele has caught an issue on driver
>unloading and suggested an investigation direction. What was concluded
>by Loic. Many thanks.
>
>Slark, if this series with the unregister fix suits you, please bundle
>it with your MHI patch, and (re-)send for final inclusion.
>
>Changes RFCv1->RFCv2:
>* Uniformly use put_device() to release port memory. This made code less
>  weird and way more clear. Thank you, Loic, for noticing and the fix
>  discussion!
>Changes RFCv2->RFCv5:
>* Fix premature WWAN device unregister; new patch 2/7, thus, all
>  subsequent patches have been renumbered
>* Minor adjustments here and there
>
Shall I keep these RFC changes info in my next commit?
Also these RFC changes info in these single patch.

And I want to know whether  v5 or v6 shall be used for my next serial?
Is there a review progress for these RFC patches ( for patch 2/7 and 
3/7 especially). If yes, I will hold my commit until these review progress
finished. If not, I will combine these changes with my MHI patch and send
them out asap.

>CC: Slark Xiao <slark_xiao@163.com>
>CC: Muhammad Nuzaihan <zaihan@unrealasia.net>
>CC: Daniele Palmas <dnlplm@gmail.com>
>CC: Qiang Yu <quic_qianyu@quicinc.com>
>CC: Manivannan Sadhasivam <mani@kernel.org>
>CC: Johan Hovold <johan@kernel.org>
>
>Sergey Ryazanov (7):
>  net: wwan: core: remove unused port_id field
>  net: wwan: core: explicit WWAN device reference counting
>  net: wwan: core: split port creation and registration
>  net: wwan: core: split port unregister and stop
>  net: wwan: add NMEA port support
>  net: wwan: hwsim: refactor to support more port types
>  net: wwan: hwsim: support NMEA port emulation
>
> drivers/net/wwan/Kconfig      |   1 +
> drivers/net/wwan/wwan_core.c  | 280 +++++++++++++++++++++++++++-------
> drivers/net/wwan/wwan_hwsim.c | 201 +++++++++++++++++++-----
> include/linux/wwan.h          |   2 +
> 4 files changed, 396 insertions(+), 88 deletions(-)
>
>-- 
>2.52.0

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

* Re:[RFC PATCH v5 0/7] net: wwan: add NMEA port type support
  2026-01-09  3:21 ` Re:[RFC PATCH v5 0/7] net: wwan: add NMEA port type support Slark Xiao
@ 2026-01-09  7:11   ` Sergey Ryazanov
  2026-01-13  2:03     ` Slark Xiao
  0 siblings, 1 reply; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-09  7:11 UTC (permalink / raw)
  To: Slark Xiao
  Cc: Loic Poulain, Johannes Berg, Andrew Lunn, Eric Dumazet,
	David S . Miller, Jakub Kicinski, Paolo Abeni, netdev,
	Muhammad Nuzaihan, Daniele Palmas, Qiang Yu,
	Manivannan Sadhasivam, Johan Hovold

On January 9, 2026 5:21:34 AM, Slark Xiao <slark_xiao@163.com> wrote:
>At 2026-01-09 09:09:02, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>The series introduces a long discussed NMEA port type support for the
>>WWAN subsystem. There are two goals. From the WWAN driver perspective,
>>NMEA exported as any other port type (e.g. AT, MBIM, QMI, etc.). From
>>user space software perspective, the exported chardev belongs to the
>>GNSS class what makes it easy to distinguish desired port and the WWAN
>>device common to both NMEA and control (AT, MBIM, etc.) ports makes it
>>easy to locate a control port for the GNSS receiver activation.
>>
>>Done by exporting the NMEA port via the GNSS subsystem with the WWAN
>>core acting as proxy between the WWAN modem driver and the GNSS
>>subsystem.
>>
>>The series starts from a cleanup patch. Then three patches prepares the
>>WWAN core for the proxy style operation. Followed by a patch introding a
>>new WWNA port type, integration with the GNSS subsystem and demux. The
>>series ends with a couple of patches that introduce emulated EMEA port
>>to the WWAN HW simulator.
>>
>>The series is the product of the discussion with Loic about the pros and
>>cons of possible models and implementation. Also Muhammad and Slark did
>>a great job defining the problem, sharing the code and pushing me to
>>finish the implementation. Daniele has caught an issue on driver
>>unloading and suggested an investigation direction. What was concluded
>>by Loic. Many thanks.
>>
>>Slark, if this series with the unregister fix suits you, please bundle
>>it with your MHI patch, and (re-)send for final inclusion.
>>
>>Changes RFCv1->RFCv2:
>>* Uniformly use put_device() to release port memory. This made code less
>>  weird and way more clear. Thank you, Loic, for noticing and the fix
>>  discussion!
>>Changes RFCv2->RFCv5:
>>* Fix premature WWAN device unregister; new patch 2/7, thus, all
>>  subsequent patches have been renumbered
>>* Minor adjustments here and there
>>
>Shall I keep these RFC changes info in my next commit?
>Also these RFC changes info in these single patch.

Generally, yeah, it's a good idea to keep information about changes, especially per item patch. Keeping the cover latter changelog is up to you.

>And I want to know whether  v5 or v6 shall be used for my next serial?

Any of them will work. If you asking me, then I would suggest to send it as v6 to continue numbering.

>Is there a review progress for these RFC patches ( for patch 2/7 and 
>3/7 especially). If yes, I will hold my commit until these review progress
>finished. If not, I will combine these changes with my MHI patch and send
>them out asap.

I have collected all the feedback. E.g., minor number leak was fixed. Fixed one long noticed mistype. And collected two new review tags given by Loic. So, my advice is to use these patches as base and put your MHI patch on top of them.

>>CC: Slark Xiao <slark_xiao@163.com>
>>CC: Muhammad Nuzaihan <zaihan@unrealasia.net>
>>CC: Daniele Palmas <dnlplm@gmail.com>
>>CC: Qiang Yu <quic_qianyu@quicinc.com>
>>CC: Manivannan Sadhasivam <mani@kernel.org>
>>CC: Johan Hovold <johan@kernel.org>
>>
>>Sergey Ryazanov (7):
>>  net: wwan: core: remove unused port_id field
>>  net: wwan: core: explicit WWAN device reference counting
>>  net: wwan: core: split port creation and registration
>>  net: wwan: core: split port unregister and stop
>>  net: wwan: add NMEA port support
>>  net: wwan: hwsim: refactor to support more port types
>>  net: wwan: hwsim: support NMEA port emulation
>>
>> drivers/net/wwan/Kconfig      |   1 +
>> drivers/net/wwan/wwan_core.c  | 280 +++++++++++++++++++++++++++-------
>> drivers/net/wwan/wwan_hwsim.c | 201 +++++++++++++++++++-----
>> include/linux/wwan.h          |   2 +
>> 4 files changed, 396 insertions(+), 88 deletions(-)
>>
>>-- 
>>2.52.0

Hi Slark

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

* Re:Re:[RFC PATCH v5 0/7] net: wwan: add NMEA port type support
  2026-01-09  7:11   ` Sergey Ryazanov
@ 2026-01-13  2:03     ` Slark Xiao
  2026-01-13  6:59       ` Sergey Ryazanov
  0 siblings, 1 reply; 17+ messages in thread
From: Slark Xiao @ 2026-01-13  2:03 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Loic Poulain, Johannes Berg, Andrew Lunn, Eric Dumazet,
	David S . Miller, Jakub Kicinski, Paolo Abeni, netdev,
	Muhammad Nuzaihan, Daniele Palmas, Qiang Yu,
	Manivannan Sadhasivam, Johan Hovold


At 2026-01-09 15:11:58, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>On January 9, 2026 5:21:34 AM, Slark Xiao <slark_xiao@163.com> wrote:
>>At 2026-01-09 09:09:02, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>>The series introduces a long discussed NMEA port type support for the
>>>WWAN subsystem. There are two goals. From the WWAN driver perspective,
>>>NMEA exported as any other port type (e.g. AT, MBIM, QMI, etc.). From
>>>user space software perspective, the exported chardev belongs to the
>>>GNSS class what makes it easy to distinguish desired port and the WWAN
>>>device common to both NMEA and control (AT, MBIM, etc.) ports makes it
>>>easy to locate a control port for the GNSS receiver activation.
>>>
>>>Done by exporting the NMEA port via the GNSS subsystem with the WWAN
>>>core acting as proxy between the WWAN modem driver and the GNSS
>>>subsystem.
>>>
>>>The series starts from a cleanup patch. Then three patches prepares the
>>>WWAN core for the proxy style operation. Followed by a patch introding a
>>>new WWNA port type, integration with the GNSS subsystem and demux. The
>>>series ends with a couple of patches that introduce emulated EMEA port
>>>to the WWAN HW simulator.
>>>
>>>The series is the product of the discussion with Loic about the pros and
>>>cons of possible models and implementation. Also Muhammad and Slark did
>>>a great job defining the problem, sharing the code and pushing me to
>>>finish the implementation. Daniele has caught an issue on driver
>>>unloading and suggested an investigation direction. What was concluded
>>>by Loic. Many thanks.
>>>
>>>Slark, if this series with the unregister fix suits you, please bundle
>>>it with your MHI patch, and (re-)send for final inclusion.
>>>
>>>Changes RFCv1->RFCv2:
>>>* Uniformly use put_device() to release port memory. This made code less
>>>  weird and way more clear. Thank you, Loic, for noticing and the fix
>>>  discussion!
>>>Changes RFCv2->RFCv5:
>>>* Fix premature WWAN device unregister; new patch 2/7, thus, all
>>>  subsequent patches have been renumbered
>>>* Minor adjustments here and there
>>>
>>Shall I keep these RFC changes info in my next commit?
>>Also these RFC changes info in these single patch.
>
>Generally, yeah, it's a good idea to keep information about changes, especially per item patch. Keeping the cover latter changelog is up to you.
>
>>And I want to know whether  v5 or v6 shall be used for my next serial?
>
>Any of them will work. If you asking me, then I would suggest to send it as v6 to continue numbering.
>
>>Is there a review progress for these RFC patches ( for patch 2/7 and 
>>3/7 especially). If yes, I will hold my commit until these review progress
>>finished. If not, I will combine these changes with my MHI patch and send
>>them out asap.
>
>I have collected all the feedback. E.g., minor number leak was fixed. Fixed one long noticed mistype. And collected two new review tags given by Loic. So, my advice is to use these patches as base and put your MHI patch on top of them.
>
Hi Sergey,
I didn't find the review tags for your patch 2/7 and 3/7 until now. Am I missing something?

>>>CC: Slark Xiao <slark_xiao@163.com>
>>>CC: Muhammad Nuzaihan <zaihan@unrealasia.net>
>>>CC: Daniele Palmas <dnlplm@gmail.com>
>>>CC: Qiang Yu <quic_qianyu@quicinc.com>
>>>CC: Manivannan Sadhasivam <mani@kernel.org>
>>>CC: Johan Hovold <johan@kernel.org>
>>>
>>>Sergey Ryazanov (7):
>>>  net: wwan: core: remove unused port_id field
>>>  net: wwan: core: explicit WWAN device reference counting
>>>  net: wwan: core: split port creation and registration
>>>  net: wwan: core: split port unregister and stop
>>>  net: wwan: add NMEA port support
>>>  net: wwan: hwsim: refactor to support more port types
>>>  net: wwan: hwsim: support NMEA port emulation
>>>
>>> drivers/net/wwan/Kconfig      |   1 +
>>> drivers/net/wwan/wwan_core.c  | 280 +++++++++++++++++++++++++++-------
>>> drivers/net/wwan/wwan_hwsim.c | 201 +++++++++++++++++++-----
>>> include/linux/wwan.h          |   2 +
>>> 4 files changed, 396 insertions(+), 88 deletions(-)
>>>
>>>-- 
>>>2.52.0
>
>Hi Slark

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

* Re:Re:[RFC PATCH v5 0/7] net: wwan: add NMEA port type support
  2026-01-13  2:03     ` Slark Xiao
@ 2026-01-13  6:59       ` Sergey Ryazanov
  2026-01-14  2:42         ` Slark Xiao
  0 siblings, 1 reply; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-13  6:59 UTC (permalink / raw)
  To: Slark Xiao
  Cc: Loic Poulain, Johannes Berg, Andrew Lunn, Eric Dumazet,
	David S . Miller, Jakub Kicinski, Paolo Abeni, netdev,
	Muhammad Nuzaihan, Daniele Palmas, Qiang Yu,
	Manivannan Sadhasivam, Johan Hovold

On January 13, 2026 4:03:19 AM, Slark Xiao <slark_xiao@163.com> wrote:
>At 2026-01-09 15:11:58, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>On January 9, 2026 5:21:34 AM, Slark Xiao <slark_xiao@163.com> wrote:
>>>At 2026-01-09 09:09:02, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>>>The series introduces a long discussed NMEA port type support for the
>>>>WWAN subsystem. There are two goals. From the WWAN driver perspective,
>>>>NMEA exported as any other port type (e.g. AT, MBIM, QMI, etc.). From
>>>>user space software perspective, the exported chardev belongs to the
>>>>GNSS class what makes it easy to distinguish desired port and the WWAN
>>>>device common to both NMEA and control (AT, MBIM, etc.) ports makes it
>>>>easy to locate a control port for the GNSS receiver activation.
>>>>
>>>>Done by exporting the NMEA port via the GNSS subsystem with the WWAN
>>>>core acting as proxy between the WWAN modem driver and the GNSS
>>>>subsystem.
>>>>
>>>>The series starts from a cleanup patch. Then three patches prepares the
>>>>WWAN core for the proxy style operation. Followed by a patch introding a
>>>>new WWNA port type, integration with the GNSS subsystem and demux. The
>>>>series ends with a couple of patches that introduce emulated EMEA port
>>>>to the WWAN HW simulator.
>>>>
>>>>The series is the product of the discussion with Loic about the pros and
>>>>cons of possible models and implementation. Also Muhammad and Slark did
>>>>a great job defining the problem, sharing the code and pushing me to
>>>>finish the implementation. Daniele has caught an issue on driver
>>>>unloading and suggested an investigation direction. What was concluded
>>>>by Loic. Many thanks.
>>>>
>>>>Slark, if this series with the unregister fix suits you, please bundle
>>>>it with your MHI patch, and (re-)send for final inclusion.
>>>>
>>>>Changes RFCv1->RFCv2:
>>>>* Uniformly use put_device() to release port memory. This made code less
>>>>  weird and way more clear. Thank you, Loic, for noticing and the fix
>>>>  discussion!
>>>>Changes RFCv2->RFCv5:
>>>>* Fix premature WWAN device unregister; new patch 2/7, thus, all
>>>>  subsequent patches have been renumbered
>>>>* Minor adjustments here and there
>>>>
>>>Shall I keep these RFC changes info in my next commit?
>>>Also these RFC changes info in these single patch.
>>
>>Generally, yeah, it's a good idea to keep information about changes, especially per item patch. Keeping the cover latter changelog is up to you.
>>
>>>And I want to know whether  v5 or v6 shall be used for my next serial?
>>
>>Any of them will work. If you asking me, then I would suggest to send it as v6 to continue numbering.
>>
>>>Is there a review progress for these RFC patches ( for patch 2/7 and 
>>>3/7 especially). If yes, I will hold my commit until these review progress
>>>finished. If not, I will combine these changes with my MHI patch and send
>>>them out asap.
>>
>>I have collected all the feedback. E.g., minor number leak was fixed. Fixed one long noticed mistype. And collected two new review tags given by Loic. So, my advice is to use these patches as base and put your MHI patch on top of them.
>>
>Hi Sergey,
>I didn't find the review tags for your patch 2/7 and 3/7 until now. Am I missing something?

You are right, there are no review tags have been given for these patches. If it works for your device, just send the complete series. You testing going to be enough.

--
Sergey

Hi Slark,

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

* Re:Re:Re:[RFC PATCH v5 0/7] net: wwan: add NMEA port type support
  2026-01-13  6:59       ` Sergey Ryazanov
@ 2026-01-14  2:42         ` Slark Xiao
  2026-01-14 19:59           ` [RFC " Sergey Ryazanov
  0 siblings, 1 reply; 17+ messages in thread
From: Slark Xiao @ 2026-01-14  2:42 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Loic Poulain, Johannes Berg, Andrew Lunn, Eric Dumazet,
	David S . Miller, Jakub Kicinski, Paolo Abeni, netdev,
	Muhammad Nuzaihan, Daniele Palmas, Qiang Yu,
	Manivannan Sadhasivam, Johan Hovold


At 2026-01-13 14:59:36, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>On January 13, 2026 4:03:19 AM, Slark Xiao <slark_xiao@163.com> wrote:
>>At 2026-01-09 15:11:58, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>>On January 9, 2026 5:21:34 AM, Slark Xiao <slark_xiao@163.com> wrote:
>>>>At 2026-01-09 09:09:02, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>>>>The series introduces a long discussed NMEA port type support for the
>>>>>WWAN subsystem. There are two goals. From the WWAN driver perspective,
>>>>>NMEA exported as any other port type (e.g. AT, MBIM, QMI, etc.). From
>>>>>user space software perspective, the exported chardev belongs to the
>>>>>GNSS class what makes it easy to distinguish desired port and the WWAN
>>>>>device common to both NMEA and control (AT, MBIM, etc.) ports makes it
>>>>>easy to locate a control port for the GNSS receiver activation.
>>>>>
>>>>>Done by exporting the NMEA port via the GNSS subsystem with the WWAN
>>>>>core acting as proxy between the WWAN modem driver and the GNSS
>>>>>subsystem.
>>>>>
>>>>>The series starts from a cleanup patch. Then three patches prepares the
>>>>>WWAN core for the proxy style operation. Followed by a patch introding a
>>>>>new WWNA port type, integration with the GNSS subsystem and demux. The
>>>>>series ends with a couple of patches that introduce emulated EMEA port
>>>>>to the WWAN HW simulator.
>>>>>
>>>>>The series is the product of the discussion with Loic about the pros and
>>>>>cons of possible models and implementation. Also Muhammad and Slark did
>>>>>a great job defining the problem, sharing the code and pushing me to
>>>>>finish the implementation. Daniele has caught an issue on driver
>>>>>unloading and suggested an investigation direction. What was concluded
>>>>>by Loic. Many thanks.
>>>>>
>>>>>Slark, if this series with the unregister fix suits you, please bundle
>>>>>it with your MHI patch, and (re-)send for final inclusion.
>>>>>
>>>>>Changes RFCv1->RFCv2:
>>>>>* Uniformly use put_device() to release port memory. This made code less
>>>>>  weird and way more clear. Thank you, Loic, for noticing and the fix
>>>>>  discussion!
>>>>>Changes RFCv2->RFCv5:
>>>>>* Fix premature WWAN device unregister; new patch 2/7, thus, all
>>>>>  subsequent patches have been renumbered
>>>>>* Minor adjustments here and there
>>>>>
>>>>Shall I keep these RFC changes info in my next commit?
>>>>Also these RFC changes info in these single patch.
>>>
>>>Generally, yeah, it's a good idea to keep information about changes, especially per item patch. Keeping the cover latter changelog is up to you.
>>>
>>>>And I want to know whether  v5 or v6 shall be used for my next serial?
>>>
>>>Any of them will work. If you asking me, then I would suggest to send it as v6 to continue numbering.
>>>
>>>>Is there a review progress for these RFC patches ( for patch 2/7 and 
>>>>3/7 especially). If yes, I will hold my commit until these review progress
>>>>finished. If not, I will combine these changes with my MHI patch and send
>>>>them out asap.
>>>
>>>I have collected all the feedback. E.g., minor number leak was fixed. Fixed one long noticed mistype. And collected two new review tags given by Loic. So, my advice is to use these patches as base and put your MHI patch on top of them.
>>>
>>Hi Sergey,
>>I didn't find the review tags for your patch 2/7 and 3/7 until now. Am I missing something?
>
>You are right, there are no review tags have been given for these patches. If it works for your device, just send the complete series. You testing going to be enough.

>

There is an error based on your RFC changes.
It locates in 7/7 net: wwan: hwsim: support NMEA port emulation.
Error as below:

  CC [M]  drivers/input/gameport/fm801-gp.o
drivers/net/wwan/wwan_hwsim.c: In function ‘wwan_hwsim_nmea_emul_timer’:
drivers/net/wwan/wwan_hwsim.c:239:40: error: implicit declaration of function ‘from_timer’; did you mean ‘mod_timer’? [-Werror=implicit-function-declaration]
  239 |         struct wwan_hwsim_port *port = from_timer(port, t, nmea_emul.timer);
      |                                        ^~~~~~~~~~
      |                                        mod_timer
drivers/net/wwan/wwan_hwsim.c:239:60: error: ‘nmea_emul’ undeclared (first use in this function)
  239 |         struct wwan_hwsim_port *port = from_timer(port, t, nmea_emul.timer);
      |                                                            ^~~~~~~~~
drivers/net/wwan/wwan_hwsim.c:239:60: note: each undeclared identifier is reported only once for each function it appears in
cc1: some warnings being treated as errors
make[5]: *** [scripts/Makefile.build:287: drivers/net/wwan/wwan_hwsim.o] Error 1
make[4]: *** [scripts/Makefile.build:544: drivers/net/wwan] Error 2


This error could be found in netdev-bpf website --Checks --netdev/build_32bit as well.
I just rollback it to previous timer_container_of() function and it builds well.


>--
>Sergey
>
>Hi Slark,

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

* Re: [RFC PATCH v5 0/7] net: wwan: add NMEA port type support
  2026-01-14  2:42         ` Slark Xiao
@ 2026-01-14 19:59           ` Sergey Ryazanov
  0 siblings, 0 replies; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-14 19:59 UTC (permalink / raw)
  To: Slark Xiao
  Cc: Loic Poulain, Johannes Berg, Andrew Lunn, Eric Dumazet,
	David S . Miller, Jakub Kicinski, Paolo Abeni, netdev,
	Muhammad Nuzaihan, Daniele Palmas, Qiang Yu,
	Manivannan Sadhasivam, Johan Hovold

On 1/14/26 04:42, Slark Xiao wrote:
> There is an error based on your RFC changes.
> It locates in 7/7 net: wwan: hwsim: support NMEA port emulation.
> Error as below:
> 
>    CC [M]  drivers/input/gameport/fm801-gp.o
> drivers/net/wwan/wwan_hwsim.c: In function ‘wwan_hwsim_nmea_emul_timer’:
> drivers/net/wwan/wwan_hwsim.c:239:40: error: implicit declaration of function ‘from_timer’; did you mean ‘mod_timer’? [-Werror=implicit-function-declaration]
>    239 |         struct wwan_hwsim_port *port = from_timer(port, t, nmea_emul.timer);
>        |                                        ^~~~~~~~~~
>        |                                        mod_timer
> drivers/net/wwan/wwan_hwsim.c:239:60: error: ‘nmea_emul’ undeclared (first use in this function)
>    239 |         struct wwan_hwsim_port *port = from_timer(port, t, nmea_emul.timer);
>        |                                                            ^~~~~~~~~
> drivers/net/wwan/wwan_hwsim.c:239:60: note: each undeclared identifier is reported only once for each function it appears in
> cc1: some warnings being treated as errors
> make[5]: *** [scripts/Makefile.build:287: drivers/net/wwan/wwan_hwsim.o] Error 1
> make[4]: *** [scripts/Makefile.build:544: drivers/net/wwan] Error 2
> 
> 
> This error could be found in netdev-bpf website --Checks --netdev/build_32bit as well.
> I just rollback it to previous timer_container_of() function and it builds well.

Ouch. My bad. Thank you, Slark, for noticing and fixing it.

--
Sergey

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

* Re: [RFC PATCH v5 2/7] net: wwan: core: explicit WWAN device reference counting
  2026-01-09  1:09 ` [RFC PATCH v5 2/7] net: wwan: core: explicit WWAN device reference counting Sergey Ryazanov
@ 2026-01-14 21:31   ` Loic Poulain
  2026-01-15  6:52     ` Sergey Ryazanov
  0 siblings, 1 reply; 17+ messages in thread
From: Loic Poulain @ 2026-01-14 21:31 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev, Daniele Palmas

On Fri, Jan 9, 2026 at 2:09 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> We need information about existing WWAN device children since we remove
> the device after removing the last child. Previously, we tracked users
> implicitly by checking whether ops was registered and existence of a
> child device of the wwan_class class. Upcoming GNSS (NMEA) port type
> support breaks this approach by introducing a child device of the
> gnss_class class.
>
> And a modem driver can easily trigger a kernel Oops by removing regular
> (e.g., MBIM, AT) ports first and then removing a GNSS port. The WWAN
> device will be unregistered on removal of a last regular WWAN port. And
> subsequent GNSS port removal will cause NULL pointer dereference in
> simple_recursive_removal().
>
> In order to support ports of classes other than wwan_class, switch to
> explicit references counting. Introduce a dedicated counter to the WWAN
> device struct, increment it on every wwan_create_dev() call, decrement
> on wwan_remove_dev(), and actually unregister the WWAN device when there
> are no more references.
>
> Run tested with wwan_hwsim with NMEA support patches applied and
> different port removing sequences.
>
> Reported-by: Daniele Palmas <dnlplm@gmail.com>
> Closes: https://lore.kernel.org/netdev/CAGRyCJE28yf-rrfkFbzu44ygLEvoUM7fecK1vnrghjG_e9UaRA@mail.gmail.com/
> Suggested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
> ---
> Changes:
> * RFCv2->RFCv5: new patch to address modem disconnection / system
>   shutdown issues
> ---
>  drivers/net/wwan/wwan_core.c | 37 ++++++++++++++++++------------------
>  1 file changed, 18 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
> index ade8bbffc93e..33f7a140fba9 100644
> --- a/drivers/net/wwan/wwan_core.c
> +++ b/drivers/net/wwan/wwan_core.c
> @@ -42,6 +42,9 @@ static struct dentry *wwan_debugfs_dir;
>   * struct wwan_device - The structure that defines a WWAN device
>   *
>   * @id: WWAN device unique ID.
> + * @refcount: Reference count of this WWAN device. When this refcount reaches
> + * zero, the device is deleted. NB: access is protected by global
> + * wwan_register_lock mutex.
>   * @dev: Underlying device.
>   * @ops: wwan device ops
>   * @ops_ctxt: context to pass to ops
> @@ -49,6 +52,7 @@ static struct dentry *wwan_debugfs_dir;
>   */
>  struct wwan_device {
>         unsigned int id;
> +       int refcount;
>         struct device dev;
>         const struct wwan_ops *ops;
>         void *ops_ctxt;
> @@ -222,8 +226,10 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
>
>         /* If wwandev already exists, return it */
>         wwandev = wwan_dev_get_by_parent(parent);
> -       if (!IS_ERR(wwandev))
> +       if (!IS_ERR(wwandev)) {
> +               wwandev->refcount++;
>                 goto done_unlock;
> +       }
>
>         id = ida_alloc(&wwan_dev_ids, GFP_KERNEL);
>         if (id < 0) {
> @@ -242,6 +248,7 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
>         wwandev->dev.class = &wwan_class;
>         wwandev->dev.type = &wwan_dev_type;
>         wwandev->id = id;
> +       wwandev->refcount = 1;
>         dev_set_name(&wwandev->dev, "wwan%d", wwandev->id);
>
>         err = device_register(&wwandev->dev);
> @@ -263,30 +270,21 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
>         return wwandev;
>  }
>
> -static int is_wwan_child(struct device *dev, void *data)
> -{
> -       return dev->class == &wwan_class;
> -}
> -
>  static void wwan_remove_dev(struct wwan_device *wwandev)
>  {
> -       int ret;
> -
>         /* Prevent concurrent picking from wwan_create_dev */
>         mutex_lock(&wwan_register_lock);

FYI, you can use guarded mutex:
guard(mutex)(&wwan_register_lock);
This ensures the lock is 'automatically' released when leaving the
scope/function, and would save the below goto/out_unlock.

>
> -       /* WWAN device is created and registered (get+add) along with its first
> -        * child port, and subsequent port registrations only grab a reference
> -        * (get). The WWAN device must then be unregistered (del+put) along with
> -        * its last port, and reference simply dropped (put) otherwise. In the
> -        * same fashion, we must not unregister it when the ops are still there.
> -        */
> -       if (wwandev->ops)
> -               ret = 1;
> -       else
> -               ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child);
> +       if (--wwandev->refcount <= 0) {
> +               struct device *child = device_find_any_child(&wwandev->dev);
> +
> +               if (WARN_ON(wwandev->ops))      /* Paranoid */

You may keep a reference to child (if existing)

> +                       goto out_unlock;
> +               if (WARN_ON(child)) {           /* Paranoid */
> +                       put_device(child);
> +                       goto out_unlock;
> +               }

Maybe you can simplify that with:
```
struct device *child = device_find_any_child(&wwandev->dev);
put_device(child) /* NULL param is ok */
if (WARN_ON(child || wwandev->ops))
    return; /* or goto */
```
>
> -       if (!ret) {
>  #ifdef CONFIG_WWAN_DEBUGFS
>                 debugfs_remove_recursive(wwandev->debugfs_dir);
>  #endif
> @@ -295,6 +293,7 @@ static void wwan_remove_dev(struct wwan_device *wwandev)
>                 put_device(&wwandev->dev);
>         }
>
> +out_unlock:
>         mutex_unlock(&wwan_register_lock);
>  }
>
> --
> 2.52.0
>

Regards,
Loic

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

* Re: [RFC PATCH v5 2/7] net: wwan: core: explicit WWAN device reference counting
  2026-01-14 21:31   ` Loic Poulain
@ 2026-01-15  6:52     ` Sergey Ryazanov
  2026-01-15  8:08       ` Slark Xiao
  0 siblings, 1 reply; 17+ messages in thread
From: Sergey Ryazanov @ 2026-01-15  6:52 UTC (permalink / raw)
  To: Loic Poulain, Slark Xiao
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev, Daniele Palmas

On January 14, 2026 11:31:45 PM, Loic Poulain <loic.poulain@oss.qualcomm.com> wrote:
>On Fri, Jan 9, 2026 at 2:09 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>>
>> We need information about existing WWAN device children since we remove
>> the device after removing the last child. Previously, we tracked users
>> implicitly by checking whether ops was registered and existence of a
>> child device of the wwan_class class. Upcoming GNSS (NMEA) port type
>> support breaks this approach by introducing a child device of the
>> gnss_class class.
>>
>> And a modem driver can easily trigger a kernel Oops by removing regular
>> (e.g., MBIM, AT) ports first and then removing a GNSS port. The WWAN
>> device will be unregistered on removal of a last regular WWAN port. And
>> subsequent GNSS port removal will cause NULL pointer dereference in
>> simple_recursive_removal().
>>
>> In order to support ports of classes other than wwan_class, switch to
>> explicit references counting. Introduce a dedicated counter to the WWAN
>> device struct, increment it on every wwan_create_dev() call, decrement
>> on wwan_remove_dev(), and actually unregister the WWAN device when there
>> are no more references.
>>
>> Run tested with wwan_hwsim with NMEA support patches applied and
>> different port removing sequences.
>>
>> Reported-by: Daniele Palmas <dnlplm@gmail.com>
>> Closes: https://lore.kernel.org/netdev/CAGRyCJE28yf-rrfkFbzu44ygLEvoUM7fecK1vnrghjG_e9UaRA@mail.gmail.com/
>> Suggested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
>> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
>> ---
>> Changes:
>> * RFCv2->RFCv5: new patch to address modem disconnection / system
>>   shutdown issues
>> ---
>>  drivers/net/wwan/wwan_core.c | 37 ++++++++++++++++++------------------
>>  1 file changed, 18 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
>> index ade8bbffc93e..33f7a140fba9 100644
>> --- a/drivers/net/wwan/wwan_core.c
>> +++ b/drivers/net/wwan/wwan_core.c
>> @@ -42,6 +42,9 @@ static struct dentry *wwan_debugfs_dir;
>>   * struct wwan_device - The structure that defines a WWAN device
>>   *
>>   * @id: WWAN device unique ID.
>> + * @refcount: Reference count of this WWAN device. When this refcount reaches
>> + * zero, the device is deleted. NB: access is protected by global
>> + * wwan_register_lock mutex.
>>   * @dev: Underlying device.
>>   * @ops: wwan device ops
>>   * @ops_ctxt: context to pass to ops
>> @@ -49,6 +52,7 @@ static struct dentry *wwan_debugfs_dir;
>>   */
>>  struct wwan_device {
>>         unsigned int id;
>> +       int refcount;
>>         struct device dev;
>>         const struct wwan_ops *ops;
>>         void *ops_ctxt;
>> @@ -222,8 +226,10 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
>>
>>         /* If wwandev already exists, return it */
>>         wwandev = wwan_dev_get_by_parent(parent);
>> -       if (!IS_ERR(wwandev))
>> +       if (!IS_ERR(wwandev)) {
>> +               wwandev->refcount++;
>>                 goto done_unlock;
>> +       }
>>
>>         id = ida_alloc(&wwan_dev_ids, GFP_KERNEL);
>>         if (id < 0) {
>> @@ -242,6 +248,7 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
>>         wwandev->dev.class = &wwan_class;
>>         wwandev->dev.type = &wwan_dev_type;
>>         wwandev->id = id;
>> +       wwandev->refcount = 1;
>>         dev_set_name(&wwandev->dev, "wwan%d", wwandev->id);
>>
>>         err = device_register(&wwandev->dev);
>> @@ -263,30 +270,21 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
>>         return wwandev;
>>  }
>>
>> -static int is_wwan_child(struct device *dev, void *data)
>> -{
>> -       return dev->class == &wwan_class;
>> -}
>> -
>>  static void wwan_remove_dev(struct wwan_device *wwandev)
>>  {
>> -       int ret;
>> -
>>         /* Prevent concurrent picking from wwan_create_dev */
>>         mutex_lock(&wwan_register_lock);
>
>FYI, you can use guarded mutex:
>guard(mutex)(&wwan_register_lock);
>This ensures the lock is 'automatically' released when leaving the
>scope/function, and would save the below goto/out_unlock.

Sounds curious. Will keep in my mind for a future development. I would rather keep this patch as simple and clear as possible.

>> -       /* WWAN device is created and registered (get+add) along with its first
>> -        * child port, and subsequent port registrations only grab a reference
>> -        * (get). The WWAN device must then be unregistered (del+put) along with
>> -        * its last port, and reference simply dropped (put) otherwise. In the
>> -        * same fashion, we must not unregister it when the ops are still there.
>> -        */
>> -       if (wwandev->ops)
>> -               ret = 1;
>> -       else
>> -               ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child);
>> +       if (--wwandev->refcount <= 0) {
>> +               struct device *child = device_find_any_child(&wwandev->dev);
>> +
>> +               if (WARN_ON(wwandev->ops))      /* Paranoid */
>
>You may keep a reference to child (if existing)
>
>> +                       goto out_unlock;
>> +               if (WARN_ON(child)) {           /* Paranoid */
>> +                       put_device(child);
>> +                       goto out_unlock;
>> +               }
>
>Maybe you can simplify that with:
>```
>struct device *child = device_find_any_child(&wwandev->dev);
>put_device(child) /* NULL param is ok */
>if (WARN_ON(child || wwandev->ops))
>    return; /* or goto */
>```

Good point.

Slark, could you adjust this in your future submission or do you want me to send another RFC series?

>>
>> -       if (!ret) {
>>  #ifdef CONFIG_WWAN_DEBUGFS
>>                 debugfs_remove_recursive(wwandev->debugfs_dir);
>>  #endif
>> @@ -295,6 +293,7 @@ static void wwan_remove_dev(struct wwan_device *wwandev)
>>                 put_device(&wwandev->dev);
>>         }
>>
>> +out_unlock:
>>         mutex_unlock(&wwan_register_lock);
>>  }

--
Sergey

Hi Loic,

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

* Re:Re: [RFC PATCH v5 2/7] net: wwan: core: explicit WWAN device reference counting
  2026-01-15  6:52     ` Sergey Ryazanov
@ 2026-01-15  8:08       ` Slark Xiao
  0 siblings, 0 replies; 17+ messages in thread
From: Slark Xiao @ 2026-01-15  8:08 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Loic Poulain, Johannes Berg, Andrew Lunn, Eric Dumazet,
	David S . Miller, Jakub Kicinski, Paolo Abeni, netdev,
	Daniele Palmas


At 2026-01-15 14:52:08, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>On January 14, 2026 11:31:45 PM, Loic Poulain <loic.poulain@oss.qualcomm.com> wrote:
>>On Fri, Jan 9, 2026 at 2:09 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>>>
>>> We need information about existing WWAN device children since we remove
>>> the device after removing the last child. Previously, we tracked users
>>> implicitly by checking whether ops was registered and existence of a
>>> child device of the wwan_class class. Upcoming GNSS (NMEA) port type
>>> support breaks this approach by introducing a child device of the
>>> gnss_class class.
>>>
>>> And a modem driver can easily trigger a kernel Oops by removing regular
>>> (e.g., MBIM, AT) ports first and then removing a GNSS port. The WWAN
>>> device will be unregistered on removal of a last regular WWAN port. And
>>> subsequent GNSS port removal will cause NULL pointer dereference in
>>> simple_recursive_removal().
>>>
>>> In order to support ports of classes other than wwan_class, switch to
>>> explicit references counting. Introduce a dedicated counter to the WWAN
>>> device struct, increment it on every wwan_create_dev() call, decrement
>>> on wwan_remove_dev(), and actually unregister the WWAN device when there
>>> are no more references.
>>>
>>> Run tested with wwan_hwsim with NMEA support patches applied and
>>> different port removing sequences.
>>>
>>> Reported-by: Daniele Palmas <dnlplm@gmail.com>
>>> Closes: https://lore.kernel.org/netdev/CAGRyCJE28yf-rrfkFbzu44ygLEvoUM7fecK1vnrghjG_e9UaRA@mail.gmail.com/
>>> Suggested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
>>> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
>>> ---
>>> Changes:
>>> * RFCv2->RFCv5: new patch to address modem disconnection / system
>>>   shutdown issues
>>> ---
>>>  drivers/net/wwan/wwan_core.c | 37 ++++++++++++++++++------------------
>>>  1 file changed, 18 insertions(+), 19 deletions(-)
>>>
>>> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
>>> index ade8bbffc93e..33f7a140fba9 100644
>>> --- a/drivers/net/wwan/wwan_core.c
>>> +++ b/drivers/net/wwan/wwan_core.c
>>> @@ -42,6 +42,9 @@ static struct dentry *wwan_debugfs_dir;
>>>   * struct wwan_device - The structure that defines a WWAN device
>>>   *
>>>   * @id: WWAN device unique ID.
>>> + * @refcount: Reference count of this WWAN device. When this refcount reaches
>>> + * zero, the device is deleted. NB: access is protected by global
>>> + * wwan_register_lock mutex.
>>>   * @dev: Underlying device.
>>>   * @ops: wwan device ops
>>>   * @ops_ctxt: context to pass to ops
>>> @@ -49,6 +52,7 @@ static struct dentry *wwan_debugfs_dir;
>>>   */
>>>  struct wwan_device {
>>>         unsigned int id;
>>> +       int refcount;
>>>         struct device dev;
>>>         const struct wwan_ops *ops;
>>>         void *ops_ctxt;
>>> @@ -222,8 +226,10 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
>>>
>>>         /* If wwandev already exists, return it */
>>>         wwandev = wwan_dev_get_by_parent(parent);
>>> -       if (!IS_ERR(wwandev))
>>> +       if (!IS_ERR(wwandev)) {
>>> +               wwandev->refcount++;
>>>                 goto done_unlock;
>>> +       }
>>>
>>>         id = ida_alloc(&wwan_dev_ids, GFP_KERNEL);
>>>         if (id < 0) {
>>> @@ -242,6 +248,7 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
>>>         wwandev->dev.class = &wwan_class;
>>>         wwandev->dev.type = &wwan_dev_type;
>>>         wwandev->id = id;
>>> +       wwandev->refcount = 1;
>>>         dev_set_name(&wwandev->dev, "wwan%d", wwandev->id);
>>>
>>>         err = device_register(&wwandev->dev);
>>> @@ -263,30 +270,21 @@ static struct wwan_device *wwan_create_dev(struct device *parent)
>>>         return wwandev;
>>>  }
>>>
>>> -static int is_wwan_child(struct device *dev, void *data)
>>> -{
>>> -       return dev->class == &wwan_class;
>>> -}
>>> -
>>>  static void wwan_remove_dev(struct wwan_device *wwandev)
>>>  {
>>> -       int ret;
>>> -
>>>         /* Prevent concurrent picking from wwan_create_dev */
>>>         mutex_lock(&wwan_register_lock);
>>
>>FYI, you can use guarded mutex:
>>guard(mutex)(&wwan_register_lock);
>>This ensures the lock is 'automatically' released when leaving the
>>scope/function, and would save the below goto/out_unlock.
>
>Sounds curious. Will keep in my mind for a future development. I would rather keep this patch as simple and clear as possible.
>
>>> -       /* WWAN device is created and registered (get+add) along with its first
>>> -        * child port, and subsequent port registrations only grab a reference
>>> -        * (get). The WWAN device must then be unregistered (del+put) along with
>>> -        * its last port, and reference simply dropped (put) otherwise. In the
>>> -        * same fashion, we must not unregister it when the ops are still there.
>>> -        */
>>> -       if (wwandev->ops)
>>> -               ret = 1;
>>> -       else
>>> -               ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child);
>>> +       if (--wwandev->refcount <= 0) {
>>> +               struct device *child = device_find_any_child(&wwandev->dev);
>>> +
>>> +               if (WARN_ON(wwandev->ops))      /* Paranoid */
>>
>>You may keep a reference to child (if existing)
>>
>>> +                       goto out_unlock;
>>> +               if (WARN_ON(child)) {           /* Paranoid */
>>> +                       put_device(child);
>>> +                       goto out_unlock;
>>> +               }
>>
>>Maybe you can simplify that with:
>>```
>>struct device *child = device_find_any_child(&wwandev->dev);
>>put_device(child) /* NULL param is ok */
>>if (WARN_ON(child || wwandev->ops))
>>    return; /* or goto */
>>```
>
>Good point.
>
>Slark, could you adjust this in your future submission or do you want me to send another RFC series?
>
I will update it in my next submission.

>>>
>>> -       if (!ret) {
>>>  #ifdef CONFIG_WWAN_DEBUGFS
>>>                 debugfs_remove_recursive(wwandev->debugfs_dir);
>>>  #endif
>>> @@ -295,6 +293,7 @@ static void wwan_remove_dev(struct wwan_device *wwandev)
>>>                 put_device(&wwandev->dev);
>>>         }
>>>
>>> +out_unlock:
>>>         mutex_unlock(&wwan_register_lock);
>>>  }
>
>--
>Sergey
>
>Hi Loic,

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

end of thread, other threads:[~2026-01-15  8:09 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-09  1:09 [RFC PATCH v5 0/7] net: wwan: add NMEA port type support Sergey Ryazanov
2026-01-09  1:09 ` [RFC PATCH v5 1/7] net: wwan: core: remove unused port_id field Sergey Ryazanov
2026-01-09  1:09 ` [RFC PATCH v5 2/7] net: wwan: core: explicit WWAN device reference counting Sergey Ryazanov
2026-01-14 21:31   ` Loic Poulain
2026-01-15  6:52     ` Sergey Ryazanov
2026-01-15  8:08       ` Slark Xiao
2026-01-09  1:09 ` [RFC PATCH v5 3/7] net: wwan: core: split port creation and registration Sergey Ryazanov
2026-01-09  1:09 ` [RFC PATCH v5 4/7] net: wwan: core: split port unregister and stop Sergey Ryazanov
2026-01-09  1:09 ` [RFC PATCH v5 5/7] net: wwan: add NMEA port support Sergey Ryazanov
2026-01-09  1:09 ` [RFC PATCH v5 6/7] net: wwan: hwsim: refactor to support more port types Sergey Ryazanov
2026-01-09  1:09 ` [RFC PATCH v5 7/7] net: wwan: hwsim: support NMEA port emulation Sergey Ryazanov
2026-01-09  3:21 ` Re:[RFC PATCH v5 0/7] net: wwan: add NMEA port type support Slark Xiao
2026-01-09  7:11   ` Sergey Ryazanov
2026-01-13  2:03     ` Slark Xiao
2026-01-13  6:59       ` Sergey Ryazanov
2026-01-14  2:42         ` Slark Xiao
2026-01-14 19:59           ` [RFC " Sergey Ryazanov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox