[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAFEp6-0McaFcecp-Kg6cj2VACw8MvY0H5dMOk4srS9NhiNBPGw@mail.gmail.com>
Date: Tue, 6 Jan 2026 21:37:32 +0100
From: Loic Poulain <loic.poulain@....qualcomm.com>
To: Slark Xiao <slark_xiao@....com>
Cc: ryazanov.s.a@...il.com, johannes@...solutions.net, andrew+netdev@...n.ch,
davem@...emloft.net, edumazet@...gle.com, kuba@...nel.org,
pabeni@...hat.com, mani@...nel.org, netdev@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: Re: [net-next v4 6/8] net: wwan: hwsim: support NMEA port emulation
On Mon, Jan 5, 2026 at 11:21 AM Slark Xiao <slark_xiao@....com> wrote:
>
> From: Sergey Ryazanov <ryazanov.s.a@...il.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@...il.com>
Reviewed-by: Loic Poulain <loic.poulain@....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@...il.com>
> + * Copyright (c) 2021, 2025, Sergey Ryazanov <ryazanov.s.a@...il.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
>
Powered by blists - more mailing lists