• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Test driver for GNSS. This driver requires the serdev binding and protocol
4  * type to be specified on the module command line.
5  *
6  * Copyright 2019 Google LLC
7  */
8 
9 #include <linux/device.h>
10 #include <linux/gnss.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/serdev.h>
14 #include <linux/slab.h>
15 #include <linux/string.h>
16 
17 #include "serial.h"
18 
19 #define GNSS_CMDLINE_MODULE_NAME "gnss-cmdline"
20 
21 #define gnss_cmdline_err(...) \
22 	pr_err(GNSS_CMDLINE_MODULE_NAME ": " __VA_ARGS__)
23 
24 static char *serdev;
25 module_param(serdev, charp, 0644);
26 MODULE_PARM_DESC(serdev, "serial device to wrap");
27 
28 static int type;
29 module_param(type, int, 0644);
30 MODULE_PARM_DESC(serdev, "GNSS protocol type (see 'enum gnss_type')");
31 
32 static struct serdev_device *serdev_device;
33 
name_match(struct device * dev,void * data)34 static int name_match(struct device *dev, void *data)
35 {
36 	return strstr(dev_name(dev), data) != NULL;
37 }
38 
gnss_cmdline_init(void)39 static int __init gnss_cmdline_init(void)
40 {
41 	struct device *serial_dev, *port_dev, *serdev_dev;
42 	char *driver_name, *port_name, *serdev_name;
43 	char *serdev_dup, *serdev_dup_sep;
44 	struct gnss_serial *gserial;
45 	int err = -ENODEV;
46 
47 	/* User did not set the serdev module parameter */
48 	if (!serdev)
49 		return 0;
50 
51 	if (type < 0 || type >= GNSS_TYPE_COUNT) {
52 		gnss_cmdline_err("invalid gnss type '%d'\n", type);
53 		return -EINVAL;
54 	}
55 
56 	serdev_dup = serdev_dup_sep = kstrdup(serdev, GFP_KERNEL);
57 	if (!serdev_dup)
58 		return -ENOMEM;
59 
60 	driver_name = strsep(&serdev_dup_sep, "/");
61 	if (!driver_name) {
62 		gnss_cmdline_err("driver name missing\n");
63 		goto err_free_serdev_dup;
64 	}
65 
66 	port_name = strsep(&serdev_dup_sep, "/");
67 	if (!port_name) {
68 		gnss_cmdline_err("port name missing\n");
69 		goto err_free_serdev_dup;
70 	}
71 
72 	serdev_name = strsep(&serdev_dup_sep, "/");
73 	if (!serdev_name) {
74 		gnss_cmdline_err("serdev name missing\n");
75 		goto err_free_serdev_dup;
76 	}
77 
78 	/* Find the driver device instance (e.g. serial8250) */
79 	serial_dev = bus_find_device_by_name(&platform_bus_type,
80 					     NULL, driver_name);
81 	if (!serial_dev) {
82 		gnss_cmdline_err("no device '%s'\n", driver_name);
83 		goto err_free_serdev_dup;
84 	}
85 
86 	/* Find the port device instance (e.g. serial0) */
87 	port_dev = device_find_child(serial_dev, port_name, name_match);
88 	if (!port_dev) {
89 		gnss_cmdline_err("no port '%s'\n", port_name);
90 		goto err_free_serdev_dup;
91 	}
92 
93 	/* Find the serdev device instance (e.g. serial0-0) */
94 	serdev_dev = device_find_child(port_dev, serdev_name, name_match);
95 	if (!serdev_dev) {
96 		gnss_cmdline_err("no serdev '%s'\n", serdev_name);
97 		goto err_free_serdev_dup;
98 	}
99 
100 	gserial = gnss_serial_allocate(to_serdev_device(serdev_dev), 0);
101 	if (IS_ERR(gserial)) {
102 		err = PTR_ERR(gserial);
103 		goto err_free_serdev_dup;
104 	}
105 
106 	gserial->gdev->type = type;
107 
108 	err = gnss_serial_register(gserial);
109 	if (err) {
110 		gnss_serial_free(gserial);
111 		goto err_free_serdev_dup;
112 	}
113 
114 	serdev_device = to_serdev_device(serdev_dev);
115 	err = 0;
116 err_free_serdev_dup:
117 	kfree(serdev_dup);
118 	return err;
119 }
120 
gnss_cmdline_exit(void)121 static void __exit gnss_cmdline_exit(void)
122 {
123 	struct gnss_serial *gserial;
124 
125 	if (!serdev_device)
126 		return;
127 
128 	gserial = serdev_device_get_drvdata(serdev_device);
129 
130 	gnss_serial_deregister(gserial);
131 	gnss_serial_free(gserial);
132 }
133 
134 module_init(gnss_cmdline_init);
135 module_exit(gnss_cmdline_exit);
136 
137 MODULE_AUTHOR("Alistair Delva <adelva@google.com>");
138 MODULE_DESCRIPTION("GNSS command line driver");
139 MODULE_LICENSE("GPL v2");
140