From nobody Tue Feb 10 11:56:15 2026 Received: from m16.mail.163.com (m16.mail.163.com [117.135.210.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7AAE73939D2; Thu, 15 Jan 2026 11:47:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.2 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768477657; cv=none; b=Z+lwq87Ay5pIAC69/QSx4wJqMim4rqFB3AY+jFq81ziHwVednD2LmSCeOnPPOH/3bCTARr/4rDqDF9+53rknCLOshMCE4UU66WBP5SXI8cDPu8s9XOp9vghvzF+sbMkk2dLQHsZqn8urooi1oldeOiFi1Hf7fOZPvi2l7X9R59Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768477657; c=relaxed/simple; bh=SyKRwH1c8v3mmIugLNkly0F2wmJQ7i/fgDJBgjlJW+I=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=CN87FwxpBDv2ZCm3P7xMWQmA60Q5htEHknyXlbu6PqszgFWL0gHtKZGThFA9sxhkcwsB+WbYiZrU6zpwXVelQSRPsHpOXGqicZxbxQTFh4YVl/6E0gJxdCbaaZHzXQF35UU4TWIMUt9L/tMjBv2bfTOx4imejzntOva5hdhYp+E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=cpYx4zzQ; arc=none smtp.client-ip=117.135.210.2 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="cpYx4zzQ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=p1 iWJqqdKYOk1pu+XFp9eZ3OVLK5D4FBSV3XmNWR0X4=; b=cpYx4zzQGBbH9sW5fM 3HNZa8AZMk3mO9+h3vr8o4IQlLJahNj2bsmWogKGrWnKeW2dX0KocflKKLZsVeDJ Al7lajE/NlSPoScRtb3Iz3ATFZgpBQz4qqi+0MyIqQ1l5rK5nJRMRVJI0/pGiA8F EMLSAmsKVEVfU40YJLk543JC4= Received: from localhost.localdomain (unknown []) by gzga-smtp-mtada-g1-3 (Coremail) with SMTP id _____wBHLGiX02hpr2PzGQ--.4331S9; Thu, 15 Jan 2026 19:46:49 +0800 (CST) From: Slark Xiao 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 v7 7/8] net: wwan: hwsim: support NMEA port emulation Date: Thu, 15 Jan 2026 19:46:24 +0800 Message-Id: <20260115114625.46991-8-slark_xiao@163.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260115114625.46991-1-slark_xiao@163.com> References: <20260115114625.46991-1-slark_xiao@163.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wBHLGiX02hpr2PzGQ--.4331S9 X-Coremail-Antispam: 1Uf129KBjvJXoWxKrWUJr4UKFy5AFykJFW8WFg_yoWxJF43pa 1qgasxKrWUAw47Wry3GFsxJFWYqF1kWry0v34fWryYvr1xtry5XFWvkr90vrW5GFZ7uFy3 urs5tw13Z397CrJanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0pR6HqcUUUUU= X-CM-SenderInfo: xvod2y5b0lt0i6rwjhhfrp/xtbCvwqT92lo06ofDgAA34 Content-Type: text/plain; charset="utf-8" From: Sergey Ryazanov 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 Reviewed-by: Loic Poulain --- 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 + * Copyright (c) 2021, 2025, Sergey Ryazanov */ =20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -12,8 +12,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -65,6 +67,9 @@ struct wwan_hwsim_port { AT_PARSER_SKIP_LINE, } pstate; } at_emul; + struct { + struct timer_list timer; + } nmea_emul; }; }; =20 @@ -193,6 +198,108 @@ static const struct wwan_port_ops wwan_hwsim_at_emul_= port_ops =3D { .tx =3D wwan_hwsim_at_emul_tx, }; =20 +#if IS_ENABLED(CONFIG_GNSS) +#define NMEA_MAX_LEN 82 /* Max sentence length */ +#define NMEA_TRAIL_LEN 5 /* '*' + Checksum + */ +#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 =3D 0; + int len; + + s =3D skb_put(skb, NMEA_MAX_LEN + 1); /* +'\0' */ + if (!s) + return; + + va_start(ap, fmt); + len =3D 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 =3D s + 1; *p !=3D '\0'; ++p)/* Skip leading '$' or '!' */ + cs ^=3D *p; + p +=3D snprintf(p, 5 + 1, "*%02X\r\n", cs); + + len =3D (p - s) - (NMEA_MAX_LEN + 1); /* exp. vs real length diff */ + skb->tail +=3D len; /* Adjust tail to real length */ + skb->len +=3D 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] =3D { 43, 44, 8528, 0, + 11, 15, 4559, 0 }; + struct wwan_hwsim_port *port =3D timer_container_of(port, t, nmea_emul.ti= mer); + 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 =3D 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,3= 1.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 =3D 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 =3D 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 =3D { + .start =3D wwan_hwsim_nmea_emul_start, + .stop =3D wwan_hwsim_nmea_emul_stop, + .tx =3D 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(str= uct wwan_hwsim_dev *dev, =20 if (type =3D=3D WWAN_PORT_AT) ops =3D &wwan_hwsim_at_emul_port_ops; +#if IS_ENABLED(CONFIG_GNSS) + else if (type =3D=3D WWAN_PORT_NMEA) + ops =3D &wwan_hwsim_nmea_emul_port_ops; +#endif else return ERR_PTR(-EINVAL); =20 @@ -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); =20 - /* 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 =3D 0; j < 2; ++j) { port =3D 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 =3D 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 } =20 return 0; --=20 2.25.1