cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");