usb.c (4595B)
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 19static const struct usb_device_id gnss_usb_id_table[] = { 20 { USB_DEVICE(0x1199, 0xb000) }, /* Sierra Wireless XM1210 */ 21 { } 22}; 23MODULE_DEVICE_TABLE(usb, gnss_usb_id_table); 24 25struct 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 33static 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); 64resubmit: 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 70static 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 85static 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 92static 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 112static 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 118static 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 180err_free_buf: 181 kfree(buf); 182err_free_urb: 183 usb_free_urb(urb); 184err_put_gdev: 185 gnss_put_device(gdev); 186err_free_gusb: 187 kfree(gusb); 188 189 return ret; 190} 191 192static 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 204static 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}; 210module_usb_driver(gnss_usb_driver); 211 212MODULE_AUTHOR("Johan Hovold <johan@kernel.org>"); 213MODULE_DESCRIPTION("Generic USB GNSS receiver driver"); 214MODULE_LICENSE("GPL v2");