Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29cb7002 authored by Alistair Delva's avatar Alistair Delva
Browse files

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=<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: default avatarAlistair Delva <adelva@google.com>
parent 3c996ff8
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -40,4 +40,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
+3 −0
Original line number Diff line number Diff line
@@ -14,3 +14,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

drivers/gnss/cmdline.c

0 → 100644
+139 −0
Original line number Diff line number Diff line
// 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");