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

usblcd.c (11423B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*****************************************************************************
      3 *                          USBLCD Kernel Driver                             *
      4 *                            Version 1.05                                   *
      5 *             (C) 2005 Georges Toth <g.toth@e-biz.lu>                       *
      6 *                                                                           *
      7 *     This file is licensed under the GPL. See COPYING in the package.      *
      8 * Based on usb-skeleton.c 2.0 by Greg Kroah-Hartman (greg@kroah.com)        *
      9 *                                                                           *
     10 *                                                                           *
     11 * 28.02.05 Complete rewrite of the original usblcd.c driver,                *
     12 *          based on usb_skeleton.c.                                         *
     13 *          This new driver allows more than one USB-LCD to be connected     *
     14 *          and controlled, at once                                          *
     15 *****************************************************************************/
     16#include <linux/module.h>
     17#include <linux/kernel.h>
     18#include <linux/slab.h>
     19#include <linux/errno.h>
     20#include <linux/mutex.h>
     21#include <linux/rwsem.h>
     22#include <linux/uaccess.h>
     23#include <linux/usb.h>
     24
     25#define DRIVER_VERSION "USBLCD Driver Version 1.05"
     26
     27#define USBLCD_MINOR		144
     28
     29#define IOCTL_GET_HARD_VERSION	1
     30#define IOCTL_GET_DRV_VERSION	2
     31
     32
     33static const struct usb_device_id id_table[] = {
     34	{ .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
     35	{ },
     36};
     37MODULE_DEVICE_TABLE(usb, id_table);
     38
     39struct usb_lcd {
     40	struct usb_device	*udev;			/* init: probe_lcd */
     41	struct usb_interface	*interface;		/* the interface for
     42							   this device */
     43	unsigned char		*bulk_in_buffer;	/* the buffer to receive
     44							   data */
     45	size_t			bulk_in_size;		/* the size of the
     46							   receive buffer */
     47	__u8			bulk_in_endpointAddr;	/* the address of the
     48							   bulk in endpoint */
     49	__u8			bulk_out_endpointAddr;	/* the address of the
     50							   bulk out endpoint */
     51	struct kref		kref;
     52	struct semaphore	limit_sem;		/* to stop writes at
     53							   full throttle from
     54							   using up all RAM */
     55	struct usb_anchor	submitted;		/* URBs to wait for
     56							   before suspend */
     57	struct rw_semaphore	io_rwsem;
     58	unsigned long		disconnected:1;
     59};
     60#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
     61
     62#define USB_LCD_CONCURRENT_WRITES	5
     63
     64static struct usb_driver lcd_driver;
     65
     66
     67static void lcd_delete(struct kref *kref)
     68{
     69	struct usb_lcd *dev = to_lcd_dev(kref);
     70
     71	usb_put_dev(dev->udev);
     72	kfree(dev->bulk_in_buffer);
     73	kfree(dev);
     74}
     75
     76
     77static int lcd_open(struct inode *inode, struct file *file)
     78{
     79	struct usb_lcd *dev;
     80	struct usb_interface *interface;
     81	int subminor, r;
     82
     83	subminor = iminor(inode);
     84
     85	interface = usb_find_interface(&lcd_driver, subminor);
     86	if (!interface) {
     87		pr_err("USBLCD: %s - error, can't find device for minor %d\n",
     88		       __func__, subminor);
     89		return -ENODEV;
     90	}
     91
     92	dev = usb_get_intfdata(interface);
     93
     94	/* increment our usage count for the device */
     95	kref_get(&dev->kref);
     96
     97	/* grab a power reference */
     98	r = usb_autopm_get_interface(interface);
     99	if (r < 0) {
    100		kref_put(&dev->kref, lcd_delete);
    101		return r;
    102	}
    103
    104	/* save our object in the file's private structure */
    105	file->private_data = dev;
    106
    107	return 0;
    108}
    109
    110static int lcd_release(struct inode *inode, struct file *file)
    111{
    112	struct usb_lcd *dev;
    113
    114	dev = file->private_data;
    115	if (dev == NULL)
    116		return -ENODEV;
    117
    118	/* decrement the count on our device */
    119	usb_autopm_put_interface(dev->interface);
    120	kref_put(&dev->kref, lcd_delete);
    121	return 0;
    122}
    123
    124static ssize_t lcd_read(struct file *file, char __user * buffer,
    125			size_t count, loff_t *ppos)
    126{
    127	struct usb_lcd *dev;
    128	int retval = 0;
    129	int bytes_read;
    130
    131	dev = file->private_data;
    132
    133	down_read(&dev->io_rwsem);
    134
    135	if (dev->disconnected) {
    136		retval = -ENODEV;
    137		goto out_up_io;
    138	}
    139
    140	/* do a blocking bulk read to get data from the device */
    141	retval = usb_bulk_msg(dev->udev,
    142			      usb_rcvbulkpipe(dev->udev,
    143					      dev->bulk_in_endpointAddr),
    144			      dev->bulk_in_buffer,
    145			      min(dev->bulk_in_size, count),
    146			      &bytes_read, 10000);
    147
    148	/* if the read was successful, copy the data to userspace */
    149	if (!retval) {
    150		if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))
    151			retval = -EFAULT;
    152		else
    153			retval = bytes_read;
    154	}
    155
    156out_up_io:
    157	up_read(&dev->io_rwsem);
    158
    159	return retval;
    160}
    161
    162static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    163{
    164	struct usb_lcd *dev;
    165	u16 bcdDevice;
    166	char buf[30];
    167
    168	dev = file->private_data;
    169	if (dev == NULL)
    170		return -ENODEV;
    171
    172	switch (cmd) {
    173	case IOCTL_GET_HARD_VERSION:
    174		bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
    175		sprintf(buf, "%1d%1d.%1d%1d",
    176			(bcdDevice & 0xF000)>>12,
    177			(bcdDevice & 0xF00)>>8,
    178			(bcdDevice & 0xF0)>>4,
    179			(bcdDevice & 0xF));
    180		if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
    181			return -EFAULT;
    182		break;
    183	case IOCTL_GET_DRV_VERSION:
    184		sprintf(buf, DRIVER_VERSION);
    185		if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
    186			return -EFAULT;
    187		break;
    188	default:
    189		return -ENOTTY;
    190	}
    191
    192	return 0;
    193}
    194
    195static void lcd_write_bulk_callback(struct urb *urb)
    196{
    197	struct usb_lcd *dev;
    198	int status = urb->status;
    199
    200	dev = urb->context;
    201
    202	/* sync/async unlink faults aren't errors */
    203	if (status &&
    204	    !(status == -ENOENT ||
    205	      status == -ECONNRESET ||
    206	      status == -ESHUTDOWN)) {
    207		dev_dbg(&dev->interface->dev,
    208			"nonzero write bulk status received: %d\n", status);
    209	}
    210
    211	/* free up our allocated buffer */
    212	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
    213			  urb->transfer_buffer, urb->transfer_dma);
    214	up(&dev->limit_sem);
    215}
    216
    217static ssize_t lcd_write(struct file *file, const char __user * user_buffer,
    218			 size_t count, loff_t *ppos)
    219{
    220	struct usb_lcd *dev;
    221	int retval = 0, r;
    222	struct urb *urb = NULL;
    223	char *buf = NULL;
    224
    225	dev = file->private_data;
    226
    227	/* verify that we actually have some data to write */
    228	if (count == 0)
    229		goto exit;
    230
    231	r = down_interruptible(&dev->limit_sem);
    232	if (r < 0)
    233		return -EINTR;
    234
    235	down_read(&dev->io_rwsem);
    236
    237	if (dev->disconnected) {
    238		retval = -ENODEV;
    239		goto err_up_io;
    240	}
    241
    242	/* create a urb, and a buffer for it, and copy the data to the urb */
    243	urb = usb_alloc_urb(0, GFP_KERNEL);
    244	if (!urb) {
    245		retval = -ENOMEM;
    246		goto err_up_io;
    247	}
    248
    249	buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL,
    250				 &urb->transfer_dma);
    251	if (!buf) {
    252		retval = -ENOMEM;
    253		goto error;
    254	}
    255
    256	if (copy_from_user(buf, user_buffer, count)) {
    257		retval = -EFAULT;
    258		goto error;
    259	}
    260
    261	/* initialize the urb properly */
    262	usb_fill_bulk_urb(urb, dev->udev,
    263			  usb_sndbulkpipe(dev->udev,
    264			  dev->bulk_out_endpointAddr),
    265			  buf, count, lcd_write_bulk_callback, dev);
    266	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    267
    268	usb_anchor_urb(urb, &dev->submitted);
    269
    270	/* send the data out the bulk port */
    271	retval = usb_submit_urb(urb, GFP_KERNEL);
    272	if (retval) {
    273		dev_err(&dev->udev->dev,
    274			"%s - failed submitting write urb, error %d\n",
    275			__func__, retval);
    276		goto error_unanchor;
    277	}
    278
    279	/* release our reference to this urb,
    280	   the USB core will eventually free it entirely */
    281	usb_free_urb(urb);
    282
    283	up_read(&dev->io_rwsem);
    284exit:
    285	return count;
    286error_unanchor:
    287	usb_unanchor_urb(urb);
    288error:
    289	usb_free_coherent(dev->udev, count, buf, urb->transfer_dma);
    290	usb_free_urb(urb);
    291err_up_io:
    292	up_read(&dev->io_rwsem);
    293	up(&dev->limit_sem);
    294	return retval;
    295}
    296
    297static const struct file_operations lcd_fops = {
    298	.owner =        THIS_MODULE,
    299	.read =         lcd_read,
    300	.write =        lcd_write,
    301	.open =         lcd_open,
    302	.unlocked_ioctl = lcd_ioctl,
    303	.release =      lcd_release,
    304	.llseek =	 noop_llseek,
    305};
    306
    307/*
    308 * usb class driver info in order to get a minor number from the usb core,
    309 * and to have the device registered with the driver core
    310 */
    311static struct usb_class_driver lcd_class = {
    312	.name =         "lcd%d",
    313	.fops =         &lcd_fops,
    314	.minor_base =   USBLCD_MINOR,
    315};
    316
    317static int lcd_probe(struct usb_interface *interface,
    318		     const struct usb_device_id *id)
    319{
    320	struct usb_lcd *dev = NULL;
    321	struct usb_endpoint_descriptor *bulk_in, *bulk_out;
    322	int i;
    323	int retval;
    324
    325	/* allocate memory for our device state and initialize it */
    326	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    327	if (!dev)
    328		return -ENOMEM;
    329
    330	kref_init(&dev->kref);
    331	sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);
    332	init_rwsem(&dev->io_rwsem);
    333	init_usb_anchor(&dev->submitted);
    334
    335	dev->udev = usb_get_dev(interface_to_usbdev(interface));
    336	dev->interface = interface;
    337
    338	if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) {
    339		dev_warn(&interface->dev, "USBLCD model not supported.\n");
    340		retval = -ENODEV;
    341		goto error;
    342	}
    343
    344	/* set up the endpoint information */
    345	/* use only the first bulk-in and bulk-out endpoints */
    346	retval = usb_find_common_endpoints(interface->cur_altsetting,
    347			&bulk_in, &bulk_out, NULL, NULL);
    348	if (retval) {
    349		dev_err(&interface->dev,
    350			"Could not find both bulk-in and bulk-out endpoints\n");
    351		goto error;
    352	}
    353
    354	dev->bulk_in_size = usb_endpoint_maxp(bulk_in);
    355	dev->bulk_in_endpointAddr = bulk_in->bEndpointAddress;
    356	dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
    357	if (!dev->bulk_in_buffer) {
    358		retval = -ENOMEM;
    359		goto error;
    360	}
    361
    362	dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress;
    363
    364	/* save our data pointer in this interface device */
    365	usb_set_intfdata(interface, dev);
    366
    367	/* we can register the device now, as it is ready */
    368	retval = usb_register_dev(interface, &lcd_class);
    369	if (retval) {
    370		/* something prevented us from registering this driver */
    371		dev_err(&interface->dev,
    372			"Not able to get a minor for this device.\n");
    373		goto error;
    374	}
    375
    376	i = le16_to_cpu(dev->udev->descriptor.bcdDevice);
    377
    378	dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found "
    379		 "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8,
    380		 (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum);
    381
    382	/* let the user know what node this device is now attached to */
    383	dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n",
    384		 interface->minor);
    385	return 0;
    386
    387error:
    388	kref_put(&dev->kref, lcd_delete);
    389	return retval;
    390}
    391
    392static void lcd_draw_down(struct usb_lcd *dev)
    393{
    394	int time;
    395
    396	time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
    397	if (!time)
    398		usb_kill_anchored_urbs(&dev->submitted);
    399}
    400
    401static int lcd_suspend(struct usb_interface *intf, pm_message_t message)
    402{
    403	struct usb_lcd *dev = usb_get_intfdata(intf);
    404
    405	if (!dev)
    406		return 0;
    407	lcd_draw_down(dev);
    408	return 0;
    409}
    410
    411static int lcd_resume(struct usb_interface *intf)
    412{
    413	return 0;
    414}
    415
    416static void lcd_disconnect(struct usb_interface *interface)
    417{
    418	struct usb_lcd *dev = usb_get_intfdata(interface);
    419	int minor = interface->minor;
    420
    421	/* give back our minor */
    422	usb_deregister_dev(interface, &lcd_class);
    423
    424	down_write(&dev->io_rwsem);
    425	dev->disconnected = 1;
    426	up_write(&dev->io_rwsem);
    427
    428	usb_kill_anchored_urbs(&dev->submitted);
    429
    430	/* decrement our usage count */
    431	kref_put(&dev->kref, lcd_delete);
    432
    433	dev_info(&interface->dev, "USB LCD #%d now disconnected\n", minor);
    434}
    435
    436static struct usb_driver lcd_driver = {
    437	.name =		"usblcd",
    438	.probe =	lcd_probe,
    439	.disconnect =	lcd_disconnect,
    440	.suspend =	lcd_suspend,
    441	.resume =	lcd_resume,
    442	.id_table =	id_table,
    443	.supports_autosuspend = 1,
    444};
    445
    446module_usb_driver(lcd_driver);
    447
    448MODULE_AUTHOR("Georges Toth <g.toth@e-biz.lu>");
    449MODULE_DESCRIPTION(DRIVER_VERSION);
    450MODULE_LICENSE("GPL");