From: Slark Xiao <slark_xiao@163.com>
To: loic.poulain@oss.qualcomm.com, ryazanov.s.a@gmail.com,
johannes@sipsolutions.net, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, mani@kernel.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, slark_xiao@163.com
Subject: [net-next v6 7/8] net: wwan: hwsim: support NMEA port emulation
Date: Thu, 15 Jan 2026 17:54:16 +0800 [thread overview]
Message-ID: <20260115095417.36975-8-slark_xiao@163.com> (raw)
In-Reply-To: <20260115095417.36975-1-slark_xiao@163.com>
From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Support NMEA port emulation for the WWAN core GNSS port testing purpose.
Emulator produces pair of GGA + RMC sentences every second what should
be enough to fool gpsd into believing it is working with a NMEA GNSS
receiver.
If the GNSS system is enabled then one NMEA port will be created
automatically for the simulated WWAN device. Manual NMEA port creation
is not supported at the moment.
Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
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 11d15dc39041..e4b1bbff9af2 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 = timer_container_of(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.25.1
next prev parent reply other threads:[~2026-01-15 9:55 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-15 9:54 [net-next v6 0/8] net: wwan: add NMEA port type support Slark Xiao
2026-01-15 9:54 ` [net-next v6 1/8] net: wwan: core: remove unused port_id field Slark Xiao
2026-01-15 9:54 ` [net-next v6 2/8] net: wwan: core: explicit WWAN device reference counting Slark Xiao
2026-01-15 9:54 ` [net-next v6 3/8] net: wwan: core: split port creation and registration Slark Xiao
2026-01-15 9:54 ` [net-next v6 4/8] net: wwan: core: split port unregister and stop Slark Xiao
2026-01-15 9:54 ` [net-next v6 5/8] net: wwan: add NMEA port support Slark Xiao
2026-01-15 9:54 ` [net-next v6 6/8] net: wwan: hwsim: refactor to support more port types Slark Xiao
2026-01-15 9:54 ` Slark Xiao [this message]
2026-01-15 9:54 ` [net-next v6 8/8] net: wwan: mhi_wwan_ctrl: Add NMEA channel support Slark Xiao
2026-01-15 11:43 ` Re:[net-next v6 0/8] net: wwan: add NMEA port type support Slark Xiao
2026-01-16 2:41 ` [net-next " Jakub Kicinski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260115095417.36975-8-slark_xiao@163.com \
--to=slark_xiao@163.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=johannes@sipsolutions.net \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=loic.poulain@oss.qualcomm.com \
--cc=mani@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=ryazanov.s.a@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox