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=<protocol number> 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 <adelva@google.com>tirimbino
parent
cdad3113fc
commit
1f70bbcd4b
@ -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 <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"); |
Loading…
Reference in new issue