18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/***************************************************************************** 38c2ecf20Sopenharmony_ci * USBLCD Kernel Driver * 48c2ecf20Sopenharmony_ci * Version 1.05 * 58c2ecf20Sopenharmony_ci * (C) 2005 Georges Toth <g.toth@e-biz.lu> * 68c2ecf20Sopenharmony_ci * * 78c2ecf20Sopenharmony_ci * This file is licensed under the GPL. See COPYING in the package. * 88c2ecf20Sopenharmony_ci * Based on usb-skeleton.c 2.0 by Greg Kroah-Hartman (greg@kroah.com) * 98c2ecf20Sopenharmony_ci * * 108c2ecf20Sopenharmony_ci * * 118c2ecf20Sopenharmony_ci * 28.02.05 Complete rewrite of the original usblcd.c driver, * 128c2ecf20Sopenharmony_ci * based on usb_skeleton.c. * 138c2ecf20Sopenharmony_ci * This new driver allows more than one USB-LCD to be connected * 148c2ecf20Sopenharmony_ci * and controlled, at once * 158c2ecf20Sopenharmony_ci *****************************************************************************/ 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci#include <linux/rwsem.h> 228c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 238c2ecf20Sopenharmony_ci#include <linux/usb.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRIVER_VERSION "USBLCD Driver Version 1.05" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define USBLCD_MINOR 144 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define IOCTL_GET_HARD_VERSION 1 308c2ecf20Sopenharmony_ci#define IOCTL_GET_DRV_VERSION 2 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = { 348c2ecf20Sopenharmony_ci { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, }, 358c2ecf20Sopenharmony_ci { }, 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct usb_lcd { 408c2ecf20Sopenharmony_ci struct usb_device *udev; /* init: probe_lcd */ 418c2ecf20Sopenharmony_ci struct usb_interface *interface; /* the interface for 428c2ecf20Sopenharmony_ci this device */ 438c2ecf20Sopenharmony_ci unsigned char *bulk_in_buffer; /* the buffer to receive 448c2ecf20Sopenharmony_ci data */ 458c2ecf20Sopenharmony_ci size_t bulk_in_size; /* the size of the 468c2ecf20Sopenharmony_ci receive buffer */ 478c2ecf20Sopenharmony_ci __u8 bulk_in_endpointAddr; /* the address of the 488c2ecf20Sopenharmony_ci bulk in endpoint */ 498c2ecf20Sopenharmony_ci __u8 bulk_out_endpointAddr; /* the address of the 508c2ecf20Sopenharmony_ci bulk out endpoint */ 518c2ecf20Sopenharmony_ci struct kref kref; 528c2ecf20Sopenharmony_ci struct semaphore limit_sem; /* to stop writes at 538c2ecf20Sopenharmony_ci full throttle from 548c2ecf20Sopenharmony_ci using up all RAM */ 558c2ecf20Sopenharmony_ci struct usb_anchor submitted; /* URBs to wait for 568c2ecf20Sopenharmony_ci before suspend */ 578c2ecf20Sopenharmony_ci struct rw_semaphore io_rwsem; 588c2ecf20Sopenharmony_ci unsigned long disconnected:1; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define USB_LCD_CONCURRENT_WRITES 5 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic struct usb_driver lcd_driver; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void lcd_delete(struct kref *kref) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct usb_lcd *dev = to_lcd_dev(kref); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci usb_put_dev(dev->udev); 728c2ecf20Sopenharmony_ci kfree(dev->bulk_in_buffer); 738c2ecf20Sopenharmony_ci kfree(dev); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int lcd_open(struct inode *inode, struct file *file) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct usb_lcd *dev; 808c2ecf20Sopenharmony_ci struct usb_interface *interface; 818c2ecf20Sopenharmony_ci int subminor, r; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci subminor = iminor(inode); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci interface = usb_find_interface(&lcd_driver, subminor); 868c2ecf20Sopenharmony_ci if (!interface) { 878c2ecf20Sopenharmony_ci pr_err("USBLCD: %s - error, can't find device for minor %d\n", 888c2ecf20Sopenharmony_ci __func__, subminor); 898c2ecf20Sopenharmony_ci return -ENODEV; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci dev = usb_get_intfdata(interface); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* increment our usage count for the device */ 958c2ecf20Sopenharmony_ci kref_get(&dev->kref); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* grab a power reference */ 988c2ecf20Sopenharmony_ci r = usb_autopm_get_interface(interface); 998c2ecf20Sopenharmony_ci if (r < 0) { 1008c2ecf20Sopenharmony_ci kref_put(&dev->kref, lcd_delete); 1018c2ecf20Sopenharmony_ci return r; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* save our object in the file's private structure */ 1058c2ecf20Sopenharmony_ci file->private_data = dev; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int lcd_release(struct inode *inode, struct file *file) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct usb_lcd *dev; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci dev = file->private_data; 1158c2ecf20Sopenharmony_ci if (dev == NULL) 1168c2ecf20Sopenharmony_ci return -ENODEV; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* decrement the count on our device */ 1198c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->interface); 1208c2ecf20Sopenharmony_ci kref_put(&dev->kref, lcd_delete); 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic ssize_t lcd_read(struct file *file, char __user * buffer, 1258c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct usb_lcd *dev; 1288c2ecf20Sopenharmony_ci int retval = 0; 1298c2ecf20Sopenharmony_ci int bytes_read; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci dev = file->private_data; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci down_read(&dev->io_rwsem); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (dev->disconnected) { 1368c2ecf20Sopenharmony_ci retval = -ENODEV; 1378c2ecf20Sopenharmony_ci goto out_up_io; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* do a blocking bulk read to get data from the device */ 1418c2ecf20Sopenharmony_ci retval = usb_bulk_msg(dev->udev, 1428c2ecf20Sopenharmony_ci usb_rcvbulkpipe(dev->udev, 1438c2ecf20Sopenharmony_ci dev->bulk_in_endpointAddr), 1448c2ecf20Sopenharmony_ci dev->bulk_in_buffer, 1458c2ecf20Sopenharmony_ci min(dev->bulk_in_size, count), 1468c2ecf20Sopenharmony_ci &bytes_read, 10000); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* if the read was successful, copy the data to userspace */ 1498c2ecf20Sopenharmony_ci if (!retval) { 1508c2ecf20Sopenharmony_ci if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read)) 1518c2ecf20Sopenharmony_ci retval = -EFAULT; 1528c2ecf20Sopenharmony_ci else 1538c2ecf20Sopenharmony_ci retval = bytes_read; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ciout_up_io: 1578c2ecf20Sopenharmony_ci up_read(&dev->io_rwsem); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return retval; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct usb_lcd *dev; 1658c2ecf20Sopenharmony_ci u16 bcdDevice; 1668c2ecf20Sopenharmony_ci char buf[30]; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci dev = file->private_data; 1698c2ecf20Sopenharmony_ci if (dev == NULL) 1708c2ecf20Sopenharmony_ci return -ENODEV; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci switch (cmd) { 1738c2ecf20Sopenharmony_ci case IOCTL_GET_HARD_VERSION: 1748c2ecf20Sopenharmony_ci bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice); 1758c2ecf20Sopenharmony_ci sprintf(buf, "%1d%1d.%1d%1d", 1768c2ecf20Sopenharmony_ci (bcdDevice & 0xF000)>>12, 1778c2ecf20Sopenharmony_ci (bcdDevice & 0xF00)>>8, 1788c2ecf20Sopenharmony_ci (bcdDevice & 0xF0)>>4, 1798c2ecf20Sopenharmony_ci (bcdDevice & 0xF)); 1808c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) 1818c2ecf20Sopenharmony_ci return -EFAULT; 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case IOCTL_GET_DRV_VERSION: 1848c2ecf20Sopenharmony_ci sprintf(buf, DRIVER_VERSION); 1858c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) 1868c2ecf20Sopenharmony_ci return -EFAULT; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci return -ENOTTY; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void lcd_write_bulk_callback(struct urb *urb) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct usb_lcd *dev; 1988c2ecf20Sopenharmony_ci int status = urb->status; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci dev = urb->context; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* sync/async unlink faults aren't errors */ 2038c2ecf20Sopenharmony_ci if (status && 2048c2ecf20Sopenharmony_ci !(status == -ENOENT || 2058c2ecf20Sopenharmony_ci status == -ECONNRESET || 2068c2ecf20Sopenharmony_ci status == -ESHUTDOWN)) { 2078c2ecf20Sopenharmony_ci dev_dbg(&dev->interface->dev, 2088c2ecf20Sopenharmony_ci "nonzero write bulk status received: %d\n", status); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* free up our allocated buffer */ 2128c2ecf20Sopenharmony_ci usb_free_coherent(urb->dev, urb->transfer_buffer_length, 2138c2ecf20Sopenharmony_ci urb->transfer_buffer, urb->transfer_dma); 2148c2ecf20Sopenharmony_ci up(&dev->limit_sem); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic ssize_t lcd_write(struct file *file, const char __user * user_buffer, 2188c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct usb_lcd *dev; 2218c2ecf20Sopenharmony_ci int retval = 0, r; 2228c2ecf20Sopenharmony_ci struct urb *urb = NULL; 2238c2ecf20Sopenharmony_ci char *buf = NULL; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci dev = file->private_data; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* verify that we actually have some data to write */ 2288c2ecf20Sopenharmony_ci if (count == 0) 2298c2ecf20Sopenharmony_ci goto exit; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci r = down_interruptible(&dev->limit_sem); 2328c2ecf20Sopenharmony_ci if (r < 0) 2338c2ecf20Sopenharmony_ci return -EINTR; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci down_read(&dev->io_rwsem); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (dev->disconnected) { 2388c2ecf20Sopenharmony_ci retval = -ENODEV; 2398c2ecf20Sopenharmony_ci goto err_up_io; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* create a urb, and a buffer for it, and copy the data to the urb */ 2438c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_KERNEL); 2448c2ecf20Sopenharmony_ci if (!urb) { 2458c2ecf20Sopenharmony_ci retval = -ENOMEM; 2468c2ecf20Sopenharmony_ci goto err_up_io; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, 2508c2ecf20Sopenharmony_ci &urb->transfer_dma); 2518c2ecf20Sopenharmony_ci if (!buf) { 2528c2ecf20Sopenharmony_ci retval = -ENOMEM; 2538c2ecf20Sopenharmony_ci goto error; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (copy_from_user(buf, user_buffer, count)) { 2578c2ecf20Sopenharmony_ci retval = -EFAULT; 2588c2ecf20Sopenharmony_ci goto error; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* initialize the urb properly */ 2628c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, dev->udev, 2638c2ecf20Sopenharmony_ci usb_sndbulkpipe(dev->udev, 2648c2ecf20Sopenharmony_ci dev->bulk_out_endpointAddr), 2658c2ecf20Sopenharmony_ci buf, count, lcd_write_bulk_callback, dev); 2668c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &dev->submitted); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* send the data out the bulk port */ 2718c2ecf20Sopenharmony_ci retval = usb_submit_urb(urb, GFP_KERNEL); 2728c2ecf20Sopenharmony_ci if (retval) { 2738c2ecf20Sopenharmony_ci dev_err(&dev->udev->dev, 2748c2ecf20Sopenharmony_ci "%s - failed submitting write urb, error %d\n", 2758c2ecf20Sopenharmony_ci __func__, retval); 2768c2ecf20Sopenharmony_ci goto error_unanchor; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* release our reference to this urb, 2808c2ecf20Sopenharmony_ci the USB core will eventually free it entirely */ 2818c2ecf20Sopenharmony_ci usb_free_urb(urb); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci up_read(&dev->io_rwsem); 2848c2ecf20Sopenharmony_ciexit: 2858c2ecf20Sopenharmony_ci return count; 2868c2ecf20Sopenharmony_cierror_unanchor: 2878c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 2888c2ecf20Sopenharmony_cierror: 2898c2ecf20Sopenharmony_ci usb_free_coherent(dev->udev, count, buf, urb->transfer_dma); 2908c2ecf20Sopenharmony_ci usb_free_urb(urb); 2918c2ecf20Sopenharmony_cierr_up_io: 2928c2ecf20Sopenharmony_ci up_read(&dev->io_rwsem); 2938c2ecf20Sopenharmony_ci up(&dev->limit_sem); 2948c2ecf20Sopenharmony_ci return retval; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic const struct file_operations lcd_fops = { 2988c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2998c2ecf20Sopenharmony_ci .read = lcd_read, 3008c2ecf20Sopenharmony_ci .write = lcd_write, 3018c2ecf20Sopenharmony_ci .open = lcd_open, 3028c2ecf20Sopenharmony_ci .unlocked_ioctl = lcd_ioctl, 3038c2ecf20Sopenharmony_ci .release = lcd_release, 3048c2ecf20Sopenharmony_ci .llseek = noop_llseek, 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* 3088c2ecf20Sopenharmony_ci * usb class driver info in order to get a minor number from the usb core, 3098c2ecf20Sopenharmony_ci * and to have the device registered with the driver core 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_cistatic struct usb_class_driver lcd_class = { 3128c2ecf20Sopenharmony_ci .name = "lcd%d", 3138c2ecf20Sopenharmony_ci .fops = &lcd_fops, 3148c2ecf20Sopenharmony_ci .minor_base = USBLCD_MINOR, 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int lcd_probe(struct usb_interface *interface, 3188c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct usb_lcd *dev = NULL; 3218c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *bulk_in, *bulk_out; 3228c2ecf20Sopenharmony_ci int i; 3238c2ecf20Sopenharmony_ci int retval; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* allocate memory for our device state and initialize it */ 3268c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 3278c2ecf20Sopenharmony_ci if (!dev) 3288c2ecf20Sopenharmony_ci return -ENOMEM; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci kref_init(&dev->kref); 3318c2ecf20Sopenharmony_ci sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES); 3328c2ecf20Sopenharmony_ci init_rwsem(&dev->io_rwsem); 3338c2ecf20Sopenharmony_ci init_usb_anchor(&dev->submitted); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci dev->udev = usb_get_dev(interface_to_usbdev(interface)); 3368c2ecf20Sopenharmony_ci dev->interface = interface; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) { 3398c2ecf20Sopenharmony_ci dev_warn(&interface->dev, "USBLCD model not supported.\n"); 3408c2ecf20Sopenharmony_ci retval = -ENODEV; 3418c2ecf20Sopenharmony_ci goto error; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* set up the endpoint information */ 3458c2ecf20Sopenharmony_ci /* use only the first bulk-in and bulk-out endpoints */ 3468c2ecf20Sopenharmony_ci retval = usb_find_common_endpoints(interface->cur_altsetting, 3478c2ecf20Sopenharmony_ci &bulk_in, &bulk_out, NULL, NULL); 3488c2ecf20Sopenharmony_ci if (retval) { 3498c2ecf20Sopenharmony_ci dev_err(&interface->dev, 3508c2ecf20Sopenharmony_ci "Could not find both bulk-in and bulk-out endpoints\n"); 3518c2ecf20Sopenharmony_ci goto error; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci dev->bulk_in_size = usb_endpoint_maxp(bulk_in); 3558c2ecf20Sopenharmony_ci dev->bulk_in_endpointAddr = bulk_in->bEndpointAddress; 3568c2ecf20Sopenharmony_ci dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL); 3578c2ecf20Sopenharmony_ci if (!dev->bulk_in_buffer) { 3588c2ecf20Sopenharmony_ci retval = -ENOMEM; 3598c2ecf20Sopenharmony_ci goto error; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* save our data pointer in this interface device */ 3658c2ecf20Sopenharmony_ci usb_set_intfdata(interface, dev); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* we can register the device now, as it is ready */ 3688c2ecf20Sopenharmony_ci retval = usb_register_dev(interface, &lcd_class); 3698c2ecf20Sopenharmony_ci if (retval) { 3708c2ecf20Sopenharmony_ci /* something prevented us from registering this driver */ 3718c2ecf20Sopenharmony_ci dev_err(&interface->dev, 3728c2ecf20Sopenharmony_ci "Not able to get a minor for this device.\n"); 3738c2ecf20Sopenharmony_ci goto error; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci i = le16_to_cpu(dev->udev->descriptor.bcdDevice); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found " 3798c2ecf20Sopenharmony_ci "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8, 3808c2ecf20Sopenharmony_ci (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* let the user know what node this device is now attached to */ 3838c2ecf20Sopenharmony_ci dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n", 3848c2ecf20Sopenharmony_ci interface->minor); 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cierror: 3888c2ecf20Sopenharmony_ci kref_put(&dev->kref, lcd_delete); 3898c2ecf20Sopenharmony_ci return retval; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic void lcd_draw_down(struct usb_lcd *dev) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci int time; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); 3978c2ecf20Sopenharmony_ci if (!time) 3988c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&dev->submitted); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int lcd_suspend(struct usb_interface *intf, pm_message_t message) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct usb_lcd *dev = usb_get_intfdata(intf); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (!dev) 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci lcd_draw_down(dev); 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int lcd_resume(struct usb_interface *intf) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic void lcd_disconnect(struct usb_interface *interface) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct usb_lcd *dev = usb_get_intfdata(interface); 4198c2ecf20Sopenharmony_ci int minor = interface->minor; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* give back our minor */ 4228c2ecf20Sopenharmony_ci usb_deregister_dev(interface, &lcd_class); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci down_write(&dev->io_rwsem); 4258c2ecf20Sopenharmony_ci dev->disconnected = 1; 4268c2ecf20Sopenharmony_ci up_write(&dev->io_rwsem); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&dev->submitted); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* decrement our usage count */ 4318c2ecf20Sopenharmony_ci kref_put(&dev->kref, lcd_delete); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci dev_info(&interface->dev, "USB LCD #%d now disconnected\n", minor); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic struct usb_driver lcd_driver = { 4378c2ecf20Sopenharmony_ci .name = "usblcd", 4388c2ecf20Sopenharmony_ci .probe = lcd_probe, 4398c2ecf20Sopenharmony_ci .disconnect = lcd_disconnect, 4408c2ecf20Sopenharmony_ci .suspend = lcd_suspend, 4418c2ecf20Sopenharmony_ci .resume = lcd_resume, 4428c2ecf20Sopenharmony_ci .id_table = id_table, 4438c2ecf20Sopenharmony_ci .supports_autosuspend = 1, 4448c2ecf20Sopenharmony_ci}; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cimodule_usb_driver(lcd_driver); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Georges Toth <g.toth@e-biz.lu>"); 4498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_VERSION); 4508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 451