Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / gnss / usb.c
blob028ce56b20ea182a99e653b1625c749ecff66107
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Generic USB GNSS receiver driver
5 * Copyright (C) 2021 Johan Hovold <johan@kernel.org>
6 */
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>
16 #define GNSS_USB_READ_BUF_LEN 512
17 #define GNSS_USB_WRITE_TIMEOUT 1000
19 static const struct usb_device_id gnss_usb_id_table[] = {
20 { USB_DEVICE(0x1199, 0xb000) }, /* Sierra Wireless XM1210 */
21 { }
23 MODULE_DEVICE_TABLE(usb, gnss_usb_id_table);
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;
33 static void gnss_usb_rx_complete(struct urb *urb)
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;
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;
57 len = urb->actual_length;
58 if (len == 0)
59 goto resubmit;
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);
70 static int gnss_usb_open(struct gnss_device *gdev)
72 struct gnss_usb *gusb = gnss_get_drvdata(gdev);
73 int ret;
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;
82 return 0;
85 static void gnss_usb_close(struct gnss_device *gdev)
87 struct gnss_usb *gusb = gnss_get_drvdata(gdev);
89 usb_kill_urb(gusb->read_urb);
92 static int gnss_usb_write_raw(struct gnss_device *gdev,
93 const unsigned char *buf, size_t count)
95 struct gnss_usb *gusb = gnss_get_drvdata(gdev);
96 void *tbuf;
97 int ret;
99 tbuf = kmemdup(buf, count, GFP_KERNEL);
100 if (!tbuf)
101 return -ENOMEM;
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;
109 return count;
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,
118 static int gnss_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
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;
129 ret = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL,
130 NULL);
131 if (ret)
132 return ret;
134 gusb = kzalloc(sizeof(*gusb), GFP_KERNEL);
135 if (!gusb)
136 return -ENOMEM;
138 gdev = gnss_allocate_device(&intf->dev);
139 if (!gdev) {
140 ret = -ENOMEM;
141 goto err_free_gusb;
144 gdev->ops = &gnss_usb_gnss_ops;
145 gdev->type = GNSS_TYPE_NMEA;
146 gnss_set_drvdata(gdev, gusb);
148 urb = usb_alloc_urb(0, GFP_KERNEL);
149 if (!urb) {
150 ret = -ENOMEM;
151 goto err_put_gdev;
154 buf_len = max(usb_endpoint_maxp(in), GNSS_USB_READ_BUF_LEN);
156 buf = kzalloc(buf_len, GFP_KERNEL);
157 if (!buf) {
158 ret = -ENOMEM;
159 goto err_free_urb;
162 usb_fill_bulk_urb(urb, udev,
163 usb_rcvbulkpipe(udev, usb_endpoint_num(in)),
164 buf, buf_len, gnss_usb_rx_complete, gusb);
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));
172 ret = gnss_register_device(gdev);
173 if (ret)
174 goto err_free_buf;
176 usb_set_intfdata(intf, gusb);
178 return 0;
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);
189 return ret;
192 static void gnss_usb_disconnect(struct usb_interface *intf)
194 struct gnss_usb *gusb = usb_get_intfdata(intf);
196 gnss_deregister_device(gusb->gdev);
198 kfree(gusb->read_urb->transfer_buffer);
199 usb_free_urb(gusb->read_urb);
200 gnss_put_device(gusb->gdev);
201 kfree(gusb);
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,
210 module_usb_driver(gnss_usb_driver);
212 MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
213 MODULE_DESCRIPTION("Generic USB GNSS receiver driver");
214 MODULE_LICENSE("GPL v2");