1 // SPDX-License-Identifier: GPL-2.0
3 * Generic USB GNSS receiver driver
5 * Copyright (C) 2021 Johan Hovold <johan@kernel.org>
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 */
23 MODULE_DEVICE_TABLE(usb
, gnss_usb_id_table
);
26 struct usb_device
*udev
;
27 struct usb_interface
*intf
;
28 struct gnss_device
*gdev
;
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
;
47 dev_dbg(&gdev
->dev
, "urb stopped: %d\n", status
);
50 dev_err(&gdev
->dev
, "urb stopped: %d\n", status
);
53 dev_dbg(&gdev
->dev
, "nonzero urb status: %d\n", status
);
57 len
= urb
->actual_length
;
61 ret
= gnss_insert_raw(gdev
, urb
->transfer_buffer
, len
);
63 dev_dbg(&gdev
->dev
, "dropped %d bytes\n", len
- ret
);
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
);
75 ret
= usb_submit_urb(gusb
->read_urb
, GFP_KERNEL
);
77 if (ret
!= -EPERM
&& ret
!= -ENODEV
)
78 dev_err(&gdev
->dev
, "failed to submit urb: %d\n", ret
);
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
);
99 tbuf
= kmemdup(buf
, count
, GFP_KERNEL
);
103 ret
= usb_bulk_msg(gusb
->udev
, gusb
->write_pipe
, tbuf
, count
, NULL
,
104 GNSS_USB_WRITE_TIMEOUT
);
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
;
129 ret
= usb_find_common_endpoints(intf
->cur_altsetting
, &in
, &out
, NULL
,
134 gusb
= kzalloc(sizeof(*gusb
), GFP_KERNEL
);
138 gdev
= gnss_allocate_device(&intf
->dev
);
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
);
154 buf_len
= max(usb_endpoint_maxp(in
), GNSS_USB_READ_BUF_LEN
);
156 buf
= kzalloc(buf_len
, GFP_KERNEL
);
162 usb_fill_bulk_urb(urb
, udev
,
163 usb_rcvbulkpipe(udev
, usb_endpoint_num(in
)),
164 buf
, buf_len
, gnss_usb_rx_complete
, gusb
);
169 gusb
->read_urb
= urb
;
170 gusb
->write_pipe
= usb_sndbulkpipe(udev
, usb_endpoint_num(out
));
172 ret
= gnss_register_device(gdev
);
176 usb_set_intfdata(intf
, gusb
);
185 gnss_put_device(gdev
);
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
);
204 static struct usb_driver gnss_usb_driver
= {
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");