162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/***************************************************************************** 362306a36Sopenharmony_ci * USBLCD Kernel Driver * 462306a36Sopenharmony_ci * Version 1.05 * 562306a36Sopenharmony_ci * (C) 2005 Georges Toth <g.toth@e-biz.lu> * 662306a36Sopenharmony_ci * * 762306a36Sopenharmony_ci * This file is licensed under the GPL. See COPYING in the package. * 862306a36Sopenharmony_ci * Based on usb-skeleton.c 2.0 by Greg Kroah-Hartman (greg@kroah.com) * 962306a36Sopenharmony_ci * * 1062306a36Sopenharmony_ci * * 1162306a36Sopenharmony_ci * 28.02.05 Complete rewrite of the original usblcd.c driver, * 1262306a36Sopenharmony_ci * based on usb_skeleton.c. * 1362306a36Sopenharmony_ci * This new driver allows more than one USB-LCD to be connected * 1462306a36Sopenharmony_ci * and controlled, at once * 1562306a36Sopenharmony_ci *****************************************************************************/ 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/errno.h> 2062306a36Sopenharmony_ci#include <linux/mutex.h> 2162306a36Sopenharmony_ci#include <linux/rwsem.h> 2262306a36Sopenharmony_ci#include <linux/uaccess.h> 2362306a36Sopenharmony_ci#include <linux/usb.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define DRIVER_VERSION "USBLCD Driver Version 1.05" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define USBLCD_MINOR 144 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define IOCTL_GET_HARD_VERSION 1 3062306a36Sopenharmony_ci#define IOCTL_GET_DRV_VERSION 2 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = { 3462306a36Sopenharmony_ci { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, }, 3562306a36Sopenharmony_ci { }, 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct usb_lcd { 4062306a36Sopenharmony_ci struct usb_device *udev; /* init: probe_lcd */ 4162306a36Sopenharmony_ci struct usb_interface *interface; /* the interface for 4262306a36Sopenharmony_ci this device */ 4362306a36Sopenharmony_ci unsigned char *bulk_in_buffer; /* the buffer to receive 4462306a36Sopenharmony_ci data */ 4562306a36Sopenharmony_ci size_t bulk_in_size; /* the size of the 4662306a36Sopenharmony_ci receive buffer */ 4762306a36Sopenharmony_ci __u8 bulk_in_endpointAddr; /* the address of the 4862306a36Sopenharmony_ci bulk in endpoint */ 4962306a36Sopenharmony_ci __u8 bulk_out_endpointAddr; /* the address of the 5062306a36Sopenharmony_ci bulk out endpoint */ 5162306a36Sopenharmony_ci struct kref kref; 5262306a36Sopenharmony_ci struct semaphore limit_sem; /* to stop writes at 5362306a36Sopenharmony_ci full throttle from 5462306a36Sopenharmony_ci using up all RAM */ 5562306a36Sopenharmony_ci struct usb_anchor submitted; /* URBs to wait for 5662306a36Sopenharmony_ci before suspend */ 5762306a36Sopenharmony_ci struct rw_semaphore io_rwsem; 5862306a36Sopenharmony_ci unsigned long disconnected:1; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define USB_LCD_CONCURRENT_WRITES 5 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct usb_driver lcd_driver; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void lcd_delete(struct kref *kref) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct usb_lcd *dev = to_lcd_dev(kref); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci usb_put_dev(dev->udev); 7262306a36Sopenharmony_ci kfree(dev->bulk_in_buffer); 7362306a36Sopenharmony_ci kfree(dev); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int lcd_open(struct inode *inode, struct file *file) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct usb_lcd *dev; 8062306a36Sopenharmony_ci struct usb_interface *interface; 8162306a36Sopenharmony_ci int subminor, r; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci subminor = iminor(inode); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci interface = usb_find_interface(&lcd_driver, subminor); 8662306a36Sopenharmony_ci if (!interface) { 8762306a36Sopenharmony_ci pr_err("USBLCD: %s - error, can't find device for minor %d\n", 8862306a36Sopenharmony_ci __func__, subminor); 8962306a36Sopenharmony_ci return -ENODEV; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci dev = usb_get_intfdata(interface); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* increment our usage count for the device */ 9562306a36Sopenharmony_ci kref_get(&dev->kref); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* grab a power reference */ 9862306a36Sopenharmony_ci r = usb_autopm_get_interface(interface); 9962306a36Sopenharmony_ci if (r < 0) { 10062306a36Sopenharmony_ci kref_put(&dev->kref, lcd_delete); 10162306a36Sopenharmony_ci return r; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* save our object in the file's private structure */ 10562306a36Sopenharmony_ci file->private_data = dev; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int lcd_release(struct inode *inode, struct file *file) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct usb_lcd *dev; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci dev = file->private_data; 11562306a36Sopenharmony_ci if (dev == NULL) 11662306a36Sopenharmony_ci return -ENODEV; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* decrement the count on our device */ 11962306a36Sopenharmony_ci usb_autopm_put_interface(dev->interface); 12062306a36Sopenharmony_ci kref_put(&dev->kref, lcd_delete); 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic ssize_t lcd_read(struct file *file, char __user * buffer, 12562306a36Sopenharmony_ci size_t count, loff_t *ppos) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct usb_lcd *dev; 12862306a36Sopenharmony_ci int retval = 0; 12962306a36Sopenharmony_ci int bytes_read; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci dev = file->private_data; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci down_read(&dev->io_rwsem); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (dev->disconnected) { 13662306a36Sopenharmony_ci retval = -ENODEV; 13762306a36Sopenharmony_ci goto out_up_io; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* do a blocking bulk read to get data from the device */ 14162306a36Sopenharmony_ci retval = usb_bulk_msg(dev->udev, 14262306a36Sopenharmony_ci usb_rcvbulkpipe(dev->udev, 14362306a36Sopenharmony_ci dev->bulk_in_endpointAddr), 14462306a36Sopenharmony_ci dev->bulk_in_buffer, 14562306a36Sopenharmony_ci min(dev->bulk_in_size, count), 14662306a36Sopenharmony_ci &bytes_read, 10000); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* if the read was successful, copy the data to userspace */ 14962306a36Sopenharmony_ci if (!retval) { 15062306a36Sopenharmony_ci if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read)) 15162306a36Sopenharmony_ci retval = -EFAULT; 15262306a36Sopenharmony_ci else 15362306a36Sopenharmony_ci retval = bytes_read; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciout_up_io: 15762306a36Sopenharmony_ci up_read(&dev->io_rwsem); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return retval; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct usb_lcd *dev; 16562306a36Sopenharmony_ci u16 bcdDevice; 16662306a36Sopenharmony_ci char buf[30]; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci dev = file->private_data; 16962306a36Sopenharmony_ci if (dev == NULL) 17062306a36Sopenharmony_ci return -ENODEV; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci switch (cmd) { 17362306a36Sopenharmony_ci case IOCTL_GET_HARD_VERSION: 17462306a36Sopenharmony_ci bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice); 17562306a36Sopenharmony_ci sprintf(buf, "%1d%1d.%1d%1d", 17662306a36Sopenharmony_ci (bcdDevice & 0xF000)>>12, 17762306a36Sopenharmony_ci (bcdDevice & 0xF00)>>8, 17862306a36Sopenharmony_ci (bcdDevice & 0xF0)>>4, 17962306a36Sopenharmony_ci (bcdDevice & 0xF)); 18062306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) 18162306a36Sopenharmony_ci return -EFAULT; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case IOCTL_GET_DRV_VERSION: 18462306a36Sopenharmony_ci sprintf(buf, DRIVER_VERSION); 18562306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) 18662306a36Sopenharmony_ci return -EFAULT; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci default: 18962306a36Sopenharmony_ci return -ENOTTY; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void lcd_write_bulk_callback(struct urb *urb) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct usb_lcd *dev; 19862306a36Sopenharmony_ci int status = urb->status; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci dev = urb->context; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* sync/async unlink faults aren't errors */ 20362306a36Sopenharmony_ci if (status && 20462306a36Sopenharmony_ci !(status == -ENOENT || 20562306a36Sopenharmony_ci status == -ECONNRESET || 20662306a36Sopenharmony_ci status == -ESHUTDOWN)) { 20762306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, 20862306a36Sopenharmony_ci "nonzero write bulk status received: %d\n", status); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* free up our allocated buffer */ 21262306a36Sopenharmony_ci usb_free_coherent(urb->dev, urb->transfer_buffer_length, 21362306a36Sopenharmony_ci urb->transfer_buffer, urb->transfer_dma); 21462306a36Sopenharmony_ci up(&dev->limit_sem); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic ssize_t lcd_write(struct file *file, const char __user * user_buffer, 21862306a36Sopenharmony_ci size_t count, loff_t *ppos) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct usb_lcd *dev; 22162306a36Sopenharmony_ci int retval = 0, r; 22262306a36Sopenharmony_ci struct urb *urb = NULL; 22362306a36Sopenharmony_ci char *buf = NULL; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci dev = file->private_data; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* verify that we actually have some data to write */ 22862306a36Sopenharmony_ci if (count == 0) 22962306a36Sopenharmony_ci goto exit; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci r = down_interruptible(&dev->limit_sem); 23262306a36Sopenharmony_ci if (r < 0) 23362306a36Sopenharmony_ci return -EINTR; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci down_read(&dev->io_rwsem); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (dev->disconnected) { 23862306a36Sopenharmony_ci retval = -ENODEV; 23962306a36Sopenharmony_ci goto err_up_io; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* create a urb, and a buffer for it, and copy the data to the urb */ 24362306a36Sopenharmony_ci urb = usb_alloc_urb(0, GFP_KERNEL); 24462306a36Sopenharmony_ci if (!urb) { 24562306a36Sopenharmony_ci retval = -ENOMEM; 24662306a36Sopenharmony_ci goto err_up_io; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, 25062306a36Sopenharmony_ci &urb->transfer_dma); 25162306a36Sopenharmony_ci if (!buf) { 25262306a36Sopenharmony_ci retval = -ENOMEM; 25362306a36Sopenharmony_ci goto error; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (copy_from_user(buf, user_buffer, count)) { 25762306a36Sopenharmony_ci retval = -EFAULT; 25862306a36Sopenharmony_ci goto error; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* initialize the urb properly */ 26262306a36Sopenharmony_ci usb_fill_bulk_urb(urb, dev->udev, 26362306a36Sopenharmony_ci usb_sndbulkpipe(dev->udev, 26462306a36Sopenharmony_ci dev->bulk_out_endpointAddr), 26562306a36Sopenharmony_ci buf, count, lcd_write_bulk_callback, dev); 26662306a36Sopenharmony_ci urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci usb_anchor_urb(urb, &dev->submitted); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* send the data out the bulk port */ 27162306a36Sopenharmony_ci retval = usb_submit_urb(urb, GFP_KERNEL); 27262306a36Sopenharmony_ci if (retval) { 27362306a36Sopenharmony_ci dev_err(&dev->udev->dev, 27462306a36Sopenharmony_ci "%s - failed submitting write urb, error %d\n", 27562306a36Sopenharmony_ci __func__, retval); 27662306a36Sopenharmony_ci goto error_unanchor; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* release our reference to this urb, 28062306a36Sopenharmony_ci the USB core will eventually free it entirely */ 28162306a36Sopenharmony_ci usb_free_urb(urb); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci up_read(&dev->io_rwsem); 28462306a36Sopenharmony_ciexit: 28562306a36Sopenharmony_ci return count; 28662306a36Sopenharmony_cierror_unanchor: 28762306a36Sopenharmony_ci usb_unanchor_urb(urb); 28862306a36Sopenharmony_cierror: 28962306a36Sopenharmony_ci usb_free_coherent(dev->udev, count, buf, urb->transfer_dma); 29062306a36Sopenharmony_ci usb_free_urb(urb); 29162306a36Sopenharmony_cierr_up_io: 29262306a36Sopenharmony_ci up_read(&dev->io_rwsem); 29362306a36Sopenharmony_ci up(&dev->limit_sem); 29462306a36Sopenharmony_ci return retval; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic const struct file_operations lcd_fops = { 29862306a36Sopenharmony_ci .owner = THIS_MODULE, 29962306a36Sopenharmony_ci .read = lcd_read, 30062306a36Sopenharmony_ci .write = lcd_write, 30162306a36Sopenharmony_ci .open = lcd_open, 30262306a36Sopenharmony_ci .unlocked_ioctl = lcd_ioctl, 30362306a36Sopenharmony_ci .release = lcd_release, 30462306a36Sopenharmony_ci .llseek = noop_llseek, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/* 30862306a36Sopenharmony_ci * usb class driver info in order to get a minor number from the usb core, 30962306a36Sopenharmony_ci * and to have the device registered with the driver core 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_cistatic struct usb_class_driver lcd_class = { 31262306a36Sopenharmony_ci .name = "lcd%d", 31362306a36Sopenharmony_ci .fops = &lcd_fops, 31462306a36Sopenharmony_ci .minor_base = USBLCD_MINOR, 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int lcd_probe(struct usb_interface *interface, 31862306a36Sopenharmony_ci const struct usb_device_id *id) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct usb_lcd *dev = NULL; 32162306a36Sopenharmony_ci struct usb_endpoint_descriptor *bulk_in, *bulk_out; 32262306a36Sopenharmony_ci int i; 32362306a36Sopenharmony_ci int retval; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* allocate memory for our device state and initialize it */ 32662306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 32762306a36Sopenharmony_ci if (!dev) 32862306a36Sopenharmony_ci return -ENOMEM; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci kref_init(&dev->kref); 33162306a36Sopenharmony_ci sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES); 33262306a36Sopenharmony_ci init_rwsem(&dev->io_rwsem); 33362306a36Sopenharmony_ci init_usb_anchor(&dev->submitted); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci dev->udev = usb_get_dev(interface_to_usbdev(interface)); 33662306a36Sopenharmony_ci dev->interface = interface; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) { 33962306a36Sopenharmony_ci dev_warn(&interface->dev, "USBLCD model not supported.\n"); 34062306a36Sopenharmony_ci retval = -ENODEV; 34162306a36Sopenharmony_ci goto error; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* set up the endpoint information */ 34562306a36Sopenharmony_ci /* use only the first bulk-in and bulk-out endpoints */ 34662306a36Sopenharmony_ci retval = usb_find_common_endpoints(interface->cur_altsetting, 34762306a36Sopenharmony_ci &bulk_in, &bulk_out, NULL, NULL); 34862306a36Sopenharmony_ci if (retval) { 34962306a36Sopenharmony_ci dev_err(&interface->dev, 35062306a36Sopenharmony_ci "Could not find both bulk-in and bulk-out endpoints\n"); 35162306a36Sopenharmony_ci goto error; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci dev->bulk_in_size = usb_endpoint_maxp(bulk_in); 35562306a36Sopenharmony_ci dev->bulk_in_endpointAddr = bulk_in->bEndpointAddress; 35662306a36Sopenharmony_ci dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL); 35762306a36Sopenharmony_ci if (!dev->bulk_in_buffer) { 35862306a36Sopenharmony_ci retval = -ENOMEM; 35962306a36Sopenharmony_ci goto error; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* save our data pointer in this interface device */ 36562306a36Sopenharmony_ci usb_set_intfdata(interface, dev); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* we can register the device now, as it is ready */ 36862306a36Sopenharmony_ci retval = usb_register_dev(interface, &lcd_class); 36962306a36Sopenharmony_ci if (retval) { 37062306a36Sopenharmony_ci /* something prevented us from registering this driver */ 37162306a36Sopenharmony_ci dev_err(&interface->dev, 37262306a36Sopenharmony_ci "Not able to get a minor for this device.\n"); 37362306a36Sopenharmony_ci goto error; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci i = le16_to_cpu(dev->udev->descriptor.bcdDevice); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found " 37962306a36Sopenharmony_ci "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8, 38062306a36Sopenharmony_ci (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* let the user know what node this device is now attached to */ 38362306a36Sopenharmony_ci dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n", 38462306a36Sopenharmony_ci interface->minor); 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cierror: 38862306a36Sopenharmony_ci kref_put(&dev->kref, lcd_delete); 38962306a36Sopenharmony_ci return retval; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void lcd_draw_down(struct usb_lcd *dev) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci int time; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); 39762306a36Sopenharmony_ci if (!time) 39862306a36Sopenharmony_ci usb_kill_anchored_urbs(&dev->submitted); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int lcd_suspend(struct usb_interface *intf, pm_message_t message) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct usb_lcd *dev = usb_get_intfdata(intf); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!dev) 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci lcd_draw_down(dev); 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int lcd_resume(struct usb_interface *intf) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic void lcd_disconnect(struct usb_interface *interface) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct usb_lcd *dev = usb_get_intfdata(interface); 41962306a36Sopenharmony_ci int minor = interface->minor; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* give back our minor */ 42262306a36Sopenharmony_ci usb_deregister_dev(interface, &lcd_class); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci down_write(&dev->io_rwsem); 42562306a36Sopenharmony_ci dev->disconnected = 1; 42662306a36Sopenharmony_ci up_write(&dev->io_rwsem); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci usb_kill_anchored_urbs(&dev->submitted); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* decrement our usage count */ 43162306a36Sopenharmony_ci kref_put(&dev->kref, lcd_delete); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci dev_info(&interface->dev, "USB LCD #%d now disconnected\n", minor); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic struct usb_driver lcd_driver = { 43762306a36Sopenharmony_ci .name = "usblcd", 43862306a36Sopenharmony_ci .probe = lcd_probe, 43962306a36Sopenharmony_ci .disconnect = lcd_disconnect, 44062306a36Sopenharmony_ci .suspend = lcd_suspend, 44162306a36Sopenharmony_ci .resume = lcd_resume, 44262306a36Sopenharmony_ci .id_table = id_table, 44362306a36Sopenharmony_ci .supports_autosuspend = 1, 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cimodule_usb_driver(lcd_driver); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ciMODULE_AUTHOR("Georges Toth <g.toth@e-biz.lu>"); 44962306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_VERSION); 45062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 451