From 51e81764d93ee20b6ed84b168a8bb03bfa26a2e1 Mon Sep 17 00:00:00 2001 From: Alistair Delva Date: Wed, 11 Dec 2019 16:44:45 -0800 Subject: [PATCH] ANDROID: gnss: Add command line test driver This driver enables the "takeover" of a serdev bus device by the GNSS subsystem. It can be used to test the GNSS subsystem without needing to write a specific hardware backend. The new module supports the following parameters: gnss_cmdline.serdev=driver/port/serdev gnss_cmdline.type= An example which allows GNSS to wrap a platform serial8250 port and advertise NMEA-0183 data is: gnss_cmdline.serdev=serial8250/serial0/serial0-0 ^ ^ ^ driver port serdev gnss_cmdline.type=0 ^ GNSS_TYPE_NMEA Bug: 146517987 Change-Id: I421386ee4f2ba8f1f0832d9c56a067a600892d3c Signed-off-by: Alistair Delva --- drivers/gnss/Kconfig | 15 +++++ drivers/gnss/Makefile | 3 + drivers/gnss/cmdline.c | 139 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 drivers/gnss/cmdline.c diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig index a0404ce155a6..81f665d3af35 100644 --- a/drivers/gnss/Kconfig +++ b/drivers/gnss/Kconfig @@ -54,4 +54,19 @@ config GNSS_UBX_SERIAL If unsure, say N. +config GNSS_CMDLINE_SERIAL + tristate "Command line test driver for GNSS" + depends on SERIAL_DEV_BUS + select GNSS_SERIAL + ---help--- + Say Y here if you want to test the GNSS subsystem but do not have a + way to communicate a binding through firmware such as DT or ACPI. + The correct serdev device and protocol type must be specified on + the module command line. + + To compile this driver as a module, choose M here: the module will + be called gnss-cmdline. + + If unsure, say N. + endif # GNSS diff --git a/drivers/gnss/Makefile b/drivers/gnss/Makefile index 451f11401ecc..1d27659f5a7a 100644 --- a/drivers/gnss/Makefile +++ b/drivers/gnss/Makefile @@ -17,3 +17,6 @@ gnss-sirf-y := sirf.o obj-$(CONFIG_GNSS_UBX_SERIAL) += gnss-ubx.o gnss-ubx-y := ubx.o + +obj-$(CONFIG_GNSS_CMDLINE_SERIAL) += gnss-cmdline.o +gnss-cmdline-y := cmdline.o diff --git a/drivers/gnss/cmdline.c b/drivers/gnss/cmdline.c new file mode 100644 index 000000000000..3e1d24636b8c --- /dev/null +++ b/drivers/gnss/cmdline.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test driver for GNSS. This driver requires the serdev binding and protocol + * type to be specified on the module command line. + * + * Copyright 2019 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" + +#define GNSS_CMDLINE_MODULE_NAME "gnss-cmdline" + +#define gnss_cmdline_err(...) \ + pr_err(GNSS_CMDLINE_MODULE_NAME ": " __VA_ARGS__) + +static char *serdev; +module_param(serdev, charp, 0644); +MODULE_PARM_DESC(serdev, "serial device to wrap"); + +static int type; +module_param(type, int, 0644); +MODULE_PARM_DESC(serdev, "GNSS protocol type (see 'enum gnss_type')"); + +static struct serdev_device *serdev_device; + +static int name_match(struct device *dev, void *data) +{ + return strstr(dev_name(dev), data) != NULL; +} + +static int __init gnss_cmdline_init(void) +{ + struct device *serial_dev, *port_dev, *serdev_dev; + char *driver_name, *port_name, *serdev_name; + char *serdev_dup, *serdev_dup_sep; + struct gnss_serial *gserial; + int err = -ENODEV; + + /* User did not set the serdev module parameter */ + if (!serdev) + return 0; + + if (type < 0 || type >= GNSS_TYPE_COUNT) { + gnss_cmdline_err("invalid gnss type '%d'\n", type); + return -EINVAL; + } + + serdev_dup = serdev_dup_sep = kstrdup(serdev, GFP_KERNEL); + if (!serdev_dup) + return -ENOMEM; + + driver_name = strsep(&serdev_dup_sep, "/"); + if (!driver_name) { + gnss_cmdline_err("driver name missing\n"); + goto err_free_serdev_dup; + } + + port_name = strsep(&serdev_dup_sep, "/"); + if (!port_name) { + gnss_cmdline_err("port name missing\n"); + goto err_free_serdev_dup; + } + + serdev_name = strsep(&serdev_dup_sep, "/"); + if (!serdev_name) { + gnss_cmdline_err("serdev name missing\n"); + goto err_free_serdev_dup; + } + + /* Find the driver device instance (e.g. serial8250) */ + serial_dev = bus_find_device_by_name(&platform_bus_type, + NULL, driver_name); + if (!serial_dev) { + gnss_cmdline_err("no device '%s'\n", driver_name); + goto err_free_serdev_dup; + } + + /* Find the port device instance (e.g. serial0) */ + port_dev = device_find_child(serial_dev, port_name, name_match); + if (!port_dev) { + gnss_cmdline_err("no port '%s'\n", port_name); + goto err_free_serdev_dup; + } + + /* Find the serdev device instance (e.g. serial0-0) */ + serdev_dev = device_find_child(port_dev, serdev_name, name_match); + if (!serdev_dev) { + gnss_cmdline_err("no serdev '%s'\n", serdev_name); + goto err_free_serdev_dup; + } + + gserial = gnss_serial_allocate(to_serdev_device(serdev_dev), 0); + if (IS_ERR(gserial)) { + err = PTR_ERR(gserial); + goto err_free_serdev_dup; + } + + gserial->gdev->type = type; + + err = gnss_serial_register(gserial); + if (err) { + gnss_serial_free(gserial); + goto err_free_serdev_dup; + } + + serdev_device = to_serdev_device(serdev_dev); + err = 0; +err_free_serdev_dup: + kfree(serdev_dup); + return err; +} + +static void __exit gnss_cmdline_exit(void) +{ + struct gnss_serial *gserial; + + if (!serdev_device) + return; + + gserial = serdev_device_get_drvdata(serdev_device); + + gnss_serial_deregister(gserial); + gnss_serial_free(gserial); +} + +module_init(gnss_cmdline_init); +module_exit(gnss_cmdline_exit); + +MODULE_AUTHOR("Alistair Delva "); +MODULE_DESCRIPTION("GNSS command line driver"); +MODULE_LICENSE("GPL v2");