xref: /linux/drivers/gnss/usb.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Generic USB GNSS receiver driver
4  *
5  * Copyright (C) 2021 Johan Hovold <johan@kernel.org>
6  */
7 
8 #include <linux/errno.h>
9 #include <linux/gnss.h>
10 #include <linux/init.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/slab.h>
14 #include <linux/usb.h>
15 
16 #define GNSS_USB_READ_BUF_LEN	512
17 #define GNSS_USB_WRITE_TIMEOUT	1000
18 
19 static const struct usb_device_id gnss_usb_id_table[] = {
20 	{ USB_DEVICE(0x1199, 0xb000) },		/* Sierra Wireless XM1210 */
21 	{ }
22 };
23 MODULE_DEVICE_TABLE(usb, gnss_usb_id_table);
24 
25 struct gnss_usb {
26 	struct usb_device *udev;
27 	struct usb_interface *intf;
28 	struct gnss_device *gdev;
29 	struct urb *read_urb;
30 	unsigned int write_pipe;
31 };
32 
33 static void gnss_usb_rx_complete(struct urb *urb)
34 {
35 	struct gnss_usb *gusb = urb->context;
36 	struct gnss_device *gdev = gusb->gdev;
37 	int status = urb->status;
38 	int len;
39 	int ret;
40 
41 	switch (status) {
42 	case 0:
43 		break;
44 	case -ENOENT:
45 	case -ECONNRESET:
46 	case -ESHUTDOWN:
47 		dev_dbg(&gdev->dev, "urb stopped: %d\n", status);
48 		return;
49 	case -EPIPE:
50 		dev_err(&gdev->dev, "urb stopped: %d\n", status);
51 		return;
52 	default:
53 		dev_dbg(&gdev->dev, "nonzero urb status: %d\n", status);
54 		goto resubmit;
55 	}
56 
57 	len = urb->actual_length;
58 	if (len == 0)
59 		goto resubmit;
60 
61 	ret = gnss_insert_raw(gdev, urb->transfer_buffer, len);
62 	if (ret < len)
63 		dev_dbg(&gdev->dev, "dropped %d bytes\n", len - ret);
64 resubmit:
65 	ret = usb_submit_urb(urb, GFP_ATOMIC);
66 	if (ret && ret != -EPERM && ret != -ENODEV)
67 		dev_err(&gdev->dev, "failed to resubmit urb: %d\n", ret);
68 }
69 
70 static int gnss_usb_open(struct gnss_device *gdev)
71 {
72 	struct gnss_usb *gusb = gnss_get_drvdata(gdev);
73 	int ret;
74 
75 	ret = usb_submit_urb(gusb->read_urb, GFP_KERNEL);
76 	if (ret) {
77 		if (ret != -EPERM && ret != -ENODEV)
78 			dev_err(&gdev->dev, "failed to submit urb: %d\n", ret);
79 		return ret;
80 	}
81 
82 	return 0;
83 }
84 
85 static void gnss_usb_close(struct gnss_device *gdev)
86 {
87 	struct gnss_usb *gusb = gnss_get_drvdata(gdev);
88 
89 	usb_kill_urb(gusb->read_urb);
90 }
91 
92 static int gnss_usb_write_raw(struct gnss_device *gdev,
93 		const unsigned char *buf, size_t count)
94 {
95 	struct gnss_usb *gusb = gnss_get_drvdata(gdev);
96 	void *tbuf;
97 	int ret;
98 
99 	tbuf = kmemdup(buf, count, GFP_KERNEL);
100 	if (!tbuf)
101 		return -ENOMEM;
102 
103 	ret = usb_bulk_msg(gusb->udev, gusb->write_pipe, tbuf, count, NULL,
104 			GNSS_USB_WRITE_TIMEOUT);
105 	kfree(tbuf);
106 	if (ret)
107 		return ret;
108 
109 	return count;
110 }
111 
112 static const struct gnss_operations gnss_usb_gnss_ops = {
113 	.open		= gnss_usb_open,
114 	.close		= gnss_usb_close,
115 	.write_raw	= gnss_usb_write_raw,
116 };
117 
118 static int gnss_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
119 {
120 	struct usb_device *udev = interface_to_usbdev(intf);
121 	struct usb_endpoint_descriptor *in, *out;
122 	struct gnss_device *gdev;
123 	struct gnss_usb *gusb;
124 	struct urb *urb;
125 	size_t buf_len;
126 	void *buf;
127 	int ret;
128 
129 	ret = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL,
130 			NULL);
131 	if (ret)
132 		return ret;
133 
134 	gusb = kzalloc(sizeof(*gusb), GFP_KERNEL);
135 	if (!gusb)
136 		return -ENOMEM;
137 
138 	gdev = gnss_allocate_device(&intf->dev);
139 	if (!gdev) {
140 		ret = -ENOMEM;
141 		goto err_free_gusb;
142 	}
143 
144 	gdev->ops = &gnss_usb_gnss_ops;
145 	gdev->type = GNSS_TYPE_NMEA;
146 	gnss_set_drvdata(gdev, gusb);
147 
148 	urb = usb_alloc_urb(0, GFP_KERNEL);
149 	if (!urb) {
150 		ret = -ENOMEM;
151 		goto err_put_gdev;
152 	}
153 
154 	buf_len = max(usb_endpoint_maxp(in), GNSS_USB_READ_BUF_LEN);
155 
156 	buf = kzalloc(buf_len, GFP_KERNEL);
157 	if (!buf) {
158 		ret = -ENOMEM;
159 		goto err_free_urb;
160 	}
161 
162 	usb_fill_bulk_urb(urb, udev,
163 			usb_rcvbulkpipe(udev, usb_endpoint_num(in)),
164 			buf, buf_len, gnss_usb_rx_complete, gusb);
165 
166 	gusb->intf = intf;
167 	gusb->udev = udev;
168 	gusb->gdev = gdev;
169 	gusb->read_urb = urb;
170 	gusb->write_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(out));
171 
172 	ret = gnss_register_device(gdev);
173 	if (ret)
174 		goto err_free_buf;
175 
176 	usb_set_intfdata(intf, gusb);
177 
178 	return 0;
179 
180 err_free_buf:
181 	kfree(buf);
182 err_free_urb:
183 	usb_free_urb(urb);
184 err_put_gdev:
185 	gnss_put_device(gdev);
186 err_free_gusb:
187 	kfree(gusb);
188 
189 	return ret;
190 }
191 
192 static void gnss_usb_disconnect(struct usb_interface *intf)
193 {
194 	struct gnss_usb *gusb = usb_get_intfdata(intf);
195 
196 	gnss_deregister_device(gusb->gdev);
197 
198 	kfree(gusb->read_urb->transfer_buffer);
199 	usb_free_urb(gusb->read_urb);
200 	gnss_put_device(gusb->gdev);
201 	kfree(gusb);
202 }
203 
204 static struct usb_driver gnss_usb_driver = {
205 	.name		= "gnss-usb",
206 	.probe		= gnss_usb_probe,
207 	.disconnect	= gnss_usb_disconnect,
208 	.id_table	= gnss_usb_id_table,
209 };
210 module_usb_driver(gnss_usb_driver);
211 
212 MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
213 MODULE_DESCRIPTION("Generic USB GNSS receiver driver");
214 MODULE_LICENSE("GPL v2");
215