// 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");