123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- // 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 <linux/device.h>
- #include <linux/gnss.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/serdev.h>
- #include <linux/slab.h>
- #include <linux/string.h>
- #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 <adelva@google.com>");
- MODULE_DESCRIPTION("GNSS command line driver");
- MODULE_LICENSE("GPL v2");
|