Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-wwan-add-nmea-port-type-support'

Slark Xiao says:

====================
net: wwan: add NMEA port type support

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

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

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

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

Link: https://patch.msgid.link/20260126062158.308598-1-slark_xiao@163.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+395 -88
+1
drivers/net/wwan/Kconfig
··· 7 7 8 8 config WWAN 9 9 tristate "WWAN Driver Core" 10 + depends on GNSS || GNSS = n 10 11 help 11 12 Say Y here if you want to use the WWAN driver core. This driver 12 13 provides a common framework for WWAN drivers.
+1
drivers/net/wwan/mhi_wwan_ctrl.c
··· 263 263 { .chan = "QMI", .driver_data = WWAN_PORT_QMI }, 264 264 { .chan = "DIAG", .driver_data = WWAN_PORT_QCDM }, 265 265 { .chan = "FIREHOSE", .driver_data = WWAN_PORT_FIREHOSE }, 266 + { .chan = "NMEA", .driver_data = WWAN_PORT_NMEA }, 266 267 {}, 267 268 }; 268 269 MODULE_DEVICE_TABLE(mhi, mhi_wwan_ctrl_match_table);
+224 -54
drivers/net/wwan/wwan_core.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 - /* Copyright (c) 2021, Linaro Ltd <loic.poulain@linaro.org> */ 2 + /* WWAN Driver Core 3 + * 4 + * Copyright (c) 2021, Linaro Ltd <loic.poulain@linaro.org> 5 + * Copyright (c) 2025, Sergey Ryazanov <ryazanov.s.a@gmail.com> 6 + */ 3 7 4 8 #include <linux/bitmap.h> 5 9 #include <linux/err.h> ··· 20 16 #include <linux/types.h> 21 17 #include <linux/uaccess.h> 22 18 #include <linux/termios.h> 19 + #include <linux/gnss.h> 23 20 #include <linux/wwan.h> 24 21 #include <net/rtnetlink.h> 25 22 #include <uapi/linux/wwan.h> ··· 47 42 * struct wwan_device - The structure that defines a WWAN device 48 43 * 49 44 * @id: WWAN device unique ID. 45 + * @refcount: Reference count of this WWAN device. When this refcount reaches 46 + * zero, the device is deleted. NB: access is protected by global 47 + * wwan_register_lock mutex. 50 48 * @dev: Underlying device. 51 - * @port_id: Current available port ID to pick. 52 49 * @ops: wwan device ops 53 50 * @ops_ctxt: context to pass to ops 54 51 * @debugfs_dir: WWAN device debugfs dir 55 52 */ 56 53 struct wwan_device { 57 54 unsigned int id; 55 + int refcount; 58 56 struct device dev; 59 - atomic_t port_id; 60 57 const struct wwan_ops *ops; 61 58 void *ops_ctxt; 62 59 #ifdef CONFIG_WWAN_DEBUGFS ··· 80 73 * @headroom_len: SKB reserved headroom size 81 74 * @frag_len: Length to fragment packet 82 75 * @at_data: AT port specific data 76 + * @gnss: Pointer to GNSS device associated with this port 83 77 */ 84 78 struct wwan_port { 85 79 enum wwan_port_type type; ··· 99 91 struct ktermios termios; 100 92 int mdmbits; 101 93 } at_data; 94 + struct gnss_device *gnss; 102 95 }; 103 96 }; 97 + 98 + static int wwan_port_op_start(struct wwan_port *port); 99 + static void wwan_port_op_stop(struct wwan_port *port); 100 + static int wwan_port_op_tx(struct wwan_port *port, struct sk_buff *skb, 101 + bool nonblock); 102 + static int wwan_wait_tx(struct wwan_port *port, bool nonblock); 104 103 105 104 static ssize_t index_show(struct device *dev, struct device_attribute *attr, char *buf) 106 105 { ··· 239 224 240 225 /* If wwandev already exists, return it */ 241 226 wwandev = wwan_dev_get_by_parent(parent); 242 - if (!IS_ERR(wwandev)) 227 + if (!IS_ERR(wwandev)) { 228 + wwandev->refcount++; 243 229 goto done_unlock; 230 + } 244 231 245 232 id = ida_alloc(&wwan_dev_ids, GFP_KERNEL); 246 233 if (id < 0) { ··· 261 244 wwandev->dev.class = &wwan_class; 262 245 wwandev->dev.type = &wwan_dev_type; 263 246 wwandev->id = id; 247 + wwandev->refcount = 1; 264 248 dev_set_name(&wwandev->dev, "wwan%d", wwandev->id); 265 249 266 250 err = device_register(&wwandev->dev); ··· 283 265 return wwandev; 284 266 } 285 267 286 - static int is_wwan_child(struct device *dev, void *data) 287 - { 288 - return dev->class == &wwan_class; 289 - } 290 - 291 268 static void wwan_remove_dev(struct wwan_device *wwandev) 292 269 { 293 - int ret; 294 - 295 270 /* Prevent concurrent picking from wwan_create_dev */ 296 271 mutex_lock(&wwan_register_lock); 297 272 298 - /* WWAN device is created and registered (get+add) along with its first 299 - * child port, and subsequent port registrations only grab a reference 300 - * (get). The WWAN device must then be unregistered (del+put) along with 301 - * its last port, and reference simply dropped (put) otherwise. In the 302 - * same fashion, we must not unregister it when the ops are still there. 303 - */ 304 - if (wwandev->ops) 305 - ret = 1; 306 - else 307 - ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child); 273 + if (--wwandev->refcount <= 0) { 274 + struct device *child = device_find_any_child(&wwandev->dev); 308 275 309 - if (!ret) { 276 + put_device(child); 277 + if (WARN_ON(wwandev->ops || child)) /* Paranoid */ 278 + goto out_unlock; 279 + 310 280 #ifdef CONFIG_WWAN_DEBUGFS 311 281 debugfs_remove_recursive(wwandev->debugfs_dir); 312 282 #endif ··· 303 297 put_device(&wwandev->dev); 304 298 } 305 299 300 + out_unlock: 306 301 mutex_unlock(&wwan_register_lock); 307 302 } 308 303 ··· 349 342 .name = "MIPC", 350 343 .devsuf = "mipc", 351 344 }, 345 + /* WWAN_PORT_NMEA is exported via the GNSS subsystem */ 352 346 }; 353 347 354 348 static ssize_t type_show(struct device *dev, struct device_attribute *attr, ··· 371 363 { 372 364 struct wwan_port *port = to_wwan_port(dev); 373 365 374 - ida_free(&minors, MINOR(port->dev.devt)); 366 + if (dev->class == &wwan_class) 367 + ida_free(&minors, MINOR(dev->devt)); 375 368 mutex_destroy(&port->data_lock); 376 369 mutex_destroy(&port->ops_lock); 377 370 kfree(port); ··· 451 442 return dev_set_name(&port->dev, "%s", buf); 452 443 } 453 444 445 + /* Register a regular WWAN port device (e.g. AT, MBIM, etc.) */ 446 + static int wwan_port_register_wwan(struct wwan_port *port) 447 + { 448 + struct wwan_device *wwandev = to_wwan_dev(port->dev.parent); 449 + char namefmt[0x20]; 450 + int minor, err; 451 + 452 + /* A port is exposed as character device, get a minor */ 453 + minor = ida_alloc_range(&minors, 0, WWAN_MAX_MINORS - 1, GFP_KERNEL); 454 + if (minor < 0) 455 + return minor; 456 + 457 + port->dev.class = &wwan_class; 458 + port->dev.devt = MKDEV(wwan_major, minor); 459 + 460 + /* allocate unique name based on wwan device id, port type and number */ 461 + snprintf(namefmt, sizeof(namefmt), "wwan%u%s%%d", wwandev->id, 462 + wwan_port_types[port->type].devsuf); 463 + 464 + /* Serialize ports registration */ 465 + mutex_lock(&wwan_register_lock); 466 + 467 + __wwan_port_dev_assign_name(port, namefmt); 468 + err = device_add(&port->dev); 469 + 470 + mutex_unlock(&wwan_register_lock); 471 + 472 + if (err) { 473 + ida_free(&minors, minor); 474 + port->dev.class = NULL; 475 + return err; 476 + } 477 + 478 + dev_info(&wwandev->dev, "port %s attached\n", dev_name(&port->dev)); 479 + 480 + return 0; 481 + } 482 + 483 + /* Unregister a regular WWAN port (e.g. AT, MBIM, etc) */ 484 + static void wwan_port_unregister_wwan(struct wwan_port *port) 485 + { 486 + struct wwan_device *wwandev = to_wwan_dev(port->dev.parent); 487 + 488 + dev_set_drvdata(&port->dev, NULL); 489 + 490 + dev_info(&wwandev->dev, "port %s disconnected\n", dev_name(&port->dev)); 491 + 492 + device_del(&port->dev); 493 + } 494 + 495 + #if IS_ENABLED(CONFIG_GNSS) 496 + static int wwan_gnss_open(struct gnss_device *gdev) 497 + { 498 + return wwan_port_op_start(gnss_get_drvdata(gdev)); 499 + } 500 + 501 + static void wwan_gnss_close(struct gnss_device *gdev) 502 + { 503 + wwan_port_op_stop(gnss_get_drvdata(gdev)); 504 + } 505 + 506 + static int wwan_gnss_write(struct gnss_device *gdev, const unsigned char *buf, 507 + size_t count) 508 + { 509 + struct wwan_port *port = gnss_get_drvdata(gdev); 510 + struct sk_buff *skb, *head = NULL, *tail = NULL; 511 + size_t frag_len, remain = count; 512 + int ret; 513 + 514 + ret = wwan_wait_tx(port, false); 515 + if (ret) 516 + return ret; 517 + 518 + do { 519 + frag_len = min(remain, port->frag_len); 520 + skb = alloc_skb(frag_len + port->headroom_len, GFP_KERNEL); 521 + if (!skb) { 522 + ret = -ENOMEM; 523 + goto freeskb; 524 + } 525 + skb_reserve(skb, port->headroom_len); 526 + memcpy(skb_put(skb, frag_len), buf + count - remain, frag_len); 527 + 528 + if (!head) { 529 + head = skb; 530 + } else { 531 + if (!tail) 532 + skb_shinfo(head)->frag_list = skb; 533 + else 534 + tail->next = skb; 535 + 536 + tail = skb; 537 + head->data_len += skb->len; 538 + head->len += skb->len; 539 + head->truesize += skb->truesize; 540 + } 541 + } while (remain -= frag_len); 542 + 543 + ret = wwan_port_op_tx(port, head, false); 544 + if (!ret) 545 + return count; 546 + 547 + freeskb: 548 + kfree_skb(head); 549 + return ret; 550 + } 551 + 552 + static struct gnss_operations wwan_gnss_ops = { 553 + .open = wwan_gnss_open, 554 + .close = wwan_gnss_close, 555 + .write_raw = wwan_gnss_write, 556 + }; 557 + 558 + /* GNSS port specific device registration */ 559 + static int wwan_port_register_gnss(struct wwan_port *port) 560 + { 561 + struct wwan_device *wwandev = to_wwan_dev(port->dev.parent); 562 + struct gnss_device *gdev; 563 + int err; 564 + 565 + gdev = gnss_allocate_device(&wwandev->dev); 566 + if (!gdev) 567 + return -ENOMEM; 568 + 569 + /* NB: for now we support only NMEA WWAN port type, so hardcode 570 + * the GNSS port type. If more GNSS WWAN port types will be added, 571 + * then we should dynamically map WWAN port type to GNSS type. 572 + */ 573 + gdev->type = GNSS_TYPE_NMEA; 574 + gdev->ops = &wwan_gnss_ops; 575 + gnss_set_drvdata(gdev, port); 576 + 577 + port->gnss = gdev; 578 + 579 + err = gnss_register_device(gdev); 580 + if (err) { 581 + gnss_put_device(gdev); 582 + return err; 583 + } 584 + 585 + dev_info(&wwandev->dev, "port %s attached\n", dev_name(&gdev->dev)); 586 + 587 + return 0; 588 + } 589 + 590 + /* GNSS port specific device unregistration */ 591 + static void wwan_port_unregister_gnss(struct wwan_port *port) 592 + { 593 + struct wwan_device *wwandev = to_wwan_dev(port->dev.parent); 594 + struct gnss_device *gdev = port->gnss; 595 + 596 + dev_info(&wwandev->dev, "port %s disconnected\n", dev_name(&gdev->dev)); 597 + 598 + gnss_deregister_device(gdev); 599 + gnss_put_device(gdev); 600 + } 601 + #else 602 + static int wwan_port_register_gnss(struct wwan_port *port) 603 + { 604 + return -EOPNOTSUPP; 605 + } 606 + 607 + static void wwan_port_unregister_gnss(struct wwan_port *port) 608 + { 609 + WARN_ON(1); /* This handler cannot be called */ 610 + } 611 + #endif 612 + 454 613 struct wwan_port *wwan_create_port(struct device *parent, 455 614 enum wwan_port_type type, 456 615 const struct wwan_port_ops *ops, ··· 627 450 { 628 451 struct wwan_device *wwandev; 629 452 struct wwan_port *port; 630 - char namefmt[0x20]; 631 - int minor, err; 453 + int err; 632 454 633 455 if (type > WWAN_PORT_MAX || !ops) 634 456 return ERR_PTR(-EINVAL); ··· 639 463 if (IS_ERR(wwandev)) 640 464 return ERR_CAST(wwandev); 641 465 642 - /* A port is exposed as character device, get a minor */ 643 - minor = ida_alloc_range(&minors, 0, WWAN_MAX_MINORS - 1, GFP_KERNEL); 644 - if (minor < 0) { 645 - err = minor; 646 - goto error_wwandev_remove; 647 - } 648 - 649 466 port = kzalloc(sizeof(*port), GFP_KERNEL); 650 467 if (!port) { 651 468 err = -ENOMEM; 652 - ida_free(&minors, minor); 653 469 goto error_wwandev_remove; 654 470 } 655 471 ··· 655 487 mutex_init(&port->data_lock); 656 488 657 489 port->dev.parent = &wwandev->dev; 658 - port->dev.class = &wwan_class; 659 490 port->dev.type = &wwan_port_dev_type; 660 - port->dev.devt = MKDEV(wwan_major, minor); 661 491 dev_set_drvdata(&port->dev, drvdata); 492 + device_initialize(&port->dev); 662 493 663 - /* allocate unique name based on wwan device id, port type and number */ 664 - snprintf(namefmt, sizeof(namefmt), "wwan%u%s%%d", wwandev->id, 665 - wwan_port_types[port->type].devsuf); 666 - 667 - /* Serialize ports registration */ 668 - mutex_lock(&wwan_register_lock); 669 - 670 - __wwan_port_dev_assign_name(port, namefmt); 671 - err = device_register(&port->dev); 672 - 673 - mutex_unlock(&wwan_register_lock); 494 + if (port->type == WWAN_PORT_NMEA) 495 + err = wwan_port_register_gnss(port); 496 + else 497 + err = wwan_port_register_wwan(port); 674 498 675 499 if (err) 676 500 goto error_put_device; 677 501 678 - dev_info(&wwandev->dev, "port %s attached\n", dev_name(&port->dev)); 679 502 return port; 680 503 681 504 error_put_device: ··· 683 524 struct wwan_device *wwandev = to_wwan_dev(port->dev.parent); 684 525 685 526 mutex_lock(&port->ops_lock); 686 - if (port->start_count) 527 + if (port->start_count) { 687 528 port->ops->stop(port); 529 + port->start_count = 0; 530 + } 688 531 port->ops = NULL; /* Prevent any new port operations (e.g. from fops) */ 689 532 mutex_unlock(&port->ops_lock); 690 533 691 534 wake_up_interruptible(&port->waitqueue); 692 - 693 535 skb_queue_purge(&port->rxq); 694 - dev_set_drvdata(&port->dev, NULL); 695 536 696 - dev_info(&wwandev->dev, "port %s disconnected\n", dev_name(&port->dev)); 697 - device_unregister(&port->dev); 537 + if (port->type == WWAN_PORT_NMEA) 538 + wwan_port_unregister_gnss(port); 539 + else 540 + wwan_port_unregister_wwan(port); 541 + 542 + put_device(&port->dev); 698 543 699 544 /* Release related wwan device */ 700 545 wwan_remove_dev(wwandev); ··· 707 544 708 545 void wwan_port_rx(struct wwan_port *port, struct sk_buff *skb) 709 546 { 710 - skb_queue_tail(&port->rxq, skb); 711 - wake_up_interruptible(&port->waitqueue); 547 + if (port->type == WWAN_PORT_NMEA) { 548 + #if IS_ENABLED(CONFIG_GNSS) 549 + gnss_insert_raw(port->gnss, skb->data, skb->len); 550 + #endif 551 + consume_skb(skb); 552 + } else { 553 + skb_queue_tail(&port->rxq, skb); 554 + wake_up_interruptible(&port->waitqueue); 555 + } 712 556 } 713 557 EXPORT_SYMBOL_GPL(wwan_port_rx); 714 558
+167 -34
drivers/net/wwan/wwan_hwsim.c
··· 2 2 /* 3 3 * WWAN device simulator for WWAN framework testing. 4 4 * 5 - * Copyright (c) 2021, Sergey Ryazanov <ryazanov.s.a@gmail.com> 5 + * Copyright (c) 2021, 2025, Sergey Ryazanov <ryazanov.s.a@gmail.com> 6 6 */ 7 7 8 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ··· 12 12 #include <linux/slab.h> 13 13 #include <linux/device.h> 14 14 #include <linux/spinlock.h> 15 + #include <linux/time.h> 15 16 #include <linux/list.h> 16 17 #include <linux/skbuff.h> 18 + #include <linux/timer.h> 17 19 #include <linux/netdevice.h> 18 20 #include <linux/wwan.h> 19 21 #include <linux/debugfs.h> ··· 58 56 struct wwan_port *wwan; 59 57 struct work_struct del_work; 60 58 struct dentry *debugfs_topdir; 61 - enum { /* AT command parser state */ 62 - AT_PARSER_WAIT_A, 63 - AT_PARSER_WAIT_T, 64 - AT_PARSER_WAIT_TERM, 65 - AT_PARSER_SKIP_LINE, 66 - } pstate; 59 + union { 60 + struct { 61 + enum { /* AT command parser state */ 62 + AT_PARSER_WAIT_A, 63 + AT_PARSER_WAIT_T, 64 + AT_PARSER_WAIT_TERM, 65 + AT_PARSER_SKIP_LINE, 66 + } pstate; 67 + } at_emul; 68 + struct { 69 + struct timer_list timer; 70 + } nmea_emul; 71 + }; 67 72 }; 68 73 69 74 static const struct file_operations wwan_hwsim_debugfs_portdestroy_fops; ··· 110 101 .setup = wwan_hwsim_netdev_setup, 111 102 }; 112 103 113 - static int wwan_hwsim_port_start(struct wwan_port *wport) 104 + static int wwan_hwsim_at_emul_start(struct wwan_port *wport) 114 105 { 115 106 struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport); 116 107 117 - port->pstate = AT_PARSER_WAIT_A; 108 + port->at_emul.pstate = AT_PARSER_WAIT_A; 118 109 119 110 return 0; 120 111 } 121 112 122 - static void wwan_hwsim_port_stop(struct wwan_port *wport) 113 + static void wwan_hwsim_at_emul_stop(struct wwan_port *wport) 123 114 { 124 115 } 125 116 ··· 129 120 * 130 121 * Be aware that this processor is not fully V.250 compliant. 131 122 */ 132 - static int wwan_hwsim_port_tx(struct wwan_port *wport, struct sk_buff *in) 123 + static int wwan_hwsim_at_emul_tx(struct wwan_port *wport, struct sk_buff *in) 133 124 { 134 125 struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport); 135 126 struct sk_buff *out; ··· 151 142 for (i = 0, s = 0; i < in->len; ++i) { 152 143 char c = in->data[i]; 153 144 154 - if (port->pstate == AT_PARSER_WAIT_A) { 145 + if (port->at_emul.pstate == AT_PARSER_WAIT_A) { 155 146 if (c == 'A' || c == 'a') 156 - port->pstate = AT_PARSER_WAIT_T; 147 + port->at_emul.pstate = AT_PARSER_WAIT_T; 157 148 else if (c != '\n') /* Ignore formating char */ 158 - port->pstate = AT_PARSER_SKIP_LINE; 159 - } else if (port->pstate == AT_PARSER_WAIT_T) { 149 + port->at_emul.pstate = AT_PARSER_SKIP_LINE; 150 + } else if (port->at_emul.pstate == AT_PARSER_WAIT_T) { 160 151 if (c == 'T' || c == 't') 161 - port->pstate = AT_PARSER_WAIT_TERM; 152 + port->at_emul.pstate = AT_PARSER_WAIT_TERM; 162 153 else 163 - port->pstate = AT_PARSER_SKIP_LINE; 164 - } else if (port->pstate == AT_PARSER_WAIT_TERM) { 154 + port->at_emul.pstate = AT_PARSER_SKIP_LINE; 155 + } else if (port->at_emul.pstate == AT_PARSER_WAIT_TERM) { 165 156 if (c != '\r') 166 157 continue; 167 158 /* Consume the trailing formatting char as well */ ··· 171 162 skb_put_data(out, &in->data[s], n);/* Echo */ 172 163 skb_put_data(out, "\r\nOK\r\n", 6); 173 164 s = i + 1; 174 - port->pstate = AT_PARSER_WAIT_A; 175 - } else if (port->pstate == AT_PARSER_SKIP_LINE) { 165 + port->at_emul.pstate = AT_PARSER_WAIT_A; 166 + } else if (port->at_emul.pstate == AT_PARSER_SKIP_LINE) { 176 167 if (c != '\r') 177 168 continue; 178 - port->pstate = AT_PARSER_WAIT_A; 169 + port->at_emul.pstate = AT_PARSER_WAIT_A; 179 170 } 180 171 } 181 172 ··· 192 183 return 0; 193 184 } 194 185 195 - static const struct wwan_port_ops wwan_hwsim_port_ops = { 196 - .start = wwan_hwsim_port_start, 197 - .stop = wwan_hwsim_port_stop, 198 - .tx = wwan_hwsim_port_tx, 186 + static const struct wwan_port_ops wwan_hwsim_at_emul_port_ops = { 187 + .start = wwan_hwsim_at_emul_start, 188 + .stop = wwan_hwsim_at_emul_stop, 189 + .tx = wwan_hwsim_at_emul_tx, 199 190 }; 200 191 201 - static struct wwan_hwsim_port *wwan_hwsim_port_new(struct wwan_hwsim_dev *dev) 192 + #if IS_ENABLED(CONFIG_GNSS) 193 + #define NMEA_MAX_LEN 82 /* Max sentence length */ 194 + #define NMEA_TRAIL_LEN 5 /* '*' + Checksum + <CR><LF> */ 195 + #define NMEA_MAX_DATA_LEN (NMEA_MAX_LEN - NMEA_TRAIL_LEN) 196 + 197 + static __printf(2, 3) 198 + void wwan_hwsim_nmea_skb_push_sentence(struct sk_buff *skb, 199 + const char *fmt, ...) 202 200 { 201 + unsigned char *s, *p; 202 + va_list ap; 203 + u8 cs = 0; 204 + int len; 205 + 206 + s = skb_put(skb, NMEA_MAX_LEN + 1); /* +'\0' */ 207 + if (!s) 208 + return; 209 + 210 + va_start(ap, fmt); 211 + len = vsnprintf(s, NMEA_MAX_DATA_LEN + 1, fmt, ap); 212 + va_end(ap); 213 + if (WARN_ON_ONCE(len > NMEA_MAX_DATA_LEN))/* No space for trailer */ 214 + return; 215 + 216 + for (p = s + 1; *p != '\0'; ++p)/* Skip leading '$' or '!' */ 217 + cs ^= *p; 218 + p += snprintf(p, 5 + 1, "*%02X\r\n", cs); 219 + 220 + len = (p - s) - (NMEA_MAX_LEN + 1); /* exp. vs real length diff */ 221 + skb->tail += len; /* Adjust tail to real length */ 222 + skb->len += len; 223 + } 224 + 225 + static void wwan_hwsim_nmea_emul_timer(struct timer_list *t) 226 + { 227 + /* 43.74754722298909 N 11.25759835922875 E in DMM format */ 228 + static const unsigned int coord[4 * 2] = { 43, 44, 8528, 0, 229 + 11, 15, 4559, 0 }; 230 + struct wwan_hwsim_port *port = timer_container_of(port, t, nmea_emul.timer); 231 + struct sk_buff *skb; 232 + struct tm tm; 233 + 234 + time64_to_tm(ktime_get_real_seconds(), 0, &tm); 235 + 236 + mod_timer(&port->nmea_emul.timer, jiffies + HZ); /* 1 second */ 237 + 238 + skb = alloc_skb(NMEA_MAX_LEN * 2 + 2, GFP_ATOMIC); /* GGA + RMC */ 239 + if (!skb) 240 + return; 241 + 242 + wwan_hwsim_nmea_skb_push_sentence(skb, 243 + "$GPGGA,%02u%02u%02u.000,%02u%02u.%04u,%c,%03u%02u.%04u,%c,1,7,1.03,176.2,M,55.2,M,,", 244 + tm.tm_hour, tm.tm_min, tm.tm_sec, 245 + coord[0], coord[1], coord[2], 246 + coord[3] ? 'S' : 'N', 247 + coord[4], coord[5], coord[6], 248 + coord[7] ? 'W' : 'E'); 249 + 250 + wwan_hwsim_nmea_skb_push_sentence(skb, 251 + "$GPRMC,%02u%02u%02u.000,A,%02u%02u.%04u,%c,%03u%02u.%04u,%c,0.02,31.66,%02u%02u%02u,,,A", 252 + tm.tm_hour, tm.tm_min, tm.tm_sec, 253 + coord[0], coord[1], coord[2], 254 + coord[3] ? 'S' : 'N', 255 + coord[4], coord[5], coord[6], 256 + coord[7] ? 'W' : 'E', 257 + tm.tm_mday, tm.tm_mon + 1, 258 + (unsigned int)tm.tm_year - 100); 259 + 260 + wwan_port_rx(port->wwan, skb); 261 + } 262 + 263 + static int wwan_hwsim_nmea_emul_start(struct wwan_port *wport) 264 + { 265 + struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport); 266 + 267 + timer_setup(&port->nmea_emul.timer, wwan_hwsim_nmea_emul_timer, 0); 268 + wwan_hwsim_nmea_emul_timer(&port->nmea_emul.timer); 269 + 270 + return 0; 271 + } 272 + 273 + static void wwan_hwsim_nmea_emul_stop(struct wwan_port *wport) 274 + { 275 + struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport); 276 + 277 + timer_delete_sync(&port->nmea_emul.timer); 278 + } 279 + 280 + static int wwan_hwsim_nmea_emul_tx(struct wwan_port *wport, struct sk_buff *in) 281 + { 282 + consume_skb(in); 283 + 284 + return 0; 285 + } 286 + 287 + static const struct wwan_port_ops wwan_hwsim_nmea_emul_port_ops = { 288 + .start = wwan_hwsim_nmea_emul_start, 289 + .stop = wwan_hwsim_nmea_emul_stop, 290 + .tx = wwan_hwsim_nmea_emul_tx, 291 + }; 292 + #endif 293 + 294 + static struct wwan_hwsim_port *wwan_hwsim_port_new(struct wwan_hwsim_dev *dev, 295 + enum wwan_port_type type) 296 + { 297 + const struct wwan_port_ops *ops; 203 298 struct wwan_hwsim_port *port; 204 299 char name[0x10]; 205 300 int err; 301 + 302 + if (type == WWAN_PORT_AT) 303 + ops = &wwan_hwsim_at_emul_port_ops; 304 + #if IS_ENABLED(CONFIG_GNSS) 305 + else if (type == WWAN_PORT_NMEA) 306 + ops = &wwan_hwsim_nmea_emul_port_ops; 307 + #endif 308 + else 309 + return ERR_PTR(-EINVAL); 206 310 207 311 port = kzalloc(sizeof(*port), GFP_KERNEL); 208 312 if (!port) ··· 327 205 port->id = dev->port_idx++; 328 206 spin_unlock(&dev->ports_lock); 329 207 330 - port->wwan = wwan_create_port(&dev->dev, WWAN_PORT_AT, 331 - &wwan_hwsim_port_ops, 332 - NULL, port); 208 + port->wwan = wwan_create_port(&dev->dev, type, ops, NULL, port); 333 209 if (IS_ERR(port->wwan)) { 334 210 err = PTR_ERR(port->wwan); 335 211 goto err_free_port; ··· 512 392 struct wwan_hwsim_dev *dev = file->private_data; 513 393 struct wwan_hwsim_port *port; 514 394 515 - port = wwan_hwsim_port_new(dev); 395 + port = wwan_hwsim_port_new(dev, WWAN_PORT_AT); 516 396 if (IS_ERR(port)) 517 397 return PTR_ERR(port); 518 398 ··· 579 459 int i, j; 580 460 581 461 for (i = 0; i < wwan_hwsim_devsnum; ++i) { 462 + struct wwan_hwsim_port *port; 463 + 582 464 dev = wwan_hwsim_dev_new(); 583 465 if (IS_ERR(dev)) 584 466 return PTR_ERR(dev); ··· 589 467 list_add_tail(&dev->list, &wwan_hwsim_devs); 590 468 spin_unlock(&wwan_hwsim_devs_lock); 591 469 592 - /* Create a couple of ports per each device to accelerate 470 + /* Create a few various ports per each device to accelerate 593 471 * the simulator readiness time. 594 472 */ 595 - for (j = 0; j < 2; ++j) { 596 - struct wwan_hwsim_port *port; 597 473 598 - port = wwan_hwsim_port_new(dev); 474 + for (j = 0; j < 2; ++j) { 475 + port = wwan_hwsim_port_new(dev, WWAN_PORT_AT); 599 476 if (IS_ERR(port)) 600 477 return PTR_ERR(port); 601 478 ··· 602 481 list_add_tail(&port->list, &dev->ports); 603 482 spin_unlock(&dev->ports_lock); 604 483 } 484 + 485 + #if IS_ENABLED(CONFIG_GNSS) 486 + port = wwan_hwsim_port_new(dev, WWAN_PORT_NMEA); 487 + if (IS_ERR(port)) { 488 + dev_warn(&dev->dev, "failed to create initial NMEA port: %d\n", 489 + (int)PTR_ERR(port)); 490 + } else { 491 + spin_lock(&dev->ports_lock); 492 + list_add_tail(&port->list, &dev->ports); 493 + spin_unlock(&dev->ports_lock); 494 + } 495 + #endif 605 496 } 606 497 607 498 return 0;
+2
include/linux/wwan.h
··· 19 19 * @WWAN_PORT_FASTBOOT: Fastboot protocol control 20 20 * @WWAN_PORT_ADB: ADB protocol control 21 21 * @WWAN_PORT_MIPC: MTK MIPC diagnostic interface 22 + * @WWAN_PORT_NMEA: embedded GNSS receiver with NMEA output 22 23 * 23 24 * @WWAN_PORT_MAX: Highest supported port types 24 25 * @WWAN_PORT_UNKNOWN: Special value to indicate an unknown port type ··· 35 34 WWAN_PORT_FASTBOOT, 36 35 WWAN_PORT_ADB, 37 36 WWAN_PORT_MIPC, 37 + WWAN_PORT_NMEA, 38 38 39 39 /* Add new port types above this line */ 40 40