netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] net: wwan: add NMEA port type support
@ 2025-04-08 23:31 Sergey Ryazanov
  2025-04-08 23:31 ` [RFC PATCH 1/6] net: wwan: core: remove unused port_id field Sergey Ryazanov
                   ` (5 more replies)
  0 siblings, 6 replies; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-08 23:31 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

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 two 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. Many thanks.

Comments are welcomed.

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>

Sergey Ryazanov (6):
  net: wwan: core: remove unused port_id field
  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  | 260 ++++++++++++++++++++++++++++------
 drivers/net/wwan/wwan_hwsim.c | 201 +++++++++++++++++++++-----
 include/linux/wwan.h          |   2 +
 4 files changed, 389 insertions(+), 75 deletions(-)

-- 
2.45.3


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

* [RFC PATCH 1/6] net: wwan: core: remove unused port_id field
  2025-04-08 23:31 [RFC PATCH 0/6] net: wwan: add NMEA port type support Sergey Ryazanov
@ 2025-04-08 23:31 ` Sergey Ryazanov
  2025-04-14 18:38   ` Loic Poulain
  2025-04-08 23:31 ` [RFC PATCH 2/6] net: wwan: core: split port creation and registration Sergey Ryazanov
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-08 23:31 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>
---
 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.45.3


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

* [RFC PATCH 2/6] net: wwan: core: split port creation and registration
  2025-04-08 23:31 [RFC PATCH 0/6] net: wwan: add NMEA port type support Sergey Ryazanov
  2025-04-08 23:31 ` [RFC PATCH 1/6] net: wwan: core: remove unused port_id field Sergey Ryazanov
@ 2025-04-08 23:31 ` Sergey Ryazanov
  2025-04-14 18:50   ` Loic Poulain
  2025-04-08 23:31 ` [RFC PATCH 3/6] net: wwan: core: split port unregister and stop Sergey Ryazanov
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-08 23:31 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.

Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
 drivers/net/wwan/wwan_core.c | 86 ++++++++++++++++++++++--------------
 1 file changed, 53 insertions(+), 33 deletions(-)

diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index ade8bbffc93e..045246d7cd50 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -357,16 +357,19 @@ static struct attribute *wwan_port_attrs[] = {
 };
 ATTRIBUTE_GROUPS(wwan_port);
 
-static void wwan_port_destroy(struct device *dev)
+static void __wwan_port_destroy(struct wwan_port *port)
 {
-	struct wwan_port *port = to_wwan_port(dev);
-
-	ida_free(&minors, MINOR(port->dev.devt));
 	mutex_destroy(&port->data_lock);
 	mutex_destroy(&port->ops_lock);
 	kfree(port);
 }
 
+static void wwan_port_destroy(struct device *dev)
+{
+	ida_free(&minors, MINOR(dev->devt));
+	__wwan_port_destroy(to_wwan_port(dev));
+}
+
 static const struct device_type wwan_port_dev_type = {
 	.name = "wwan_port",
 	.release = wwan_port_destroy,
@@ -440,6 +443,49 @@ 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.)
+ *
+ * NB: in case of error function frees the port memory.
+ */
+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) {
+		__wwan_port_destroy(port);
+		return minor;
+	}
+
+	port->dev.class = &wwan_class;
+	port->dev.type = &wwan_port_dev_type;
+	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_register(&port->dev);
+
+	mutex_unlock(&wwan_register_lock);
+
+	if (err) {
+		put_device(&port->dev);
+		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,
@@ -448,8 +494,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);
@@ -461,17 +506,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;
 	}
 
@@ -485,31 +522,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);
 
-	/* 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;
+		goto error_wwandev_remove;
 
-	dev_info(&wwandev->dev, "port %s attached\n", dev_name(&port->dev));
 	return port;
 
-error_put_device:
-	put_device(&port->dev);
 error_wwandev_remove:
 	wwan_remove_dev(wwandev);
 
-- 
2.45.3


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

* [RFC PATCH 3/6] net: wwan: core: split port unregister and stop
  2025-04-08 23:31 [RFC PATCH 0/6] net: wwan: add NMEA port type support Sergey Ryazanov
  2025-04-08 23:31 ` [RFC PATCH 1/6] net: wwan: core: remove unused port_id field Sergey Ryazanov
  2025-04-08 23:31 ` [RFC PATCH 2/6] net: wwan: core: split port creation and registration Sergey Ryazanov
@ 2025-04-08 23:31 ` Sergey Ryazanov
  2025-04-14 18:54   ` Loic Poulain
  2025-04-08 23:31 ` [RFC PATCH 4/6] net: wwan: add NMEA port support Sergey Ryazanov
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-08 23:31 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.

Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
 drivers/net/wwan/wwan_core.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
index 045246d7cd50..439a57bc2b9c 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -486,6 +486,18 @@ static int wwan_port_register_wwan(struct wwan_port *port)
 	return 0;
 }
 
+/* Unregister 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_unregister(&port->dev);
+}
+
 struct wwan_port *wwan_create_port(struct device *parent,
 				   enum wwan_port_type type,
 				   const struct wwan_port_ops *ops,
@@ -542,18 +554,17 @@ 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);
 
 	/* Release related wwan device */
 	wwan_remove_dev(wwandev);
-- 
2.45.3


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

* [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-08 23:31 [RFC PATCH 0/6] net: wwan: add NMEA port type support Sergey Ryazanov
                   ` (2 preceding siblings ...)
  2025-04-08 23:31 ` [RFC PATCH 3/6] net: wwan: core: split port unregister and stop Sergey Ryazanov
@ 2025-04-08 23:31 ` Sergey Ryazanov
  2025-04-09  3:54   ` Slark Xiao
                     ` (2 more replies)
  2025-04-08 23:31 ` [RFC PATCH 5/6] net: wwan: hwsim: refactor to support more port types Sergey Ryazanov
  2025-04-08 23:31 ` [RFC PATCH 6/6] net: wwan: hwsim: support NMEA port emulation Sergey Ryazanov
  5 siblings, 3 replies; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-08 23:31 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>
---
 drivers/net/wwan/Kconfig     |   1 +
 drivers/net/wwan/wwan_core.c | 157 ++++++++++++++++++++++++++++++++++-
 include/linux/wwan.h         |   2 +
 3 files changed, 156 insertions(+), 4 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 439a57bc2b9c..a30f0c89aa82 100644
--- a/drivers/net/wwan/wwan_core.c
+++ b/drivers/net/wwan/wwan_core.c
@@ -16,6 +16,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>
@@ -89,9 +90,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);
@@ -340,6 +348,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,
@@ -498,6 +507,132 @@ static void wwan_port_unregister_wwan(struct wwan_port *port)
 	device_unregister(&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,
+};
+
+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) {
+		err = -ENOMEM;
+		goto error_destroy_port;
+	}
+
+	/* 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 mapt 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);
+		goto error_destroy_port;
+	}
+
+	dev_info(&wwandev->dev, "port %s attached\n", dev_name(&gdev->dev));
+
+	return 0;
+
+error_destroy_port:
+	__wwan_port_destroy(port);
+
+	return err;
+}
+
+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);
+
+	__wwan_port_destroy(port);
+}
+#else
+static inline int wwan_port_register_gnss(struct wwan_port *port)
+{
+	__wwan_port_destroy(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,
@@ -536,7 +671,11 @@ struct wwan_port *wwan_create_port(struct device *parent,
 	port->dev.parent = &wwandev->dev;
 	dev_set_drvdata(&port->dev, drvdata);
 
-	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_wwandev_remove;
 
@@ -564,7 +703,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);
 
 	/* Release related wwan device */
 	wwan_remove_dev(wwandev);
@@ -573,8 +715,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.45.3


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

* [RFC PATCH 5/6] net: wwan: hwsim: refactor to support more port types
  2025-04-08 23:31 [RFC PATCH 0/6] net: wwan: add NMEA port type support Sergey Ryazanov
                   ` (3 preceding siblings ...)
  2025-04-08 23:31 ` [RFC PATCH 4/6] net: wwan: add NMEA port support Sergey Ryazanov
@ 2025-04-08 23:31 ` Sergey Ryazanov
  2025-04-14 18:56   ` Loic Poulain
  2025-04-08 23:31 ` [RFC PATCH 6/6] net: wwan: hwsim: support NMEA port emulation Sergey Ryazanov
  5 siblings, 1 reply; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-08 23:31 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>
---
 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.45.3


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

* [RFC PATCH 6/6] net: wwan: hwsim: support NMEA port emulation
  2025-04-08 23:31 [RFC PATCH 0/6] net: wwan: add NMEA port type support Sergey Ryazanov
                   ` (4 preceding siblings ...)
  2025-04-08 23:31 ` [RFC PATCH 5/6] net: wwan: hwsim: refactor to support more port types Sergey Ryazanov
@ 2025-04-08 23:31 ` Sergey Ryazanov
  5 siblings, 0 replies; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-08 23:31 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>
---
 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.45.3


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

* Re:[RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-08 23:31 ` [RFC PATCH 4/6] net: wwan: add NMEA port support Sergey Ryazanov
@ 2025-04-09  3:54   ` Slark Xiao
  2025-04-09  8:30     ` Slark Xiao
  2025-04-09 20:18     ` Sergey Ryazanov
  2025-04-16 20:04   ` Loic Poulain
  2025-04-17 20:41   ` Loic Poulain
  2 siblings, 2 replies; 31+ messages in thread
From: Slark Xiao @ 2025-04-09  3:54 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, Qiang Yu, Manivannan Sadhasivam, Johan Hovold


Hi Sergey,
I saw you add WWAN_PORT_NMEA here. And I have a concern, shall we
add it into mhi_wwan_ctrl.c for mapping "NMEA" channel in 
mhi_wwan_ctrl_match_table since previous QDU100 device has added
NMEA channel(So only "NMEA" channel is valid in pci_generic.c, but not
"GNSS" in future).

I am testing your patch now. Will update later.
Thanks

At 2025-04-09 07:31:16, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>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>
>---
> drivers/net/wwan/Kconfig     |   1 +
> drivers/net/wwan/wwan_core.c | 157 ++++++++++++++++++++++++++++++++++-
> include/linux/wwan.h         |   2 +
> 3 files changed, 156 insertions(+), 4 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 439a57bc2b9c..a30f0c89aa82 100644
>--- a/drivers/net/wwan/wwan_core.c
>+++ b/drivers/net/wwan/wwan_core.c
>@@ -16,6 +16,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>
>@@ -89,9 +90,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);
>@@ -340,6 +348,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,
>@@ -498,6 +507,132 @@ static void wwan_port_unregister_wwan(struct wwan_port *port)
> 	device_unregister(&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,
>+};
>+
>+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) {
>+		err = -ENOMEM;
>+		goto error_destroy_port;
>+	}
>+
>+	/* 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 mapt 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);
>+		goto error_destroy_port;
>+	}
>+
>+	dev_info(&wwandev->dev, "port %s attached\n", dev_name(&gdev->dev));
>+
>+	return 0;
>+
>+error_destroy_port:
>+	__wwan_port_destroy(port);
>+
>+	return err;
>+}
>+
>+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);
>+
>+	__wwan_port_destroy(port);
>+}
>+#else
>+static inline int wwan_port_register_gnss(struct wwan_port *port)
>+{
>+	__wwan_port_destroy(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,
>@@ -536,7 +671,11 @@ struct wwan_port *wwan_create_port(struct device *parent,
> 	port->dev.parent = &wwandev->dev;
> 	dev_set_drvdata(&port->dev, drvdata);
> 
>-	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_wwandev_remove;
> 
>@@ -564,7 +703,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);
> 
> 	/* Release related wwan device */
> 	wwan_remove_dev(wwandev);
>@@ -573,8 +715,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.45.3

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

* Re:Re:[RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-09  3:54   ` Slark Xiao
@ 2025-04-09  8:30     ` Slark Xiao
  2025-04-09 10:42       ` Sergey Ryazanov
  2025-04-09 20:18     ` Sergey Ryazanov
  1 sibling, 1 reply; 31+ messages in thread
From: Slark Xiao @ 2025-04-09  8:30 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, Qiang Yu, Manivannan Sadhasivam, Johan Hovold


Hi Sergey,
Device port /dev/gnss0 is enumerated . Does it be expected?
I can get the NMEA data from this port by cat or minicom command.
But the gpsd.service also can not be initialized normally. It reports:

TriggeredBy: ● gpsd.socket
    Process: 3824 ExecStartPre=/bin/stty speed 115200 -F $DEVICES (code=exited, status=1/FAILURE)
        CPU: 7ms

4月 09 16:04:16 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
4月 09 16:04:17 jbd stty[3824]: /bin/stty: /dev/gnss0: Inappropriate ioctl for device
4月 09 16:04:17 jbd systemd[1]: gpsd.service: Control process exited, code=exited, status=1/FAILURE
4月 09 16:04:17 jbd systemd[1]: gpsd.service: Failed with result 'exit-code'.
4月 09 16:04:17 jbd systemd[1]: Failed to start GPS (Global Positioning System) Daemon.

Seems it's not a serial port.
Any advice?

Thanks

At 2025-04-09 11:54:50, "Slark Xiao" <slark_xiao@163.com> wrote:
>
>Hi Sergey,
>I saw you add WWAN_PORT_NMEA here. And I have a concern, shall we
>add it into mhi_wwan_ctrl.c for mapping "NMEA" channel in 
>mhi_wwan_ctrl_match_table since previous QDU100 device has added
>NMEA channel(So only "NMEA" channel is valid in pci_generic.c, but not
>"GNSS" in future).
>
>I am testing your patch now. Will update later.
>Thanks
>
>At 2025-04-09 07:31:16, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>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>
>>---
>> drivers/net/wwan/Kconfig     |   1 +
>> drivers/net/wwan/wwan_core.c | 157 ++++++++++++++++++++++++++++++++++-
>> include/linux/wwan.h         |   2 +
>> 3 files changed, 156 insertions(+), 4 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 439a57bc2b9c..a30f0c89aa82 100644
>>--- a/drivers/net/wwan/wwan_core.c
>>+++ b/drivers/net/wwan/wwan_core.c
>>@@ -16,6 +16,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>
>>@@ -89,9 +90,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);
>>@@ -340,6 +348,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,
>>@@ -498,6 +507,132 @@ static void wwan_port_unregister_wwan(struct wwan_port *port)
>> 	device_unregister(&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,
>>+};
>>+
>>+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) {
>>+		err = -ENOMEM;
>>+		goto error_destroy_port;
>>+	}
>>+
>>+	/* 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 mapt 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);
>>+		goto error_destroy_port;
>>+	}
>>+
>>+	dev_info(&wwandev->dev, "port %s attached\n", dev_name(&gdev->dev));
>>+
>>+	return 0;
>>+
>>+error_destroy_port:
>>+	__wwan_port_destroy(port);
>>+
>>+	return err;
>>+}
>>+
>>+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);
>>+
>>+	__wwan_port_destroy(port);
>>+}
>>+#else
>>+static inline int wwan_port_register_gnss(struct wwan_port *port)
>>+{
>>+	__wwan_port_destroy(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,
>>@@ -536,7 +671,11 @@ struct wwan_port *wwan_create_port(struct device *parent,
>> 	port->dev.parent = &wwandev->dev;
>> 	dev_set_drvdata(&port->dev, drvdata);
>> 
>>-	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_wwandev_remove;
>> 
>>@@ -564,7 +703,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);
>> 
>> 	/* Release related wwan device */
>> 	wwan_remove_dev(wwandev);
>>@@ -573,8 +715,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.45.3

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

* Re:Re:[RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-09  8:30     ` Slark Xiao
@ 2025-04-09 10:42       ` Sergey Ryazanov
  2025-04-28  8:27         ` Slark Xiao
  0 siblings, 1 reply; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-09 10:42 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, Qiang Yu, Manivannan Sadhasivam, Johan Hovold

On April 9, 2025 11:30:58 AM GMT+03:00, Slark Xiao <slark_xiao@163.com> wrote:
>
>Hi Sergey,
>Device port /dev/gnss0 is enumerated . Does it be expected?
>I can get the NMEA data from this port by cat or minicom command.
>But the gpsd.service also can not be initialized normally. It reports:
>
>TriggeredBy: ● gpsd.socket
>    Process: 3824 ExecStartPre=/bin/stty speed 115200 -F $DEVICES (code=exited, status=1/FAILURE)
>        CPU: 7ms
>
>4月 09 16:04:16 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
>4月 09 16:04:17 jbd stty[3824]: /bin/stty: /dev/gnss0: Inappropriate ioctl for device
>4月 09 16:04:17 jbd systemd[1]: gpsd.service: Control process exited, code=exited, status=1/FAILURE
>4月 09 16:04:17 jbd systemd[1]: gpsd.service: Failed with result 'exit-code'.
>4月 09 16:04:17 jbd systemd[1]: Failed to start GPS (Global Positioning System) Daemon.
>
>Seems it's not a serial port.

It is a char dev lacking some IOCTLs support. Yeah.

>Any advice?

Yep. Remove that stty invocation from the service definition. For me, gpsd works flawlessly. You can try to start it manually from a terminal.

--
Sergey
Hi Slark,

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

* Re: [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-09  3:54   ` Slark Xiao
  2025-04-09  8:30     ` Slark Xiao
@ 2025-04-09 20:18     ` Sergey Ryazanov
  1 sibling, 0 replies; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-09 20:18 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, Qiang Yu, Manivannan Sadhasivam, Johan Hovold

Hi Slark,

On 09.04.2025 06:54, Slark Xiao wrote:
> Hi Sergey,
> I saw you add WWAN_PORT_NMEA here. And I have a concern, shall we
> add it into mhi_wwan_ctrl.c for mapping "NMEA" channel in
> mhi_wwan_ctrl_match_table since previous QDU100 device has added
> NMEA channel(So only "NMEA" channel is valid in pci_generic.c, but not
> "GNSS" in future).

This patch introduces NMEA port support to the WWAN core which is 
generic entity. Introduced enum item needs only for a driver to interact 
with the WWAN core. How the driver implements support is up to its 
author. If it needs a new definition for MHI implementation, then feel 
free to do it.

--
Sergey

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

* Re: [RFC PATCH 1/6] net: wwan: core: remove unused port_id field
  2025-04-08 23:31 ` [RFC PATCH 1/6] net: wwan: core: remove unused port_id field Sergey Ryazanov
@ 2025-04-14 18:38   ` Loic Poulain
  0 siblings, 0 replies; 31+ messages in thread
From: Loic Poulain @ 2025-04-14 18:38 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> 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>

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

* Re: [RFC PATCH 2/6] net: wwan: core: split port creation and registration
  2025-04-08 23:31 ` [RFC PATCH 2/6] net: wwan: core: split port creation and registration Sergey Ryazanov
@ 2025-04-14 18:50   ` Loic Poulain
  2025-04-14 21:29     ` Sergey Ryazanov
  0 siblings, 1 reply; 31+ messages in thread
From: Loic Poulain @ 2025-04-14 18:50 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

Hi Sergey,

On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> 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.
>
> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
> ---
>  drivers/net/wwan/wwan_core.c | 86 ++++++++++++++++++++++--------------
>  1 file changed, 53 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
> index ade8bbffc93e..045246d7cd50 100644
> --- a/drivers/net/wwan/wwan_core.c
> +++ b/drivers/net/wwan/wwan_core.c
> @@ -357,16 +357,19 @@ static struct attribute *wwan_port_attrs[] = {
>  };
>  ATTRIBUTE_GROUPS(wwan_port);
>
> -static void wwan_port_destroy(struct device *dev)
> +static void __wwan_port_destroy(struct wwan_port *port)
>  {
> -       struct wwan_port *port = to_wwan_port(dev);
> -
> -       ida_free(&minors, MINOR(port->dev.devt));
>         mutex_destroy(&port->data_lock);
>         mutex_destroy(&port->ops_lock);
>         kfree(port);
>  }
>
> +static void wwan_port_destroy(struct device *dev)
> +{
> +       ida_free(&minors, MINOR(dev->devt));
> +       __wwan_port_destroy(to_wwan_port(dev));
> +}
> +
>  static const struct device_type wwan_port_dev_type = {
>         .name = "wwan_port",
>         .release = wwan_port_destroy,
> @@ -440,6 +443,49 @@ 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.)
> + *
> + * NB: in case of error function frees the port memory.
> + */
> +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) {
> +               __wwan_port_destroy(port);

I see this is documented above, but it's a bit weird that the port is
freed inside the register function, it should be up to the caller to
do this. Is there a reason for this?

> +               return minor;
> +       }
> +
> +       port->dev.class = &wwan_class;
> +       port->dev.type = &wwan_port_dev_type;
> +       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_register(&port->dev);
> +
> +       mutex_unlock(&wwan_register_lock);
> +
> +       if (err) {
> +               put_device(&port->dev);
> +               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,
> @@ -448,8 +494,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);
> @@ -461,17 +506,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;
>         }
>
> @@ -485,31 +522,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);
>
> -       /* 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;
> +               goto error_wwandev_remove;
>
> -       dev_info(&wwandev->dev, "port %s attached\n", dev_name(&port->dev));
>         return port;
>
> -error_put_device:
> -       put_device(&port->dev);
>  error_wwandev_remove:
>         wwan_remove_dev(wwandev);
>
> --
> 2.45.3
>

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

* Re: [RFC PATCH 3/6] net: wwan: core: split port unregister and stop
  2025-04-08 23:31 ` [RFC PATCH 3/6] net: wwan: core: split port unregister and stop Sergey Ryazanov
@ 2025-04-14 18:54   ` Loic Poulain
  2025-04-14 21:34     ` Sergey Ryazanov
  0 siblings, 1 reply; 31+ messages in thread
From: Loic Poulain @ 2025-04-14 18:54 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> 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.
>
> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
> ---
>  drivers/net/wwan/wwan_core.c | 21 ++++++++++++++++-----
>  1 file changed, 16 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
> index 045246d7cd50..439a57bc2b9c 100644
> --- a/drivers/net/wwan/wwan_core.c
> +++ b/drivers/net/wwan/wwan_core.c
> @@ -486,6 +486,18 @@ static int wwan_port_register_wwan(struct wwan_port *port)
>         return 0;
>  }
>
> +/* Unregister regular WWAN port (e.g. AT, MBIM, etc) */
> +static void wwan_port_unregister_wwan(struct wwan_port *port)

Wouldn't it be simpler to name it  `wwan_port_unregister` ?

> +{
> +       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_unregister(&port->dev);
> +}
> +
>  struct wwan_port *wwan_create_port(struct device *parent,
>                                    enum wwan_port_type type,
>                                    const struct wwan_port_ops *ops,
> @@ -542,18 +554,17 @@ 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);
>
>         /* Release related wwan device */
>         wwan_remove_dev(wwandev);
> --
> 2.45.3
>

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

* Re: [RFC PATCH 5/6] net: wwan: hwsim: refactor to support more port types
  2025-04-08 23:31 ` [RFC PATCH 5/6] net: wwan: hwsim: refactor to support more port types Sergey Ryazanov
@ 2025-04-14 18:56   ` Loic Poulain
  0 siblings, 0 replies; 31+ messages in thread
From: Loic Poulain @ 2025-04-14 18:56 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> 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>

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

* Re: [RFC PATCH 2/6] net: wwan: core: split port creation and registration
  2025-04-14 18:50   ` Loic Poulain
@ 2025-04-14 21:29     ` Sergey Ryazanov
  2025-04-17 20:35       ` Loic Poulain
  0 siblings, 1 reply; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-14 21:29 UTC (permalink / raw)
  To: Loic Poulain
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

Hi Loic,

thank you that you found a time to check it. See the explanation below, 
might be you can suggest a better solution.

On 14.04.2025 21:50, Loic Poulain wrote:
> Hi Sergey,
> 
> On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>>
>> 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.
>>
>> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
>> ---
>>   drivers/net/wwan/wwan_core.c | 86 ++++++++++++++++++++++--------------
>>   1 file changed, 53 insertions(+), 33 deletions(-)
>>
>> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
>> index ade8bbffc93e..045246d7cd50 100644
>> --- a/drivers/net/wwan/wwan_core.c
>> +++ b/drivers/net/wwan/wwan_core.c
>> @@ -357,16 +357,19 @@ static struct attribute *wwan_port_attrs[] = {
>>   };
>>   ATTRIBUTE_GROUPS(wwan_port);
>>
>> -static void wwan_port_destroy(struct device *dev)
>> +static void __wwan_port_destroy(struct wwan_port *port)
>>   {
>> -       struct wwan_port *port = to_wwan_port(dev);
>> -
>> -       ida_free(&minors, MINOR(port->dev.devt));
>>          mutex_destroy(&port->data_lock);
>>          mutex_destroy(&port->ops_lock);
>>          kfree(port);
>>   }
>>
>> +static void wwan_port_destroy(struct device *dev)
>> +{
>> +       ida_free(&minors, MINOR(dev->devt));
>> +       __wwan_port_destroy(to_wwan_port(dev));
>> +}
>> +
>>   static const struct device_type wwan_port_dev_type = {
>>          .name = "wwan_port",
>>          .release = wwan_port_destroy,
>> @@ -440,6 +443,49 @@ 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.)
>> + *
>> + * NB: in case of error function frees the port memory.
>> + */
>> +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) {
>> +               __wwan_port_destroy(port);
> 
> I see this is documented above, but it's a bit weird that the port is
> freed inside the register function, it should be up to the caller to
> do this. Is there a reason for this?

I agree that this looks weird and asymmetrical. I left the port 
allocation in wwan_create_port() because both WWAN-exported and 
GNSS-exported types of port share the same port allocation. And the port 
struct is used as a container to keep all the port registration arguments.

I did the port freeing inside this function because we free the port 
differently depending of the device registration state. If we fail to 
initialize the port at earlier stage then we use __wwan_port_destroy() 
which basically just releases the memory.

But if device_register() fails then we are required to use put_device() 
which does more job.

I do not think it is acceptable to skip put_device() call and just 
release the memory. Also I do not find maintainable to partially open 
code put_device() here in the WWAN-exportable handler and release the 
memory in caller function wwan_create_port().

We could somehow try to return this information from 
wwan_port_register_wwan() to wwan_create_port(), so the caller could 
decide, shall it use __wwan_port_destroy() or put_device() in case of 
failure.

But I can not see a way to clearly indicate, which releasing approach 
should be used by the caller. And even in this case it going to look 
weird since the called function controls the caller.

Another solution for the asymmetry problem is to move the allocation 
from the caller to the called function. So the memory will be allocated 
and released in the same function. But in this case we will need to pass 
all the parameters from wwan_create_port() to wwan_port_register_wwan(). 
Even if we consolidate the port basic allocation/initialization in a 
common routine, the final solution going to look a duplication. E.g.

struct wwan_port *wwan_port_allocate(struct wwan_device *wwandev,
                                      enum wwan_port_type type,
                                      const struct wwan_port_ops *ops,
                                      struct wwan_port_caps *caps,
                                      void *drvdata)
{
     /* Do the mem allocation and init here */
     return port;
}

struct wwan_port *wwan_port_register_wwan(struct wwan_device *wwandev,
                        enum wwan_port_type type,
                        const struct wwan_port_ops *ops,
                        struct wwan_port_caps *caps,
                        void *drvdata)
{
     port = wwan_port_allocate(wwandev, type, ops, caps, drvdata);
     /* Proceed with chardev registration or release on failure */
     /* return port; or return ERR_PTR(-err); */
}

struct wwan_port *wwan_port_register_gnss(struct wwan_device *wwandev,
                        enum wwan_port_type type,
                        const struct wwan_port_ops *ops,
                        struct wwan_port_caps *caps,
                        void *drvdata)
{
     port = wwan_port_allocate(wwandev, type, ops, caps, drvdata);
     /* Proceed with GNSS registration or release on failure */
     /* return port; or return ERR_PTR(-err); */
}

struct wwan_port *wwan_create_port(struct device *parent,
                                    enum wwan_port_type type,
                                    const struct wwan_port_ops *ops,
                                    struct wwan_port_caps *caps,
                                    void *drvdata)
{
     ...
     wwandev = wwan_create_dev(parent);
     if (type == WWAN_PORT_NMEA)
         port = wwan_port_register_gnss(wwandev, type, ops,
                                        caps, drvdata);
     else
         port = wwan_port_register_wwan(wwandev, type, ops,
                                        caps, drvdata);
     if (!IS_ERR(port))
         return port;
     wwan_remove_dev(wwandev);
     return ERR_CAST(port);
}

wwan_create_port() looks better in prices of passing a list of arguments 
and allocating the port in multiple places.

Maybe some other design approach, what was overseen?


For me, the ideal solution would be a routine that works like 
put_device() except calling the device type release handler. Then we can 
use it to cleanup leftovers of the failed device_register() call and 
then release the memory in the calling wwan_create_port() function.

>> +               return minor;
>> +       }
>> +
>> +       port->dev.class = &wwan_class;
>> +       port->dev.type = &wwan_port_dev_type;
>> +       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_register(&port->dev);
>> +
>> +       mutex_unlock(&wwan_register_lock);
>> +
>> +       if (err) {
>> +               put_device(&port->dev);
>> +               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,
>> @@ -448,8 +494,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);
>> @@ -461,17 +506,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;
>>          }
>>
>> @@ -485,31 +522,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);
>>
>> -       /* 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;
>> +               goto error_wwandev_remove;
>>
>> -       dev_info(&wwandev->dev, "port %s attached\n", dev_name(&port->dev));
>>          return port;
>>
>> -error_put_device:
>> -       put_device(&port->dev);
>>   error_wwandev_remove:
>>          wwan_remove_dev(wwandev);
>>

--
Sergey


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

* Re: [RFC PATCH 3/6] net: wwan: core: split port unregister and stop
  2025-04-14 18:54   ` Loic Poulain
@ 2025-04-14 21:34     ` Sergey Ryazanov
  2025-04-16 20:21       ` Loic Poulain
  0 siblings, 1 reply; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-14 21:34 UTC (permalink / raw)
  To: Loic Poulain
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

On 14.04.2025 21:54, Loic Poulain wrote:
> On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>>
>> 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.
>>
>> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
>> ---
>>   drivers/net/wwan/wwan_core.c | 21 ++++++++++++++++-----
>>   1 file changed, 16 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
>> index 045246d7cd50..439a57bc2b9c 100644
>> --- a/drivers/net/wwan/wwan_core.c
>> +++ b/drivers/net/wwan/wwan_core.c
>> @@ -486,6 +486,18 @@ static int wwan_port_register_wwan(struct wwan_port *port)
>>          return 0;
>>   }
>>
>> +/* Unregister regular WWAN port (e.g. AT, MBIM, etc) */
>> +static void wwan_port_unregister_wwan(struct wwan_port *port)
> 
> Wouldn't it be simpler to name it  `wwan_port_unregister` ?

I came with this complex name for a symmetry purpose. The next patch 
going to introduce wwan_port_unregister_gnss() handler.

The prefix indicates the module and the suffix indicates the type of the 
unregistering 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_unregister(&port->dev);
>> +}
>> +
>>   struct wwan_port *wwan_create_port(struct device *parent,
>>                                     enum wwan_port_type type,
>>                                     const struct wwan_port_ops *ops,
>> @@ -542,18 +554,17 @@ 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);
>>
>>          /* Release related wwan device */
>>          wwan_remove_dev(wwandev);
>> --
>> 2.45.3
>>


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

* Re: [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-08 23:31 ` [RFC PATCH 4/6] net: wwan: add NMEA port support Sergey Ryazanov
  2025-04-09  3:54   ` Slark Xiao
@ 2025-04-16 20:04   ` Loic Poulain
  2025-04-18 23:20     ` Sergey Ryazanov
  2025-04-17 20:41   ` Loic Poulain
  2 siblings, 1 reply; 31+ messages in thread
From: Loic Poulain @ 2025-04-16 20:04 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev, Slark Xiao,
	Muhammad Nuzaihan, Qiang Yu, Manivannan Sadhasivam, Johan Hovold

On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> 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>
> ---
>  drivers/net/wwan/Kconfig     |   1 +
>  drivers/net/wwan/wwan_core.c | 157 ++++++++++++++++++++++++++++++++++-
>  include/linux/wwan.h         |   2 +
>  3 files changed, 156 insertions(+), 4 deletions(-)
>
[...]
> +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);
> +
> +       __wwan_port_destroy(port);
> +}
> +#else
> +static inline int wwan_port_register_gnss(struct wwan_port *port)
> +{
> +       __wwan_port_destroy(port);
> +       return -EOPNOTSUPP;
> +}

I don't think the wwan core should return an error in case GNSS_CONFIG
is not enabled, a wwan driver may consider aborting the full
probing/registration if one of the port registrations is failing.
Maybe we should silently ignore such ports, and/or simply print a
warning.

> +
> +static inline void wwan_port_unregister_gnss(struct wwan_port *port)
> +{
> +       WARN_ON(1);     /* This handler cannot be called */
> +}
> +#endif

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

* Re: [RFC PATCH 3/6] net: wwan: core: split port unregister and stop
  2025-04-14 21:34     ` Sergey Ryazanov
@ 2025-04-16 20:21       ` Loic Poulain
  0 siblings, 0 replies; 31+ messages in thread
From: Loic Poulain @ 2025-04-16 20:21 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

On Mon, Apr 14, 2025 at 11:33 PM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> On 14.04.2025 21:54, Loic Poulain wrote:
> > On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
> >>
> >> 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.
> >>
> >> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
> >> ---
> >>   drivers/net/wwan/wwan_core.c | 21 ++++++++++++++++-----
> >>   1 file changed, 16 insertions(+), 5 deletions(-)
> >>
> >> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
> >> index 045246d7cd50..439a57bc2b9c 100644
> >> --- a/drivers/net/wwan/wwan_core.c
> >> +++ b/drivers/net/wwan/wwan_core.c
> >> @@ -486,6 +486,18 @@ static int wwan_port_register_wwan(struct wwan_port *port)
> >>          return 0;
> >>   }
> >>
> >> +/* Unregister regular WWAN port (e.g. AT, MBIM, etc) */
> >> +static void wwan_port_unregister_wwan(struct wwan_port *port)
> >
> > Wouldn't it be simpler to name it  `wwan_port_unregister` ?
>
> I came with this complex name for a symm>
> >> +{
> >> +       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_unregister(&port->dev);
> >> +}
> >> +
> >>   struct wwan_port *wwan_create_port(struct device *parent,
> >>                                     enum wwan_port_type type,
> >>                                     const struct wwan_port_ops *ops,
> >> @@ -542,18 +554,17 @@ 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);
> >>
> >>          /* Release related wwan device */
> >>          wwan_remove_dev(wwandev);
> >> --
> >> 2.45.3
> >>
>etry purpose. The next patch
> going to introduce wwan_port_unregister_gnss() handler.
>
> The prefix indicates the module and the suffix indicates the type of the
> unregistering port.

Ok, fair enough.

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

* Re: [RFC PATCH 2/6] net: wwan: core: split port creation and registration
  2025-04-14 21:29     ` Sergey Ryazanov
@ 2025-04-17 20:35       ` Loic Poulain
  2025-04-18 23:04         ` Sergey Ryazanov
  0 siblings, 1 reply; 31+ messages in thread
From: Loic Poulain @ 2025-04-17 20:35 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

Hi Sergey,

On Mon, Apr 14, 2025 at 11:28 PM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> Hi Loic,
>
> thank you that you found a time to check it. See the explanation below,
> might be you can suggest a better solution.
>
> On 14.04.2025 21:50, Loic Poulain wrote:
> > Hi Sergey,
> >
> > On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
> >>
> >> 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.
> >>
> >> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
> >> ---
> >>   drivers/net/wwan/wwan_core.c | 86 ++++++++++++++++++++++--------------
> >>   1 file changed, 53 insertions(+), 33 deletions(-)
> >>
> >> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
> >> index ade8bbffc93e..045246d7cd50 100644
> >> --- a/drivers/net/wwan/wwan_core.c
> >> +++ b/drivers/net/wwan/wwan_core.c
> >> @@ -357,16 +357,19 @@ static struct attribute *wwan_port_attrs[] = {
> >>   };
> >>   ATTRIBUTE_GROUPS(wwan_port);
> >>
> >> -static void wwan_port_destroy(struct device *dev)
> >> +static void __wwan_port_destroy(struct wwan_port *port)
> >>   {
> >> -       struct wwan_port *port = to_wwan_port(dev);
> >> -
> >> -       ida_free(&minors, MINOR(port->dev.devt));
> >>          mutex_destroy(&port->data_lock);
> >>          mutex_destroy(&port->ops_lock);
> >>          kfree(port);
> >>   }
> >>
> >> +static void wwan_port_destroy(struct device *dev)
> >> +{
> >> +       ida_free(&minors, MINOR(dev->devt));
> >> +       __wwan_port_destroy(to_wwan_port(dev));
> >> +}
> >> +
> >>   static const struct device_type wwan_port_dev_type = {
> >>          .name = "wwan_port",
> >>          .release = wwan_port_destroy,
> >> @@ -440,6 +443,49 @@ 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.)
> >> + *
> >> + * NB: in case of error function frees the port memory.
> >> + */
> >> +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) {
> >> +               __wwan_port_destroy(port);
> >
> > I see this is documented above, but it's a bit weird that the port is
> > freed inside the register function, it should be up to the caller to
> > do this. Is there a reason for this?
>
> I agree that this looks weird and asymmetrical. I left the port
> allocation in wwan_create_port() because both WWAN-exported and
> GNSS-exported types of port share the same port allocation. And the port
> struct is used as a container to keep all the port registration arguments.
>
> I did the port freeing inside this function because we free the port
> differently depending of the device registration state. If we fail to
> initialize the port at earlier stage then we use __wwan_port_destroy()
> which basically just releases the memory.
>
> But if device_register() fails then we are required to use put_device()
> which does more job.
>
> I do not think it is acceptable to skip put_device() call and just
> release the memory. Also I do not find maintainable to partially open
> code put_device() here in the WWAN-exportable handler and release the
> memory in caller function wwan_create_port().
>
> We could somehow try to return this information from
> wwan_port_register_wwan() to wwan_create_port(), so the caller could
> decide, shall it use __wwan_port_destroy() or put_device() in case of
> failure.
>
> But I can not see a way to clearly indicate, which releasing approach
> should be used by the caller. And even in this case it going to look
> weird since the called function controls the caller.
>
> Another solution for the asymmetry problem is to move the allocation
> from the caller to the called function. So the memory will be allocated
> and released in the same function. But in this case we will need to pass
> all the parameters from wwan_create_port() to wwan_port_register_wwan().
> Even if we consolidate the port basic allocation/initialization in a
> common routine, the final solution going to look a duplication. E.g.
>
> struct wwan_port *wwan_port_allocate(struct wwan_device *wwandev,
>                                       enum wwan_port_type type,
>                                       const struct wwan_port_ops *ops,
>                                       struct wwan_port_caps *caps,
>                                       void *drvdata)
> {
>      /* Do the mem allocation and init here */
>      return port;
> }
>
> struct wwan_port *wwan_port_register_wwan(struct wwan_device *wwandev,
>                         enum wwan_port_type type,
>                         const struct wwan_port_ops *ops,
>                         struct wwan_port_caps *caps,
>                         void *drvdata)
> {
>      port = wwan_port_allocate(wwandev, type, ops, caps, drvdata);
>      /* Proceed with chardev registration or release on failure */
>      /* return port; or return ERR_PTR(-err); */
> }
>
> struct wwan_port *wwan_port_register_gnss(struct wwan_device *wwandev,
>                         enum wwan_port_type type,
>                         const struct wwan_port_ops *ops,
>                         struct wwan_port_caps *caps,
>                         void *drvdata)
> {
>      port = wwan_port_allocate(wwandev, type, ops, caps, drvdata);
>      /* Proceed with GNSS registration or release on failure */
>      /* return port; or return ERR_PTR(-err); */
> }
>
> struct wwan_port *wwan_create_port(struct device *parent,
>                                     enum wwan_port_type type,
>                                     const struct wwan_port_ops *ops,
>                                     struct wwan_port_caps *caps,
>                                     void *drvdata)
> {
>      ...
>      wwandev = wwan_create_dev(parent);
>      if (type == WWAN_PORT_NMEA)
>          port = wwan_port_register_gnss(wwandev, type, ops,
>                                         caps, drvdata);
>      else
>          port = wwan_port_register_wwan(wwandev, type, ops,
>                                         caps, drvdata);
>      if (!IS_ERR(port))
>          return port;
>      wwan_remove_dev(wwandev);
>      return ERR_CAST(port);
> }
>
> wwan_create_port() looks better in prices of passing a list of arguments
> and allocating the port in multiple places.
>
> Maybe some other design approach, what was overseen?
>
>
> For me, the ideal solution would be a routine that works like
> put_device() except calling the device type release handler. Then we can
> use it to cleanup leftovers of the failed device_register() call and
> then release the memory in the calling wwan_create_port() function.

Ok I see, thanks for the clear explanation, I don't see a perfect
solution here without over complication. So the current approach is
acceptable, can you add a comment in the caller function as well,so
that it's clear why we don't have to release the port on error.

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

* Re: [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-08 23:31 ` [RFC PATCH 4/6] net: wwan: add NMEA port support Sergey Ryazanov
  2025-04-09  3:54   ` Slark Xiao
  2025-04-16 20:04   ` Loic Poulain
@ 2025-04-17 20:41   ` Loic Poulain
  2025-04-18 23:07     ` Sergey Ryazanov
  2 siblings, 1 reply; 31+ messages in thread
From: Loic Poulain @ 2025-04-17 20:41 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev, Slark Xiao,
	Muhammad Nuzaihan, Qiang Yu, Manivannan Sadhasivam, Johan Hovold

On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> 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>
> ---
>  drivers/net/wwan/Kconfig     |   1 +
>  drivers/net/wwan/wwan_core.c | 157 ++++++++++++++++++++++++++++++++++-
>  include/linux/wwan.h         |   2 +
>  3 files changed, 156 insertions(+), 4 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 439a57bc2b9c..a30f0c89aa82 100644
[...]
> +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) {
> +               err = -ENOMEM;
> +               goto error_destroy_port;
> +       }
> +
> +       /* 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 mapt WWAN port type to GNSS type.

typo: mapt

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

* Re: [RFC PATCH 2/6] net: wwan: core: split port creation and registration
  2025-04-17 20:35       ` Loic Poulain
@ 2025-04-18 23:04         ` Sergey Ryazanov
  2025-04-19 11:44           ` Loic Poulain
  0 siblings, 1 reply; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-18 23:04 UTC (permalink / raw)
  To: Loic Poulain
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

Hi Loic,

please find one extra option below.

On 17.04.2025 23:35, Loic Poulain wrote:
> Hi Sergey,
> 
> On Mon, Apr 14, 2025 at 11:28 PM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>>
>> Hi Loic,
>>
>> thank you that you found a time to check it. See the explanation below,
>> might be you can suggest a better solution.
>>
>> On 14.04.2025 21:50, Loic Poulain wrote:
>>> Hi Sergey,
>>>
>>> On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>>>>
>>>> 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.
>>>>
>>>> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
>>>> ---
>>>>    drivers/net/wwan/wwan_core.c | 86 ++++++++++++++++++++++--------------
>>>>    1 file changed, 53 insertions(+), 33 deletions(-)
>>>>
>>>> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
>>>> index ade8bbffc93e..045246d7cd50 100644
>>>> --- a/drivers/net/wwan/wwan_core.c
>>>> +++ b/drivers/net/wwan/wwan_core.c
>>>> @@ -357,16 +357,19 @@ static struct attribute *wwan_port_attrs[] = {
>>>>    };
>>>>    ATTRIBUTE_GROUPS(wwan_port);
>>>>
>>>> -static void wwan_port_destroy(struct device *dev)
>>>> +static void __wwan_port_destroy(struct wwan_port *port)
>>>>    {
>>>> -       struct wwan_port *port = to_wwan_port(dev);
>>>> -
>>>> -       ida_free(&minors, MINOR(port->dev.devt));
>>>>           mutex_destroy(&port->data_lock);
>>>>           mutex_destroy(&port->ops_lock);
>>>>           kfree(port);
>>>>    }
>>>>
>>>> +static void wwan_port_destroy(struct device *dev)
>>>> +{
>>>> +       ida_free(&minors, MINOR(dev->devt));
>>>> +       __wwan_port_destroy(to_wwan_port(dev));
>>>> +}
>>>> +
>>>>    static const struct device_type wwan_port_dev_type = {
>>>>           .name = "wwan_port",
>>>>           .release = wwan_port_destroy,
>>>> @@ -440,6 +443,49 @@ 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.)
>>>> + *
>>>> + * NB: in case of error function frees the port memory.
>>>> + */
>>>> +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) {
>>>> +               __wwan_port_destroy(port);
>>>
>>> I see this is documented above, but it's a bit weird that the port is
>>> freed inside the register function, it should be up to the caller to
>>> do this. Is there a reason for this?
>>
>> I agree that this looks weird and asymmetrical. I left the port
>> allocation in wwan_create_port() because both WWAN-exported and
>> GNSS-exported types of port share the same port allocation. And the port
>> struct is used as a container to keep all the port registration arguments.
>>
>> I did the port freeing inside this function because we free the port
>> differently depending of the device registration state. If we fail to
>> initialize the port at earlier stage then we use __wwan_port_destroy()
>> which basically just releases the memory.
>>
>> But if device_register() fails then we are required to use put_device()
>> which does more job.
>>
>> I do not think it is acceptable to skip put_device() call and just
>> release the memory. Also I do not find maintainable to partially open
>> code put_device() here in the WWAN-exportable handler and release the
>> memory in caller function wwan_create_port().
>>
>> We could somehow try to return this information from
>> wwan_port_register_wwan() to wwan_create_port(), so the caller could
>> decide, shall it use __wwan_port_destroy() or put_device() in case of
>> failure.
>>
>> But I can not see a way to clearly indicate, which releasing approach
>> should be used by the caller. And even in this case it going to look
>> weird since the called function controls the caller.
>>
>> Another solution for the asymmetry problem is to move the allocation
>> from the caller to the called function. So the memory will be allocated
>> and released in the same function. But in this case we will need to pass
>> all the parameters from wwan_create_port() to wwan_port_register_wwan().
>> Even if we consolidate the port basic allocation/initialization in a
>> common routine, the final solution going to look a duplication. E.g.
>>
>> struct wwan_port *wwan_port_allocate(struct wwan_device *wwandev,
>>                                        enum wwan_port_type type,
>>                                        const struct wwan_port_ops *ops,
>>                                        struct wwan_port_caps *caps,
>>                                        void *drvdata)
>> {
>>       /* Do the mem allocation and init here */
>>       return port;
>> }
>>
>> struct wwan_port *wwan_port_register_wwan(struct wwan_device *wwandev,
>>                          enum wwan_port_type type,
>>                          const struct wwan_port_ops *ops,
>>                          struct wwan_port_caps *caps,
>>                          void *drvdata)
>> {
>>       port = wwan_port_allocate(wwandev, type, ops, caps, drvdata);
>>       /* Proceed with chardev registration or release on failure */
>>       /* return port; or return ERR_PTR(-err); */
>> }
>>
>> struct wwan_port *wwan_port_register_gnss(struct wwan_device *wwandev,
>>                          enum wwan_port_type type,
>>                          const struct wwan_port_ops *ops,
>>                          struct wwan_port_caps *caps,
>>                          void *drvdata)
>> {
>>       port = wwan_port_allocate(wwandev, type, ops, caps, drvdata);
>>       /* Proceed with GNSS registration or release on failure */
>>       /* return port; or return ERR_PTR(-err); */
>> }
>>
>> struct wwan_port *wwan_create_port(struct device *parent,
>>                                      enum wwan_port_type type,
>>                                      const struct wwan_port_ops *ops,
>>                                      struct wwan_port_caps *caps,
>>                                      void *drvdata)
>> {
>>       ...
>>       wwandev = wwan_create_dev(parent);
>>       if (type == WWAN_PORT_NMEA)
>>           port = wwan_port_register_gnss(wwandev, type, ops,
>>                                          caps, drvdata);
>>       else
>>           port = wwan_port_register_wwan(wwandev, type, ops,
>>                                          caps, drvdata);
>>       if (!IS_ERR(port))
>>           return port;
>>       wwan_remove_dev(wwandev);
>>       return ERR_CAST(port);
>> }
>>
>> wwan_create_port() looks better in prices of passing a list of arguments
>> and allocating the port in multiple places.
>>
>> Maybe some other design approach, what was overseen?
>>
>>
>> For me, the ideal solution would be a routine that works like
>> put_device() except calling the device type release handler. Then we can
>> use it to cleanup leftovers of the failed device_register() call and
>> then release the memory in the calling wwan_create_port() function.
> 
> Ok I see, thanks for the clear explanation, I don't see a perfect
> solution here without over complication. So the current approach is
> acceptable, can you add a comment in the caller function as well,so
> that it's clear why we don't have to release the port on error.

Looks like I've found another one solution to move the port resources 
(memory) release back to the function allocating it. It is also a bit 
hackish and I would like to hear a feedback from you.

The port is released inside wwan_port_register_wwan() just because we 
release it differently depending on the initialization stage, right?

We cannot call put_device() before the device_register() call. And that 
is why I've created that __wwan_port_destroy() routine. What if we make 
port eligible to be released with put_device() ASAP? Internally, 
device_register() just sequentially calls device_initialize() and then 
device_add(). Indeed, device_initialize() makes a device struct eligible 
to be released with put_device().

We can avoid using device_register() and instead of it, do the 
registration step by step. Just after the port memory allocation and 
very basic initialization, we can call device_initialize(). And then 
call device_add() when it is time to register the port with the kernel. 
And if something going to go wrong, we can return just an error from 
wwan_port_register_wwan() and release the port with put_device() in 
wwan_create_port() where it was allocated. Something like this:

static int wwan_port_register_wwan(struct wwan_port *port)
{
     ...
     if (something_wrong)
         return -E<ERROR_TYPE>;
     ...
     return 0;
}

struct wwan_port *wwan_create_port(struct device *parent,
                                    enum wwan_port_type type,
                                    const struct wwan_port_ops *ops,
                                    struct wwan_port_caps *caps,
                                    void *drvdata)
{
     ...
     port = kzalloc(sizeof(*port), GFP_KERNEL);
     /* Do basic port init here */
     port->dev.type = &wwan_port_dev_type;
     device_initialize(&port->dev);  /* allows put_device() usage */

     if (port->type == WWAN_PORT_NMEA)
         err = wwan_port_register_gnss(port);
     else
         err = wwan_port_register_wwan(port);

     if (err) {
         put_device(&port->dev);
         goto error_wwandev_remove;
     }

     return port;
     ...
}

The only drawback I see here is that we have to use put_device() to 
release the port memory even in case of GNSS port. We don't actually 
register the port as device, but I believe, this can be explained with a 
proper comment.

What do you think, is it worth to try to rework the code in this way?

--
Sergey

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

* Re: [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-17 20:41   ` Loic Poulain
@ 2025-04-18 23:07     ` Sergey Ryazanov
  0 siblings, 0 replies; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-18 23:07 UTC (permalink / raw)
  To: Loic Poulain
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev, Slark Xiao,
	Muhammad Nuzaihan, Qiang Yu, Manivannan Sadhasivam, Johan Hovold

On 17.04.2025 23:41, Loic Poulain wrote:
> On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
[...]
>> +static int wwan_port_register_gnss(struct wwan_port *port)
>> +{
>> +       /* 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 mapt WWAN port type to GNSS type.
> 
> typo: mapt

Nice catch. Will fix it.

--
Sergey

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

* Re: [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-16 20:04   ` Loic Poulain
@ 2025-04-18 23:20     ` Sergey Ryazanov
  0 siblings, 0 replies; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-18 23:20 UTC (permalink / raw)
  To: Loic Poulain
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev, Slark Xiao,
	Muhammad Nuzaihan, Qiang Yu, Manivannan Sadhasivam, Johan Hovold

On 16.04.2025 23:04, Loic Poulain wrote:
> On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>> 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>
>> ---
>>   drivers/net/wwan/Kconfig     |   1 +
>>   drivers/net/wwan/wwan_core.c | 157 ++++++++++++++++++++++++++++++++++-
>>   include/linux/wwan.h         |   2 +
>>   3 files changed, 156 insertions(+), 4 deletions(-)
>>
> [...]
>> +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);
>> +
>> +       __wwan_port_destroy(port);
>> +}
>> +#else
>> +static inline int wwan_port_register_gnss(struct wwan_port *port)
>> +{
>> +       __wwan_port_destroy(port);
>> +       return -EOPNOTSUPP;
>> +}
> 
> I don't think the wwan core should return an error in case GNSS_CONFIG
> is not enabled, a wwan driver may consider aborting the full
> probing/registration if one of the port registrations is failing.
> Maybe we should silently ignore such ports, and/or simply print a
> warning.

Good point. We can end up with the case of driver aborting the whole 
registration.

On the other hand, a driver author is supposed to know that for the GNSS 
functionality the corresponding subsystem should be enabled. And either 
don't even try to register the GNSS (NMEA) port if the system is 
disabled or handle the error properly. See for example, the Intel's NIC 
driver, which skips an embedded GNSS receiver exporting if the GNSS 
subsystem is disabled. We can catch such issues on the review stage or 
treat like bug if something like this going to sneak into the code.

So here, the WWAN core does its best to kindly inform that functionality 
is not enabled. This is what many other components do, and I personally 
like this approach.

Is it any good justification to clearly indicate the error in case of 
GNSS subsystem is not enabled?

--
Sergey

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

* Re: [RFC PATCH 2/6] net: wwan: core: split port creation and registration
  2025-04-18 23:04         ` Sergey Ryazanov
@ 2025-04-19 11:44           ` Loic Poulain
  2025-04-19 16:51             ` Sergey Ryazanov
  0 siblings, 1 reply; 31+ messages in thread
From: Loic Poulain @ 2025-04-19 11:44 UTC (permalink / raw)
  To: Sergey Ryazanov
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

On Sat, Apr 19, 2025 at 1:04 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>
> Hi Loic,
>
> please find one extra option below.
>
> On 17.04.2025 23:35, Loic Poulain wrote:
> > Hi Sergey,
> >
> > On Mon, Apr 14, 2025 at 11:28 PM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
> >>
> >> Hi Loic,
> >>
> >> thank you that you found a time to check it. See the explanation below,
> >> might be you can suggest a better solution.
> >>
> >> On 14.04.2025 21:50, Loic Poulain wrote:
> >>> Hi Sergey,
> >>>
> >>> On Wed, Apr 9, 2025 at 1:31 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
> >>>>
> >>>> 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.
> >>>>
> >>>> Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
> >>>> ---
> >>>>    drivers/net/wwan/wwan_core.c | 86 ++++++++++++++++++++++--------------
> >>>>    1 file changed, 53 insertions(+), 33 deletions(-)
> >>>>
> >>>> diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c
> >>>> index ade8bbffc93e..045246d7cd50 100644
> >>>> --- a/drivers/net/wwan/wwan_core.c
> >>>> +++ b/drivers/net/wwan/wwan_core.c
> >>>> @@ -357,16 +357,19 @@ static struct attribute *wwan_port_attrs[] = {
> >>>>    };
> >>>>    ATTRIBUTE_GROUPS(wwan_port);
> >>>>
> >>>> -static void wwan_port_destroy(struct device *dev)
> >>>> +static void __wwan_port_destroy(struct wwan_port *port)
> >>>>    {
> >>>> -       struct wwan_port *port = to_wwan_port(dev);
> >>>> -
> >>>> -       ida_free(&minors, MINOR(port->dev.devt));
> >>>>           mutex_destroy(&port->data_lock);
> >>>>           mutex_destroy(&port->ops_lock);
> >>>>           kfree(port);
> >>>>    }
> >>>>
> >>>> +static void wwan_port_destroy(struct device *dev)
> >>>> +{
> >>>> +       ida_free(&minors, MINOR(dev->devt));
> >>>> +       __wwan_port_destroy(to_wwan_port(dev));
> >>>> +}
> >>>> +
> >>>>    static const struct device_type wwan_port_dev_type = {
> >>>>           .name = "wwan_port",
> >>>>           .release = wwan_port_destroy,
> >>>> @@ -440,6 +443,49 @@ 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.)
> >>>> + *
> >>>> + * NB: in case of error function frees the port memory.
> >>>> + */
> >>>> +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) {
> >>>> +               __wwan_port_destroy(port);
> >>>
> >>> I see this is documented above, but it's a bit weird that the port is
> >>> freed inside the register function, it should be up to the caller to
> >>> do this. Is there a reason for this?
> >>
> >> I agree that this looks weird and asymmetrical. I left the port
> >> allocation in wwan_create_port() because both WWAN-exported and
> >> GNSS-exported types of port share the same port allocation. And the port
> >> struct is used as a container to keep all the port registration arguments.
> >>
> >> I did the port freeing inside this function because we free the port
> >> differently depending of the device registration state. If we fail to
> >> initialize the port at earlier stage then we use __wwan_port_destroy()
> >> which basically just releases the memory.
> >>
> >> But if device_register() fails then we are required to use put_device()
> >> which does more job.
> >>
> >> I do not think it is acceptable to skip put_device() call and just
> >> release the memory. Also I do not find maintainable to partially open
> >> code put_device() here in the WWAN-exportable handler and release the
> >> memory in caller function wwan_create_port().
> >>
> >> We could somehow try to return this information from
> >> wwan_port_register_wwan() to wwan_create_port(), so the caller could
> >> decide, shall it use __wwan_port_destroy() or put_device() in case of
> >> failure.
> >>
> >> But I can not see a way to clearly indicate, which releasing approach
> >> should be used by the caller. And even in this case it going to look
> >> weird since the called function controls the caller.
> >>
> >> Another solution for the asymmetry problem is to move the allocation
> >> from the caller to the called function. So the memory will be allocated
> >> and released in the same function. But in this case we will need to pass
> >> all the parameters from wwan_create_port() to wwan_port_register_wwan().
> >> Even if we consolidate the port basic allocation/initialization in a
> >> common routine, the final solution going to look a duplication. E.g.
> >>
> >> struct wwan_port *wwan_port_allocate(struct wwan_device *wwandev,
> >>                                        enum wwan_port_type type,
> >>                                        const struct wwan_port_ops *ops,
> >>                                        struct wwan_port_caps *caps,
> >>                                        void *drvdata)
> >> {
> >>       /* Do the mem allocation and init here */
> >>       return port;
> >> }
> >>
> >> struct wwan_port *wwan_port_register_wwan(struct wwan_device *wwandev,
> >>                          enum wwan_port_type type,
> >>                          const struct wwan_port_ops *ops,
> >>                          struct wwan_port_caps *caps,
> >>                          void *drvdata)
> >> {
> >>       port = wwan_port_allocate(wwandev, type, ops, caps, drvdata);
> >>       /* Proceed with chardev registration or release on failure */
> >>       /* return port; or return ERR_PTR(-err); */
> >> }
> >>
> >> struct wwan_port *wwan_port_register_gnss(struct wwan_device *wwandev,
> >>                          enum wwan_port_type type,
> >>                          const struct wwan_port_ops *ops,
> >>                          struct wwan_port_caps *caps,
> >>                          void *drvdata)
> >> {
> >>       port = wwan_port_allocate(wwandev, type, ops, caps, drvdata);
> >>       /* Proceed with GNSS registration or release on failure */
> >>       /* return port; or return ERR_PTR(-err); */
> >> }
> >>
> >> struct wwan_port *wwan_create_port(struct device *parent,
> >>                                      enum wwan_port_type type,
> >>                                      const struct wwan_port_ops *ops,
> >>                                      struct wwan_port_caps *caps,
> >>                                      void *drvdata)
> >> {
> >>       ...
> >>       wwandev = wwan_create_dev(parent);
> >>       if (type == WWAN_PORT_NMEA)
> >>           port = wwan_port_register_gnss(wwandev, type, ops,
> >>                                          caps, drvdata);
> >>       else
> >>           port = wwan_port_register_wwan(wwandev, type, ops,
> >>                                          caps, drvdata);
> >>       if (!IS_ERR(port))
> >>           return port;
> >>       wwan_remove_dev(wwandev);
> >>       return ERR_CAST(port);
> >> }
> >>
> >> wwan_create_port() looks better in prices of passing a list of arguments
> >> and allocating the port in multiple places.
> >>
> >> Maybe some other design approach, what was overseen?
> >>
> >>
> >> For me, the ideal solution would be a routine that works like
> >> put_device() except calling the device type release handler. Then we can
> >> use it to cleanup leftovers of the failed device_register() call and
> >> then release the memory in the calling wwan_create_port() function.
> >
> > Ok I see, thanks for the clear explanation, I don't see a perfect
> > solution here without over complication. So the current approach is
> > acceptable, can you add a comment in the caller function as well,so
> > that it's clear why we don't have to release the port on error.
>
> Looks like I've found another one solution to move the port resources
> (memory) release back to the function allocating it. It is also a bit
> hackish and I would like to hear a feedback from you.
>
> The port is released inside wwan_port_register_wwan() just because we
> release it differently depending on the initialization stage, right?
>
> We cannot call put_device() before the device_register() call. And that
> is why I've created that __wwan_port_destroy() routine. What if we make
> port eligible to be released with put_device() ASAP? Internally,
> device_register() just sequentially calls device_initialize() and then
> device_add(). Indeed, device_initialize() makes a device struct eligible
> to be released with put_device().
>
> We can avoid using device_register() and instead of it, do the
> registration step by step. Just after the port memory allocation and
> very basic initialization, we can call device_initialize(). And then
> call device_add() when it is time to register the port with the kernel.
> And if something going to go wrong, we can return just an error from
> wwan_port_register_wwan() and release the port with put_device() in
> wwan_create_port() where it was allocated. Something like this:
>
> static int wwan_port_register_wwan(struct wwan_port *port)
> {
>      ...
>      if (something_wrong)
>          return -E<ERROR_TYPE>;
>      ...
>      return 0;
> }
>
> struct wwan_port *wwan_create_port(struct device *parent,
>                                     enum wwan_port_type type,
>                                     const struct wwan_port_ops *ops,
>                                     struct wwan_port_caps *caps,
>                                     void *drvdata)
> {
>      ...
>      port = kzalloc(sizeof(*port), GFP_KERNEL);
>      /* Do basic port init here */
>      port->dev.type = &wwan_port_dev_type;
>      device_initialize(&port->dev);  /* allows put_device() usage */
>
>      if (port->type == WWAN_PORT_NMEA)
>          err = wwan_port_register_gnss(port);
>      else
>          err = wwan_port_register_wwan(port);
>
>      if (err) {
>          put_device(&port->dev);
>          goto error_wwandev_remove;
>      }
>
>      return port;
>      ...
> }
>
> The only drawback I see here is that we have to use put_device() to
> release the port memory even in case of GNSS port. We don't actually
> register the port as device, but I believe, this can be explained with a
> proper comment.

Yes, that's a good alternative, so you would also have to use
put_device in gnss_unregister, or do something like:

void wwan_remove_port(struct wwan_port *port)
{
[...]
    if (port->type == WWAN_PORT_NMEA)
                wwan_port_unregister_gnss(port);
    /* common unregistering (put_device), not necessarily in a
separate function */
   wwan_port_unregister(port);
[...]
}

And probably have a common wwan_port_destroy function:
static void wwan_port_destroy(struct device *dev)
{
    if (port->type != WWAN_PORT_NMEA)
        ida_free(&minors, MINOR(dev->devt));
    [...]
    kfree(port);
}

Regards,
Loic

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

* Re: [RFC PATCH 2/6] net: wwan: core: split port creation and registration
  2025-04-19 11:44           ` Loic Poulain
@ 2025-04-19 16:51             ` Sergey Ryazanov
  0 siblings, 0 replies; 31+ messages in thread
From: Sergey Ryazanov @ 2025-04-19 16:51 UTC (permalink / raw)
  To: Loic Poulain
  Cc: Johannes Berg, Andrew Lunn, Eric Dumazet, David S . Miller,
	Jakub Kicinski, Paolo Abeni, netdev

On 19.04.2025 14:44, Loic Poulain wrote:
> On Sat, Apr 19, 2025 at 1:04 AM Sergey Ryazanov <ryazanov.s.a@gmail.com> wrote:
>> ...
>> Looks like I've found another one solution to move the port resources
>> (memory) release back to the function allocating it. It is also a bit
>> hackish and I would like to hear a feedback from you.
>>
>> The port is released inside wwan_port_register_wwan() just because we
>> release it differently depending on the initialization stage, right?
>>
>> We cannot call put_device() before the device_register() call. And that
>> is why I've created that __wwan_port_destroy() routine. What if we make
>> port eligible to be released with put_device() ASAP? Internally,
>> device_register() just sequentially calls device_initialize() and then
>> device_add(). Indeed, device_initialize() makes a device struct eligible
>> to be released with put_device().
>>
>> We can avoid using device_register() and instead of it, do the
>> registration step by step. Just after the port memory allocation and
>> very basic initialization, we can call device_initialize(). And then
>> call device_add() when it is time to register the port with the kernel.
>> And if something going to go wrong, we can return just an error from
>> wwan_port_register_wwan() and release the port with put_device() in
>> wwan_create_port() where it was allocated. Something like this:
>>
>> static int wwan_port_register_wwan(struct wwan_port *port)
>> {
>>       ...
>>       if (something_wrong)
>>           return -E<ERROR_TYPE>;
>>       ...
>>       return 0;
>> }
>>
>> struct wwan_port *wwan_create_port(struct device *parent,
>>                                      enum wwan_port_type type,
>>                                      const struct wwan_port_ops *ops,
>>                                      struct wwan_port_caps *caps,
>>                                      void *drvdata)
>> {
>>       ...
>>       port = kzalloc(sizeof(*port), GFP_KERNEL);
>>       /* Do basic port init here */
>>       port->dev.type = &wwan_port_dev_type;
>>       device_initialize(&port->dev);  /* allows put_device() usage */
>>
>>       if (port->type == WWAN_PORT_NMEA)
>>           err = wwan_port_register_gnss(port);
>>       else
>>           err = wwan_port_register_wwan(port);
>>
>>       if (err) {
>>           put_device(&port->dev);
>>           goto error_wwandev_remove;
>>       }
>>
>>       return port;
>>       ...
>> }
>>
>> The only drawback I see here is that we have to use put_device() to
>> release the port memory even in case of GNSS port. We don't actually
>> register the port as device, but I believe, this can be explained with a
>> proper comment.
> 
> Yes, that's a good alternative, so you would also have to use
> put_device in gnss_unregister, or do something like:
> 
> void wwan_remove_port(struct wwan_port *port)
> {
> [...]
>      if (port->type == WWAN_PORT_NMEA)
>                  wwan_port_unregister_gnss(port);
>      /* common unregistering (put_device), not necessarily in a
> separate function */
>     wwan_port_unregister(port);
> [...]
> }
> 
> And probably have a common wwan_port_destroy function:
> static void wwan_port_destroy(struct device *dev)
> {
>      if (port->type != WWAN_PORT_NMEA)
>          ida_free(&minors, MINOR(dev->devt));
>      [...]
>      kfree(port);
> }

Yep. This change also makes sense if we manage to release the port in a 
unified way. Will try to implement it next week to see how it's going to 
look like in as a code.

--
Sergey

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

* Re:Re:Re:[RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-09 10:42       ` Sergey Ryazanov
@ 2025-04-28  8:27         ` Slark Xiao
  2025-04-28 13:23           ` [RFC " Muhammad Nuzaihan Kamal Luddin
  0 siblings, 1 reply; 31+ messages in thread
From: Slark Xiao @ 2025-04-28  8:27 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, Qiang Yu, Manivannan Sadhasivam, Johan Hovold


At 2025-04-09 18:42:59, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>On April 9, 2025 11:30:58 AM GMT+03:00, Slark Xiao <slark_xiao@163.com> wrote:
>>
>>Hi Sergey,
>>Device port /dev/gnss0 is enumerated . Does it be expected?
>>I can get the NMEA data from this port by cat or minicom command.
>>But the gpsd.service also can not be initialized normally. It reports:
>>
>>TriggeredBy: ● gpsd.socket
>>    Process: 3824 ExecStartPre=/bin/stty speed 115200 -F $DEVICES (code=exited, status=1/FAILURE)
>>        CPU: 7ms
>>
>>4月 09 16:04:16 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
>>4月 09 16:04:17 jbd stty[3824]: /bin/stty: /dev/gnss0: Inappropriate ioctl for device
>>4月 09 16:04:17 jbd systemd[1]: gpsd.service: Control process exited, code=exited, status=1/FAILURE
>>4月 09 16:04:17 jbd systemd[1]: gpsd.service: Failed with result 'exit-code'.
>>4月 09 16:04:17 jbd systemd[1]: Failed to start GPS (Global Positioning System) Daemon.
>>
>>Seems it's not a serial port.
>
>It is a char dev lacking some IOCTLs support. Yeah.
>
>>Any advice?
>
>Yep. Remove that stty invocation from the service definition. For me, gpsd works flawlessly. You can try to start it manually from a terminal.
>
>--
>Sergey
Hi Sergey,
My device could output the NMEA data by port /dev/gnss0. Something like below:

$GPRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*2D
$GARMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*3C
$GBRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*3F
$GNRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*33
$GNGNS,071634.00,2239.372067,N,11402.653048,E,NAANNN,02,500.0,,,,,V*15
$GPGGA,071634.00,2239.372067,N,11402.653048,E,1,00,500.0,,,,,,*59
$GAGGA,071634.00,2239.372067,N,11402.653048,E,1,01,500.0,,,,,,*49
$GBGGA,071634.00,2239.372067,N,11402.653048,E,1,00,500.0,,,,,,*4B
$GNGGA,071634.00,2239.372067,N,11402.653048,E,1,02,500.0,,,,,,*45
$GPGSV,4,1,13,04,00,038,,05,33,240,,06,41,033,,09,25,058,,1*6F
$GPGSV,4,2,13,11,47,344,,12,33,286,,13,09,185,,17,29,128,,1*6E
$GPGSV,4,3,13,19,54,113,,20,62,284,,22,15,174,,25,09,311,,1*68
$GPGSV,4,4,13,40,00,000,28,1*58
$GLGSV,3,1,09,10,19,245,,06,38,185,,09,06,203,,11,13,296,,1*79
$GLGSV,3,2,09,05,60,064,,20,39,013,,19,17,084,,21,15,321,,1*7E
$GLGSV,3,3,09,04,16,030,,1*41
$GAGSV,3,1,11,02,30,297,,04,32,076,,05,10,188,,06,41,107,,7*78
$GAGSV,3,2,11,09,39,140,,10,26,055,,11,42,027,,12,09,071,,7*7E
$GAGSV,3,3,11,16,36,198,,24,19,176,,36,39,317,,7*45
$GBGSV,4,1,15,01,47,122,,02,46,234,,03,63,189,,04,34,108,,1*7D
$GBGSV,4,2,15,05,23,253,,06,04,187,,07,86,194,,08,68,284,,1*75
$GBGSV,4,3,15,09,03,201,,10,78,299,,11,56,025,,12,29,094,,1*71

But the gpsd progress were stuck with below errors:
● gpsd.service - GPS (Global Positioning System) Daemon
     Loaded: loaded (/lib/systemd/system/gpsd.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2025-04-28 23:16:47 CST; 20s ago
TriggeredBy: ● gpsd.socket
    Process: 5281 ExecStart=/usr/sbin/gpsd $GPSD_OPTIONS $OPTIONS $DEVICES (code=exited, status=0/SUCCESS)
   Main PID: 5283 (gpsd)
      Tasks: 1 (limit: 37272)
     Memory: 652.0K
        CPU: 10ms
     CGroup: /system.slice/gpsd.service
             └─5283 /usr/sbin/gpsd -F /var/run/gpsd.sock /dev/gnss0

4月 28 23:16:47 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
4月 28 23:16:47 jbd systemd[1]: Started GPS (Global Positioning System) Daemon.
4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: SER: device open of /dev/gnss0 failed: Permission denied - retrying read-only
4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: SER: read-only device open of /dev/gnss0 failed: Permission denied
4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: /dev/gnss0: device activation failed, freeing device.

And I checked the gnss device attribute, it reports:
crw------- 1 root root 237, 0  4月 28  2025 /dev/gnss0

May I know how do you fix this?

Thanks



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

* Re: [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-28  8:27         ` Slark Xiao
@ 2025-04-28 13:23           ` Muhammad Nuzaihan Kamal Luddin
  2025-04-29  6:34             ` Slark Xiao
  0 siblings, 1 reply; 31+ messages in thread
From: Muhammad Nuzaihan Kamal Luddin @ 2025-04-28 13:23 UTC (permalink / raw)
  To: Slark Xiao
  Cc: Sergey Ryazanov, Loic Poulain, Johannes Berg, Andrew Lunn,
	Eric Dumazet, David S Miller, Jakub Kicinski, Abeni Paolo, netdev,
	Qiang Yu, Manivannan Sadhasivam, Johan Hovold

Hi Slark,

you cab add udev rules to set the permissions.

Regards,
Zaihan
Sent from my iPhone

> On 28 Apr 2025, at 4:28 PM, Slark Xiao <slark_xiao@163.com> wrote:
> 
> 
> At 2025-04-09 18:42:59, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>> On April 9, 2025 11:30:58 AM GMT+03:00, Slark Xiao <slark_xiao@163.com> wrote:
>>> 
>>> Hi Sergey,
>>> Device port /dev/gnss0 is enumerated . Does it be expected?
>>> I can get the NMEA data from this port by cat or minicom command.
>>> But the gpsd.service also can not be initialized normally. It reports:
>>> 
>>> TriggeredBy: ● gpsd.socket
>>>   Process: 3824 ExecStartPre=/bin/stty speed 115200 -F $DEVICES (code=exited, status=1/FAILURE)
>>>       CPU: 7ms
>>> 
>>> 4月 09 16:04:16 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
>>> 4月 09 16:04:17 jbd stty[3824]: /bin/stty: /dev/gnss0: Inappropriate ioctl for device
>>> 4月 09 16:04:17 jbd systemd[1]: gpsd.service: Control process exited, code=exited, status=1/FAILURE
>>> 4月 09 16:04:17 jbd systemd[1]: gpsd.service: Failed with result 'exit-code'.
>>> 4月 09 16:04:17 jbd systemd[1]: Failed to start GPS (Global Positioning System) Daemon.
>>> 
>>> Seems it's not a serial port.
>> 
>> It is a char dev lacking some IOCTLs support. Yeah.
>> 
>>> Any advice?
>> 
>> Yep. Remove that stty invocation from the service definition. For me, gpsd works flawlessly. You can try to start it manually from a terminal.
>> 
>> --
>> Sergey
> Hi Sergey,
> My device could output the NMEA data by port /dev/gnss0. Something like below:
> 
> $GPRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*2D
> $GARMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*3C
> $GBRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*3F
> $GNRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*33
> $GNGNS,071634.00,2239.372067,N,11402.653048,E,NAANNN,02,500.0,,,,,V*15
> $GPGGA,071634.00,2239.372067,N,11402.653048,E,1,00,500.0,,,,,,*59
> $GAGGA,071634.00,2239.372067,N,11402.653048,E,1,01,500.0,,,,,,*49
> $GBGGA,071634.00,2239.372067,N,11402.653048,E,1,00,500.0,,,,,,*4B
> $GNGGA,071634.00,2239.372067,N,11402.653048,E,1,02,500.0,,,,,,*45
> $GPGSV,4,1,13,04,00,038,,05,33,240,,06,41,033,,09,25,058,,1*6F
> $GPGSV,4,2,13,11,47,344,,12,33,286,,13,09,185,,17,29,128,,1*6E
> $GPGSV,4,3,13,19,54,113,,20,62,284,,22,15,174,,25,09,311,,1*68
> $GPGSV,4,4,13,40,00,000,28,1*58
> $GLGSV,3,1,09,10,19,245,,06,38,185,,09,06,203,,11,13,296,,1*79
> $GLGSV,3,2,09,05,60,064,,20,39,013,,19,17,084,,21,15,321,,1*7E
> $GLGSV,3,3,09,04,16,030,,1*41
> $GAGSV,3,1,11,02,30,297,,04,32,076,,05,10,188,,06,41,107,,7*78
> $GAGSV,3,2,11,09,39,140,,10,26,055,,11,42,027,,12,09,071,,7*7E
> $GAGSV,3,3,11,16,36,198,,24,19,176,,36,39,317,,7*45
> $GBGSV,4,1,15,01,47,122,,02,46,234,,03,63,189,,04,34,108,,1*7D
> $GBGSV,4,2,15,05,23,253,,06,04,187,,07,86,194,,08,68,284,,1*75
> $GBGSV,4,3,15,09,03,201,,10,78,299,,11,56,025,,12,29,094,,1*71
> 
> But the gpsd progress were stuck with below errors:
> ● gpsd.service - GPS (Global Positioning System) Daemon
>     Loaded: loaded (/lib/systemd/system/gpsd.service; enabled; vendor preset: enabled)
>     Active: active (running) since Mon 2025-04-28 23:16:47 CST; 20s ago
> TriggeredBy: ● gpsd.socket
>    Process: 5281 ExecStart=/usr/sbin/gpsd $GPSD_OPTIONS $OPTIONS $DEVICES (code=exited, status=0/SUCCESS)
>   Main PID: 5283 (gpsd)
>      Tasks: 1 (limit: 37272)
>     Memory: 652.0K
>        CPU: 10ms
>     CGroup: /system.slice/gpsd.service
>             └─5283 /usr/sbin/gpsd -F /var/run/gpsd.sock /dev/gnss0
> 
> 4月 28 23:16:47 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
> 4月 28 23:16:47 jbd systemd[1]: Started GPS (Global Positioning System) Daemon.
> 4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: SER: device open of /dev/gnss0 failed: Permission denied - retrying read-only
> 4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: SER: read-only device open of /dev/gnss0 failed: Permission denied
> 4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: /dev/gnss0: device activation failed, freeing device.
> 
> And I checked the gnss device attribute, it reports:
> crw------- 1 root root 237, 0  4月 28  2025 /dev/gnss0
> 
> May I know how do you fix this?
> 
> Thanks
> 
> 

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

* Re:Re: [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-28 13:23           ` [RFC " Muhammad Nuzaihan Kamal Luddin
@ 2025-04-29  6:34             ` Slark Xiao
  2025-06-13  9:23               ` Slark Xiao
  0 siblings, 1 reply; 31+ messages in thread
From: Slark Xiao @ 2025-04-29  6:34 UTC (permalink / raw)
  To: Muhammad Nuzaihan Kamal Luddin
  Cc: Sergey Ryazanov, Loic Poulain, Johannes Berg, Andrew Lunn,
	Eric Dumazet, David S Miller, Jakub Kicinski, Abeni Paolo, netdev,
	Qiang Yu, Manivannan Sadhasivam, Johan Hovold



At 2025-04-28 21:23:53, "Muhammad Nuzaihan Kamal Luddin" <zaihan@unrealasia.net> wrote:
>Hi Slark,
>
>you cab add udev rules to set the permissions.
>
>Regards,
>Zaihan
>Sent from my iPhone
>
Hi Zaihan,
Thanks for your advice.
But I tried in my local, no matter the mode is 644 or 755, similar issue still exist.
BTW, this error only was reported when I call "gpsmon" or "cgps" command.

test reference as below:
jbd@jbd:~$ cat /etc/udev/rules.d/99-gps.rules 
KERNEL=="gnss0", MODE="0755", GROUP="dialout"

4月 29 14:08:17 jbd gpsd[3544]: gpsd:ERROR: SER: device open of /dev/gnss0 failed: Permission denied - retrying read-only
4月 29 14:08:17 jbd gpsd[3544]: gpsd:ERROR: SER: read-only device open of /dev/gnss0 failed: Permission denied
4月 29 14:08:17 jbd gpsd[3544]: gpsd:ERROR: /dev/gnss0: device activation failed, freeing device.
jbd@jbd:~$ 
jbd@jbd:~$ ls -l /dev/gnss0 
crwxr-xr-x 1 root dialout 237, 0  4月 29 14:05 /dev/gnss0

Thanks
>> On 28 Apr 2025, at 4:28 PM, Slark Xiao <slark_xiao@163.com> wrote:
>> 
>> 
>> At 2025-04-09 18:42:59, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>>> On April 9, 2025 11:30:58 AM GMT+03:00, Slark Xiao <slark_xiao@163.com> wrote:
>>>> 
>>>> Hi Sergey,
>>>> Device port /dev/gnss0 is enumerated . Does it be expected?
>>>> I can get the NMEA data from this port by cat or minicom command.
>>>> But the gpsd.service also can not be initialized normally. It reports:
>>>> 
>>>> TriggeredBy: ● gpsd.socket
>>>>   Process: 3824 ExecStartPre=/bin/stty speed 115200 -F $DEVICES (code=exited, status=1/FAILURE)
>>>>       CPU: 7ms
>>>> 
>>>> 4月 09 16:04:16 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
>>>> 4月 09 16:04:17 jbd stty[3824]: /bin/stty: /dev/gnss0: Inappropriate ioctl for device
>>>> 4月 09 16:04:17 jbd systemd[1]: gpsd.service: Control process exited, code=exited, status=1/FAILURE
>>>> 4月 09 16:04:17 jbd systemd[1]: gpsd.service: Failed with result 'exit-code'.
>>>> 4月 09 16:04:17 jbd systemd[1]: Failed to start GPS (Global Positioning System) Daemon.
>>>> 
>>>> Seems it's not a serial port.
>>> 
>>> It is a char dev lacking some IOCTLs support. Yeah.
>>> 
>>>> Any advice?
>>> 
>>> Yep. Remove that stty invocation from the service definition. For me, gpsd works flawlessly. You can try to start it manually from a terminal.
>>> 
>>> --
>>> Sergey
>> Hi Sergey,
>> My device could output the NMEA data by port /dev/gnss0. Something like below:
>> 
>> $GPRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*2D
>> $GARMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*3C
>> $GBRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*3F
>> $GNRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*33
>> $GNGNS,071634.00,2239.372067,N,11402.653048,E,NAANNN,02,500.0,,,,,V*15
>> $GPGGA,071634.00,2239.372067,N,11402.653048,E,1,00,500.0,,,,,,*59
>> $GAGGA,071634.00,2239.372067,N,11402.653048,E,1,01,500.0,,,,,,*49
>> $GBGGA,071634.00,2239.372067,N,11402.653048,E,1,00,500.0,,,,,,*4B
>> $GNGGA,071634.00,2239.372067,N,11402.653048,E,1,02,500.0,,,,,,*45
>> $GPGSV,4,1,13,04,00,038,,05,33,240,,06,41,033,,09,25,058,,1*6F
>> $GPGSV,4,2,13,11,47,344,,12,33,286,,13,09,185,,17,29,128,,1*6E
>> $GPGSV,4,3,13,19,54,113,,20,62,284,,22,15,174,,25,09,311,,1*68
>> $GPGSV,4,4,13,40,00,000,28,1*58
>> $GLGSV,3,1,09,10,19,245,,06,38,185,,09,06,203,,11,13,296,,1*79
>> $GLGSV,3,2,09,05,60,064,,20,39,013,,19,17,084,,21,15,321,,1*7E
>> $GLGSV,3,3,09,04,16,030,,1*41
>> $GAGSV,3,1,11,02,30,297,,04,32,076,,05,10,188,,06,41,107,,7*78
>> $GAGSV,3,2,11,09,39,140,,10,26,055,,11,42,027,,12,09,071,,7*7E
>> $GAGSV,3,3,11,16,36,198,,24,19,176,,36,39,317,,7*45
>> $GBGSV,4,1,15,01,47,122,,02,46,234,,03,63,189,,04,34,108,,1*7D
>> $GBGSV,4,2,15,05,23,253,,06,04,187,,07,86,194,,08,68,284,,1*75
>> $GBGSV,4,3,15,09,03,201,,10,78,299,,11,56,025,,12,29,094,,1*71
>> 
>> But the gpsd progress were stuck with below errors:
>> ● gpsd.service - GPS (Global Positioning System) Daemon
>>     Loaded: loaded (/lib/systemd/system/gpsd.service; enabled; vendor preset: enabled)
>>     Active: active (running) since Mon 2025-04-28 23:16:47 CST; 20s ago
>> TriggeredBy: ● gpsd.socket
>>    Process: 5281 ExecStart=/usr/sbin/gpsd $GPSD_OPTIONS $OPTIONS $DEVICES (code=exited, status=0/SUCCESS)
>>   Main PID: 5283 (gpsd)
>>      Tasks: 1 (limit: 37272)
>>     Memory: 652.0K
>>        CPU: 10ms
>>     CGroup: /system.slice/gpsd.service
>>             └─5283 /usr/sbin/gpsd -F /var/run/gpsd.sock /dev/gnss0
>> 
>> 4月 28 23:16:47 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
>> 4月 28 23:16:47 jbd systemd[1]: Started GPS (Global Positioning System) Daemon.
>> 4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: SER: device open of /dev/gnss0 failed: Permission denied - retrying read-only
>> 4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: SER: read-only device open of /dev/gnss0 failed: Permission denied
>> 4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: /dev/gnss0: device activation failed, freeing device.
>> 
>> And I checked the gnss device attribute, it reports:
>> crw------- 1 root root 237, 0  4月 28  2025 /dev/gnss0
>> 
>> May I know how do you fix this?
>> 
>> Thanks
>> 
>> 

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

* Re:Re:Re: [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-04-29  6:34             ` Slark Xiao
@ 2025-06-13  9:23               ` Slark Xiao
  2025-06-23 20:33                 ` Sergey Ryazanov
  0 siblings, 1 reply; 31+ messages in thread
From: Slark Xiao @ 2025-06-13  9:23 UTC (permalink / raw)
  To: Muhammad Nuzaihan Kamal Luddin, Sergey Ryazanov
  Cc: Sergey Ryazanov, Loic Poulain, Johannes Berg, Andrew Lunn,
	Eric Dumazet, David S Miller, Jakub Kicinski, Abeni Paolo, netdev,
	Qiang Yu, Manivannan Sadhasivam, Johan Hovold


Hi Sergey,
Do we have any updates about this feature? I didn't see any new patch or review suggestion.
Seems this task is pending without any push.

Thanks
Slark
At 2025-04-29 14:34:04, "Slark Xiao" <slark_xiao@163.com> wrote:
>
>
>At 2025-04-28 21:23:53, "Muhammad Nuzaihan Kamal Luddin" <zaihan@unrealasia.net> wrote:
>>Hi Slark,
>>
>>you cab add udev rules to set the permissions.
>>
>>Regards,
>>Zaihan
>>Sent from my iPhone
>>
>Hi Zaihan,
>Thanks for your advice.
>But I tried in my local, no matter the mode is 644 or 755, similar issue still exist.
>BTW, this error only was reported when I call "gpsmon" or "cgps" command.
>
>test reference as below:
>jbd@jbd:~$ cat /etc/udev/rules.d/99-gps.rules 
>KERNEL=="gnss0", MODE="0755", GROUP="dialout"
>
>4月 29 14:08:17 jbd gpsd[3544]: gpsd:ERROR: SER: device open of /dev/gnss0 failed: Permission denied - retrying read-only
>4月 29 14:08:17 jbd gpsd[3544]: gpsd:ERROR: SER: read-only device open of /dev/gnss0 failed: Permission denied
>4月 29 14:08:17 jbd gpsd[3544]: gpsd:ERROR: /dev/gnss0: device activation failed, freeing device.
>jbd@jbd:~$ 
>jbd@jbd:~$ ls -l /dev/gnss0 
>crwxr-xr-x 1 root dialout 237, 0  4月 29 14:05 /dev/gnss0
>
>Thanks
>>> On 28 Apr 2025, at 4:28 PM, Slark Xiao <slark_xiao@163.com> wrote:
>>> 
>>> 
>>> At 2025-04-09 18:42:59, "Sergey Ryazanov" <ryazanov.s.a@gmail.com> wrote:
>>>>> On April 9, 2025 11:30:58 AM GMT+03:00, Slark Xiao <slark_xiao@163.com> wrote:
>>>>> 
>>>>> Hi Sergey,
>>>>> Device port /dev/gnss0 is enumerated . Does it be expected?
>>>>> I can get the NMEA data from this port by cat or minicom command.
>>>>> But the gpsd.service also can not be initialized normally. It reports:
>>>>> 
>>>>> TriggeredBy: ● gpsd.socket
>>>>>   Process: 3824 ExecStartPre=/bin/stty speed 115200 -F $DEVICES (code=exited, status=1/FAILURE)
>>>>>       CPU: 7ms
>>>>> 
>>>>> 4月 09 16:04:16 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
>>>>> 4月 09 16:04:17 jbd stty[3824]: /bin/stty: /dev/gnss0: Inappropriate ioctl for device
>>>>> 4月 09 16:04:17 jbd systemd[1]: gpsd.service: Control process exited, code=exited, status=1/FAILURE
>>>>> 4月 09 16:04:17 jbd systemd[1]: gpsd.service: Failed with result 'exit-code'.
>>>>> 4月 09 16:04:17 jbd systemd[1]: Failed to start GPS (Global Positioning System) Daemon.
>>>>> 
>>>>> Seems it's not a serial port.
>>>> 
>>>> It is a char dev lacking some IOCTLs support. Yeah.
>>>> 
>>>>> Any advice?
>>>> 
>>>> Yep. Remove that stty invocation from the service definition. For me, gpsd works flawlessly. You can try to start it manually from a terminal.
>>>> 
>>>> --
>>>> Sergey
>>> Hi Sergey,
>>> My device could output the NMEA data by port /dev/gnss0. Something like below:
>>> 
>>> $GPRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*2D
>>> $GARMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*3C
>>> $GBRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*3F
>>> $GNRMC,071634.00,A,2239.372067,N,11402.653048,E,,,280425,,,A,V*33
>>> $GNGNS,071634.00,2239.372067,N,11402.653048,E,NAANNN,02,500.0,,,,,V*15
>>> $GPGGA,071634.00,2239.372067,N,11402.653048,E,1,00,500.0,,,,,,*59
>>> $GAGGA,071634.00,2239.372067,N,11402.653048,E,1,01,500.0,,,,,,*49
>>> $GBGGA,071634.00,2239.372067,N,11402.653048,E,1,00,500.0,,,,,,*4B
>>> $GNGGA,071634.00,2239.372067,N,11402.653048,E,1,02,500.0,,,,,,*45
>>> $GPGSV,4,1,13,04,00,038,,05,33,240,,06,41,033,,09,25,058,,1*6F
>>> $GPGSV,4,2,13,11,47,344,,12,33,286,,13,09,185,,17,29,128,,1*6E
>>> $GPGSV,4,3,13,19,54,113,,20,62,284,,22,15,174,,25,09,311,,1*68
>>> $GPGSV,4,4,13,40,00,000,28,1*58
>>> $GLGSV,3,1,09,10,19,245,,06,38,185,,09,06,203,,11,13,296,,1*79
>>> $GLGSV,3,2,09,05,60,064,,20,39,013,,19,17,084,,21,15,321,,1*7E
>>> $GLGSV,3,3,09,04,16,030,,1*41
>>> $GAGSV,3,1,11,02,30,297,,04,32,076,,05,10,188,,06,41,107,,7*78
>>> $GAGSV,3,2,11,09,39,140,,10,26,055,,11,42,027,,12,09,071,,7*7E
>>> $GAGSV,3,3,11,16,36,198,,24,19,176,,36,39,317,,7*45
>>> $GBGSV,4,1,15,01,47,122,,02,46,234,,03,63,189,,04,34,108,,1*7D
>>> $GBGSV,4,2,15,05,23,253,,06,04,187,,07,86,194,,08,68,284,,1*75
>>> $GBGSV,4,3,15,09,03,201,,10,78,299,,11,56,025,,12,29,094,,1*71
>>> 
>>> But the gpsd progress were stuck with below errors:
>>> ● gpsd.service - GPS (Global Positioning System) Daemon
>>>     Loaded: loaded (/lib/systemd/system/gpsd.service; enabled; vendor preset: enabled)
>>>     Active: active (running) since Mon 2025-04-28 23:16:47 CST; 20s ago
>>> TriggeredBy: ● gpsd.socket
>>>    Process: 5281 ExecStart=/usr/sbin/gpsd $GPSD_OPTIONS $OPTIONS $DEVICES (code=exited, status=0/SUCCESS)
>>>   Main PID: 5283 (gpsd)
>>>      Tasks: 1 (limit: 37272)
>>>     Memory: 652.0K
>>>        CPU: 10ms
>>>     CGroup: /system.slice/gpsd.service
>>>             └─5283 /usr/sbin/gpsd -F /var/run/gpsd.sock /dev/gnss0
>>> 
>>> 4月 28 23:16:47 jbd systemd[1]: Starting GPS (Global Positioning System) Daemon...
>>> 4月 28 23:16:47 jbd systemd[1]: Started GPS (Global Positioning System) Daemon.
>>> 4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: SER: device open of /dev/gnss0 failed: Permission denied - retrying read-only
>>> 4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: SER: read-only device open of /dev/gnss0 failed: Permission denied
>>> 4月 28 23:17:02 jbd gpsd[5283]: gpsd:ERROR: /dev/gnss0: device activation failed, freeing device.
>>> 
>>> And I checked the gnss device attribute, it reports:
>>> crw------- 1 root root 237, 0  4月 28  2025 /dev/gnss0
>>> 
>>> May I know how do you fix this?
>>> 
>>> Thanks
>>> 
>>> 

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

* Re: [RFC PATCH 4/6] net: wwan: add NMEA port support
  2025-06-13  9:23               ` Slark Xiao
@ 2025-06-23 20:33                 ` Sergey Ryazanov
  0 siblings, 0 replies; 31+ messages in thread
From: Sergey Ryazanov @ 2025-06-23 20:33 UTC (permalink / raw)
  To: Slark Xiao
  Cc: Muhammad Nuzaihan Kamal Luddin, Loic Poulain, Johannes Berg,
	Andrew Lunn, Eric Dumazet, David S Miller, Jakub Kicinski,
	Abeni Paolo, netdev, Qiang Yu, Manivannan Sadhasivam,
	Johan Hovold

Hi Slark,

On 6/13/25 12:23, Slark Xiao wrote:
> Hi Sergey,
> Do we have any updates about this feature? I didn't see any new patch or review suggestion.
> Seems this task is pending without any push.

You are right. I've missed this case. Let me finish the updated patch 
development as suggested by Loic. Will send in couple of days, I hope.

I'll send it as RFCv2. If it will be Ok with your driver changes, than 
fill free to (re-)send it as a complete series: NMEA port support in 
WWAN core + your patches for the modem driver.

--
Sergey

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

end of thread, other threads:[~2025-06-23 20:33 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-08 23:31 [RFC PATCH 0/6] net: wwan: add NMEA port type support Sergey Ryazanov
2025-04-08 23:31 ` [RFC PATCH 1/6] net: wwan: core: remove unused port_id field Sergey Ryazanov
2025-04-14 18:38   ` Loic Poulain
2025-04-08 23:31 ` [RFC PATCH 2/6] net: wwan: core: split port creation and registration Sergey Ryazanov
2025-04-14 18:50   ` Loic Poulain
2025-04-14 21:29     ` Sergey Ryazanov
2025-04-17 20:35       ` Loic Poulain
2025-04-18 23:04         ` Sergey Ryazanov
2025-04-19 11:44           ` Loic Poulain
2025-04-19 16:51             ` Sergey Ryazanov
2025-04-08 23:31 ` [RFC PATCH 3/6] net: wwan: core: split port unregister and stop Sergey Ryazanov
2025-04-14 18:54   ` Loic Poulain
2025-04-14 21:34     ` Sergey Ryazanov
2025-04-16 20:21       ` Loic Poulain
2025-04-08 23:31 ` [RFC PATCH 4/6] net: wwan: add NMEA port support Sergey Ryazanov
2025-04-09  3:54   ` Slark Xiao
2025-04-09  8:30     ` Slark Xiao
2025-04-09 10:42       ` Sergey Ryazanov
2025-04-28  8:27         ` Slark Xiao
2025-04-28 13:23           ` [RFC " Muhammad Nuzaihan Kamal Luddin
2025-04-29  6:34             ` Slark Xiao
2025-06-13  9:23               ` Slark Xiao
2025-06-23 20:33                 ` Sergey Ryazanov
2025-04-09 20:18     ` Sergey Ryazanov
2025-04-16 20:04   ` Loic Poulain
2025-04-18 23:20     ` Sergey Ryazanov
2025-04-17 20:41   ` Loic Poulain
2025-04-18 23:07     ` Sergey Ryazanov
2025-04-08 23:31 ` [RFC PATCH 5/6] net: wwan: hwsim: refactor to support more port types Sergey Ryazanov
2025-04-14 18:56   ` Loic Poulain
2025-04-08 23:31 ` [RFC PATCH 6/6] net: wwan: hwsim: support NMEA port emulation Sergey Ryazanov

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