Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Generic USB GNSS receiver driver
0004  *
0005  * Copyright (C) 2021 Johan Hovold <johan@kernel.org>
0006  */
0007 
0008 #include <linux/errno.h>
0009 #include <linux/gnss.h>
0010 #include <linux/init.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/slab.h>
0014 #include <linux/usb.h>
0015 
0016 #define GNSS_USB_READ_BUF_LEN   512
0017 #define GNSS_USB_WRITE_TIMEOUT  1000
0018 
0019 static const struct usb_device_id gnss_usb_id_table[] = {
0020     { USB_DEVICE(0x1199, 0xb000) },     /* Sierra Wireless XM1210 */
0021     { }
0022 };
0023 MODULE_DEVICE_TABLE(usb, gnss_usb_id_table);
0024 
0025 struct gnss_usb {
0026     struct usb_device *udev;
0027     struct usb_interface *intf;
0028     struct gnss_device *gdev;
0029     struct urb *read_urb;
0030     unsigned int write_pipe;
0031 };
0032 
0033 static void gnss_usb_rx_complete(struct urb *urb)
0034 {
0035     struct gnss_usb *gusb = urb->context;
0036     struct gnss_device *gdev = gusb->gdev;
0037     int status = urb->status;
0038     int len;
0039     int ret;
0040 
0041     switch (status) {
0042     case 0:
0043         break;
0044     case -ENOENT:
0045     case -ECONNRESET:
0046     case -ESHUTDOWN:
0047         dev_dbg(&gdev->dev, "urb stopped: %d\n", status);
0048         return;
0049     case -EPIPE:
0050         dev_err(&gdev->dev, "urb stopped: %d\n", status);
0051         return;
0052     default:
0053         dev_dbg(&gdev->dev, "nonzero urb status: %d\n", status);
0054         goto resubmit;
0055     }
0056 
0057     len = urb->actual_length;
0058     if (len == 0)
0059         goto resubmit;
0060 
0061     ret = gnss_insert_raw(gdev, urb->transfer_buffer, len);
0062     if (ret < len)
0063         dev_dbg(&gdev->dev, "dropped %d bytes\n", len - ret);
0064 resubmit:
0065     ret = usb_submit_urb(urb, GFP_ATOMIC);
0066     if (ret && ret != -EPERM && ret != -ENODEV)
0067         dev_err(&gdev->dev, "failed to resubmit urb: %d\n", ret);
0068 }
0069 
0070 static int gnss_usb_open(struct gnss_device *gdev)
0071 {
0072     struct gnss_usb *gusb = gnss_get_drvdata(gdev);
0073     int ret;
0074 
0075     ret = usb_submit_urb(gusb->read_urb, GFP_KERNEL);
0076     if (ret) {
0077         if (ret != -EPERM && ret != -ENODEV)
0078             dev_err(&gdev->dev, "failed to submit urb: %d\n", ret);
0079         return ret;
0080     }
0081 
0082     return 0;
0083 }
0084 
0085 static void gnss_usb_close(struct gnss_device *gdev)
0086 {
0087     struct gnss_usb *gusb = gnss_get_drvdata(gdev);
0088 
0089     usb_kill_urb(gusb->read_urb);
0090 }
0091 
0092 static int gnss_usb_write_raw(struct gnss_device *gdev,
0093         const unsigned char *buf, size_t count)
0094 {
0095     struct gnss_usb *gusb = gnss_get_drvdata(gdev);
0096     void *tbuf;
0097     int ret;
0098 
0099     tbuf = kmemdup(buf, count, GFP_KERNEL);
0100     if (!tbuf)
0101         return -ENOMEM;
0102 
0103     ret = usb_bulk_msg(gusb->udev, gusb->write_pipe, tbuf, count, NULL,
0104             GNSS_USB_WRITE_TIMEOUT);
0105     kfree(tbuf);
0106     if (ret)
0107         return ret;
0108 
0109     return count;
0110 }
0111 
0112 static const struct gnss_operations gnss_usb_gnss_ops = {
0113     .open       = gnss_usb_open,
0114     .close      = gnss_usb_close,
0115     .write_raw  = gnss_usb_write_raw,
0116 };
0117 
0118 static int gnss_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
0119 {
0120     struct usb_device *udev = interface_to_usbdev(intf);
0121     struct usb_endpoint_descriptor *in, *out;
0122     struct gnss_device *gdev;
0123     struct gnss_usb *gusb;
0124     struct urb *urb;
0125     size_t buf_len;
0126     void *buf;
0127     int ret;
0128 
0129     ret = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL,
0130             NULL);
0131     if (ret)
0132         return ret;
0133 
0134     gusb = kzalloc(sizeof(*gusb), GFP_KERNEL);
0135     if (!gusb)
0136         return -ENOMEM;
0137 
0138     gdev = gnss_allocate_device(&intf->dev);
0139     if (!gdev) {
0140         ret = -ENOMEM;
0141         goto err_free_gusb;
0142     }
0143 
0144     gdev->ops = &gnss_usb_gnss_ops;
0145     gdev->type = GNSS_TYPE_NMEA;
0146     gnss_set_drvdata(gdev, gusb);
0147 
0148     urb = usb_alloc_urb(0, GFP_KERNEL);
0149     if (!urb) {
0150         ret = -ENOMEM;
0151         goto err_put_gdev;
0152     }
0153 
0154     buf_len = max(usb_endpoint_maxp(in), GNSS_USB_READ_BUF_LEN);
0155 
0156     buf = kzalloc(buf_len, GFP_KERNEL);
0157     if (!buf) {
0158         ret = -ENOMEM;
0159         goto err_free_urb;
0160     }
0161 
0162     usb_fill_bulk_urb(urb, udev,
0163             usb_rcvbulkpipe(udev, usb_endpoint_num(in)),
0164             buf, buf_len, gnss_usb_rx_complete, gusb);
0165 
0166     gusb->intf = intf;
0167     gusb->udev = udev;
0168     gusb->gdev = gdev;
0169     gusb->read_urb = urb;
0170     gusb->write_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(out));
0171 
0172     ret = gnss_register_device(gdev);
0173     if (ret)
0174         goto err_free_buf;
0175 
0176     usb_set_intfdata(intf, gusb);
0177 
0178     return 0;
0179 
0180 err_free_buf:
0181     kfree(buf);
0182 err_free_urb:
0183     usb_free_urb(urb);
0184 err_put_gdev:
0185     gnss_put_device(gdev);
0186 err_free_gusb:
0187     kfree(gusb);
0188 
0189     return ret;
0190 }
0191 
0192 static void gnss_usb_disconnect(struct usb_interface *intf)
0193 {
0194     struct gnss_usb *gusb = usb_get_intfdata(intf);
0195 
0196     gnss_deregister_device(gusb->gdev);
0197 
0198     kfree(gusb->read_urb->transfer_buffer);
0199     usb_free_urb(gusb->read_urb);
0200     gnss_put_device(gusb->gdev);
0201     kfree(gusb);
0202 }
0203 
0204 static struct usb_driver gnss_usb_driver = {
0205     .name       = "gnss-usb",
0206     .probe      = gnss_usb_probe,
0207     .disconnect = gnss_usb_disconnect,
0208     .id_table   = gnss_usb_id_table,
0209 };
0210 module_usb_driver(gnss_usb_driver);
0211 
0212 MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
0213 MODULE_DESCRIPTION("Generic USB GNSS receiver driver");
0214 MODULE_LICENSE("GPL v2");