18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* Siemens ID Mouse driver v0.6 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Copyright (C) 2004-5 by Florian 'Floe' Echtler <echtler@fs.tum.de> 58c2ecf20Sopenharmony_ci and Andreas 'ad' Deresch <aderesch@fs.tum.de> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci Derived from the USB Skeleton driver 1.1, 88c2ecf20Sopenharmony_ci Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com) 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci Additional information provided by Martin Reising 118c2ecf20Sopenharmony_ci <Martin.Reising@natural-computing.de> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci*/ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/completion.h> 228c2ecf20Sopenharmony_ci#include <linux/mutex.h> 238c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 248c2ecf20Sopenharmony_ci#include <linux/usb.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* image constants */ 278c2ecf20Sopenharmony_ci#define WIDTH 225 288c2ecf20Sopenharmony_ci#define HEIGHT 289 298c2ecf20Sopenharmony_ci#define HEADER "P5 225 289 255 " 308c2ecf20Sopenharmony_ci#define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define DRIVER_SHORT "idmouse" 338c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Florian 'Floe' Echtler <echtler@fs.tum.de>" 348c2ecf20Sopenharmony_ci#define DRIVER_DESC "Siemens ID Mouse FingerTIP Sensor Driver" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* minor number for misc USB devices */ 378c2ecf20Sopenharmony_ci#define USB_IDMOUSE_MINOR_BASE 132 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* vendor and device IDs */ 408c2ecf20Sopenharmony_ci#define ID_SIEMENS 0x0681 418c2ecf20Sopenharmony_ci#define ID_IDMOUSE 0x0005 428c2ecf20Sopenharmony_ci#define ID_CHERRY 0x0010 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* device ID table */ 458c2ecf20Sopenharmony_cistatic const struct usb_device_id idmouse_table[] = { 468c2ecf20Sopenharmony_ci {USB_DEVICE(ID_SIEMENS, ID_IDMOUSE)}, /* Siemens ID Mouse (Professional) */ 478c2ecf20Sopenharmony_ci {USB_DEVICE(ID_SIEMENS, ID_CHERRY )}, /* Cherry FingerTIP ID Board */ 488c2ecf20Sopenharmony_ci {} /* terminating null entry */ 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* sensor commands */ 528c2ecf20Sopenharmony_ci#define FTIP_RESET 0x20 538c2ecf20Sopenharmony_ci#define FTIP_ACQUIRE 0x21 548c2ecf20Sopenharmony_ci#define FTIP_RELEASE 0x22 558c2ecf20Sopenharmony_ci#define FTIP_BLINK 0x23 /* LSB of value = blink pulse width */ 568c2ecf20Sopenharmony_ci#define FTIP_SCROLL 0x24 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define ftip_command(dev, command, value, index) \ 598c2ecf20Sopenharmony_ci usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), command, \ 608c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, idmouse_table); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* structure to hold all of our device specific stuff */ 658c2ecf20Sopenharmony_cistruct usb_idmouse { 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci struct usb_device *udev; /* save off the usb device pointer */ 688c2ecf20Sopenharmony_ci struct usb_interface *interface; /* the interface for this device */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci unsigned char *bulk_in_buffer; /* the buffer to receive data */ 718c2ecf20Sopenharmony_ci size_t bulk_in_size; /* the maximum bulk packet size */ 728c2ecf20Sopenharmony_ci size_t orig_bi_size; /* same as above, but reported by the device */ 738c2ecf20Sopenharmony_ci __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci int open; /* if the port is open or not */ 768c2ecf20Sopenharmony_ci int present; /* if the device is not disconnected */ 778c2ecf20Sopenharmony_ci struct mutex lock; /* locks this structure */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* local function prototypes */ 828c2ecf20Sopenharmony_cistatic ssize_t idmouse_read(struct file *file, char __user *buffer, 838c2ecf20Sopenharmony_ci size_t count, loff_t * ppos); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int idmouse_open(struct inode *inode, struct file *file); 868c2ecf20Sopenharmony_cistatic int idmouse_release(struct inode *inode, struct file *file); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int idmouse_probe(struct usb_interface *interface, 898c2ecf20Sopenharmony_ci const struct usb_device_id *id); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void idmouse_disconnect(struct usb_interface *interface); 928c2ecf20Sopenharmony_cistatic int idmouse_suspend(struct usb_interface *intf, pm_message_t message); 938c2ecf20Sopenharmony_cistatic int idmouse_resume(struct usb_interface *intf); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* file operation pointers */ 968c2ecf20Sopenharmony_cistatic const struct file_operations idmouse_fops = { 978c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 988c2ecf20Sopenharmony_ci .read = idmouse_read, 998c2ecf20Sopenharmony_ci .open = idmouse_open, 1008c2ecf20Sopenharmony_ci .release = idmouse_release, 1018c2ecf20Sopenharmony_ci .llseek = default_llseek, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* class driver information */ 1058c2ecf20Sopenharmony_cistatic struct usb_class_driver idmouse_class = { 1068c2ecf20Sopenharmony_ci .name = "idmouse%d", 1078c2ecf20Sopenharmony_ci .fops = &idmouse_fops, 1088c2ecf20Sopenharmony_ci .minor_base = USB_IDMOUSE_MINOR_BASE, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */ 1128c2ecf20Sopenharmony_cistatic struct usb_driver idmouse_driver = { 1138c2ecf20Sopenharmony_ci .name = DRIVER_SHORT, 1148c2ecf20Sopenharmony_ci .probe = idmouse_probe, 1158c2ecf20Sopenharmony_ci .disconnect = idmouse_disconnect, 1168c2ecf20Sopenharmony_ci .suspend = idmouse_suspend, 1178c2ecf20Sopenharmony_ci .resume = idmouse_resume, 1188c2ecf20Sopenharmony_ci .reset_resume = idmouse_resume, 1198c2ecf20Sopenharmony_ci .id_table = idmouse_table, 1208c2ecf20Sopenharmony_ci .supports_autosuspend = 1, 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int idmouse_create_image(struct usb_idmouse *dev) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int bytes_read; 1268c2ecf20Sopenharmony_ci int bulk_read; 1278c2ecf20Sopenharmony_ci int result; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci memcpy(dev->bulk_in_buffer, HEADER, sizeof(HEADER)-1); 1308c2ecf20Sopenharmony_ci bytes_read = sizeof(HEADER)-1; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* reset the device and set a fast blink rate */ 1338c2ecf20Sopenharmony_ci result = ftip_command(dev, FTIP_RELEASE, 0, 0); 1348c2ecf20Sopenharmony_ci if (result < 0) 1358c2ecf20Sopenharmony_ci goto reset; 1368c2ecf20Sopenharmony_ci result = ftip_command(dev, FTIP_BLINK, 1, 0); 1378c2ecf20Sopenharmony_ci if (result < 0) 1388c2ecf20Sopenharmony_ci goto reset; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* initialize the sensor - sending this command twice */ 1418c2ecf20Sopenharmony_ci /* significantly reduces the rate of failed reads */ 1428c2ecf20Sopenharmony_ci result = ftip_command(dev, FTIP_ACQUIRE, 0, 0); 1438c2ecf20Sopenharmony_ci if (result < 0) 1448c2ecf20Sopenharmony_ci goto reset; 1458c2ecf20Sopenharmony_ci result = ftip_command(dev, FTIP_ACQUIRE, 0, 0); 1468c2ecf20Sopenharmony_ci if (result < 0) 1478c2ecf20Sopenharmony_ci goto reset; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* start the readout - sending this command twice */ 1508c2ecf20Sopenharmony_ci /* presumably enables the high dynamic range mode */ 1518c2ecf20Sopenharmony_ci result = ftip_command(dev, FTIP_RESET, 0, 0); 1528c2ecf20Sopenharmony_ci if (result < 0) 1538c2ecf20Sopenharmony_ci goto reset; 1548c2ecf20Sopenharmony_ci result = ftip_command(dev, FTIP_RESET, 0, 0); 1558c2ecf20Sopenharmony_ci if (result < 0) 1568c2ecf20Sopenharmony_ci goto reset; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* loop over a blocking bulk read to get data from the device */ 1598c2ecf20Sopenharmony_ci while (bytes_read < IMGSIZE) { 1608c2ecf20Sopenharmony_ci result = usb_bulk_msg(dev->udev, 1618c2ecf20Sopenharmony_ci usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), 1628c2ecf20Sopenharmony_ci dev->bulk_in_buffer + bytes_read, 1638c2ecf20Sopenharmony_ci dev->bulk_in_size, &bulk_read, 5000); 1648c2ecf20Sopenharmony_ci if (result < 0) { 1658c2ecf20Sopenharmony_ci /* Maybe this error was caused by the increased packet size? */ 1668c2ecf20Sopenharmony_ci /* Reset to the original value and tell userspace to retry. */ 1678c2ecf20Sopenharmony_ci if (dev->bulk_in_size != dev->orig_bi_size) { 1688c2ecf20Sopenharmony_ci dev->bulk_in_size = dev->orig_bi_size; 1698c2ecf20Sopenharmony_ci result = -EAGAIN; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci if (signal_pending(current)) { 1748c2ecf20Sopenharmony_ci result = -EINTR; 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci bytes_read += bulk_read; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* check for valid image */ 1818c2ecf20Sopenharmony_ci /* right border should be black (0x00) */ 1828c2ecf20Sopenharmony_ci for (bytes_read = sizeof(HEADER)-1 + WIDTH-1; bytes_read < IMGSIZE; bytes_read += WIDTH) 1838c2ecf20Sopenharmony_ci if (dev->bulk_in_buffer[bytes_read] != 0x00) 1848c2ecf20Sopenharmony_ci return -EAGAIN; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* lower border should be white (0xFF) */ 1878c2ecf20Sopenharmony_ci for (bytes_read = IMGSIZE-WIDTH; bytes_read < IMGSIZE-1; bytes_read++) 1888c2ecf20Sopenharmony_ci if (dev->bulk_in_buffer[bytes_read] != 0xFF) 1898c2ecf20Sopenharmony_ci return -EAGAIN; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* reset the device */ 1928c2ecf20Sopenharmony_cireset: 1938c2ecf20Sopenharmony_ci ftip_command(dev, FTIP_RELEASE, 0, 0); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* should be IMGSIZE == 65040 */ 1968c2ecf20Sopenharmony_ci dev_dbg(&dev->interface->dev, "read %d bytes fingerprint data\n", 1978c2ecf20Sopenharmony_ci bytes_read); 1988c2ecf20Sopenharmony_ci return result; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* PM operations are nops as this driver does IO only during open() */ 2028c2ecf20Sopenharmony_cistatic int idmouse_suspend(struct usb_interface *intf, pm_message_t message) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int idmouse_resume(struct usb_interface *intf) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic inline void idmouse_delete(struct usb_idmouse *dev) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci kfree(dev->bulk_in_buffer); 2158c2ecf20Sopenharmony_ci kfree(dev); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int idmouse_open(struct inode *inode, struct file *file) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct usb_idmouse *dev; 2218c2ecf20Sopenharmony_ci struct usb_interface *interface; 2228c2ecf20Sopenharmony_ci int result; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* get the interface from minor number and driver information */ 2258c2ecf20Sopenharmony_ci interface = usb_find_interface(&idmouse_driver, iminor(inode)); 2268c2ecf20Sopenharmony_ci if (!interface) 2278c2ecf20Sopenharmony_ci return -ENODEV; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* get the device information block from the interface */ 2308c2ecf20Sopenharmony_ci dev = usb_get_intfdata(interface); 2318c2ecf20Sopenharmony_ci if (!dev) 2328c2ecf20Sopenharmony_ci return -ENODEV; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* lock this device */ 2358c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* check if already open */ 2388c2ecf20Sopenharmony_ci if (dev->open) { 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* already open, so fail */ 2418c2ecf20Sopenharmony_ci result = -EBUSY; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci } else { 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* create a new image and check for success */ 2468c2ecf20Sopenharmony_ci result = usb_autopm_get_interface(interface); 2478c2ecf20Sopenharmony_ci if (result) 2488c2ecf20Sopenharmony_ci goto error; 2498c2ecf20Sopenharmony_ci result = idmouse_create_image(dev); 2508c2ecf20Sopenharmony_ci usb_autopm_put_interface(interface); 2518c2ecf20Sopenharmony_ci if (result) 2528c2ecf20Sopenharmony_ci goto error; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* increment our usage count for the driver */ 2558c2ecf20Sopenharmony_ci ++dev->open; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* save our object in the file's private structure */ 2588c2ecf20Sopenharmony_ci file->private_data = dev; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cierror: 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* unlock this device */ 2658c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 2668c2ecf20Sopenharmony_ci return result; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int idmouse_release(struct inode *inode, struct file *file) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct usb_idmouse *dev; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci dev = file->private_data; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (dev == NULL) 2768c2ecf20Sopenharmony_ci return -ENODEV; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* lock our device */ 2798c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci --dev->open; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (!dev->present) { 2848c2ecf20Sopenharmony_ci /* the device was unplugged before the file was released */ 2858c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 2868c2ecf20Sopenharmony_ci idmouse_delete(dev); 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count, 2948c2ecf20Sopenharmony_ci loff_t * ppos) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct usb_idmouse *dev = file->private_data; 2978c2ecf20Sopenharmony_ci int result; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* lock this object */ 3008c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* verify that the device wasn't unplugged */ 3038c2ecf20Sopenharmony_ci if (!dev->present) { 3048c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 3058c2ecf20Sopenharmony_ci return -ENODEV; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci result = simple_read_from_buffer(buffer, count, ppos, 3098c2ecf20Sopenharmony_ci dev->bulk_in_buffer, IMGSIZE); 3108c2ecf20Sopenharmony_ci /* unlock the device */ 3118c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 3128c2ecf20Sopenharmony_ci return result; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int idmouse_probe(struct usb_interface *interface, 3168c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(interface); 3198c2ecf20Sopenharmony_ci struct usb_idmouse *dev; 3208c2ecf20Sopenharmony_ci struct usb_host_interface *iface_desc; 3218c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 3228c2ecf20Sopenharmony_ci int result; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* check if we have gotten the data or the hid interface */ 3258c2ecf20Sopenharmony_ci iface_desc = interface->cur_altsetting; 3268c2ecf20Sopenharmony_ci if (iface_desc->desc.bInterfaceClass != 0x0A) 3278c2ecf20Sopenharmony_ci return -ENODEV; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (iface_desc->desc.bNumEndpoints < 1) 3308c2ecf20Sopenharmony_ci return -ENODEV; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* allocate memory for our device state and initialize it */ 3338c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 3348c2ecf20Sopenharmony_ci if (dev == NULL) 3358c2ecf20Sopenharmony_ci return -ENOMEM; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci mutex_init(&dev->lock); 3388c2ecf20Sopenharmony_ci dev->udev = udev; 3398c2ecf20Sopenharmony_ci dev->interface = interface; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* set up the endpoint information - use only the first bulk-in endpoint */ 3428c2ecf20Sopenharmony_ci result = usb_find_bulk_in_endpoint(iface_desc, &endpoint); 3438c2ecf20Sopenharmony_ci if (result) { 3448c2ecf20Sopenharmony_ci dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n"); 3458c2ecf20Sopenharmony_ci idmouse_delete(dev); 3468c2ecf20Sopenharmony_ci return result; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci dev->orig_bi_size = usb_endpoint_maxp(endpoint); 3508c2ecf20Sopenharmony_ci dev->bulk_in_size = 0x200; /* works _much_ faster */ 3518c2ecf20Sopenharmony_ci dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; 3528c2ecf20Sopenharmony_ci dev->bulk_in_buffer = kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL); 3538c2ecf20Sopenharmony_ci if (!dev->bulk_in_buffer) { 3548c2ecf20Sopenharmony_ci idmouse_delete(dev); 3558c2ecf20Sopenharmony_ci return -ENOMEM; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* allow device read, write and ioctl */ 3598c2ecf20Sopenharmony_ci dev->present = 1; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* we can register the device now, as it is ready */ 3628c2ecf20Sopenharmony_ci usb_set_intfdata(interface, dev); 3638c2ecf20Sopenharmony_ci result = usb_register_dev(interface, &idmouse_class); 3648c2ecf20Sopenharmony_ci if (result) { 3658c2ecf20Sopenharmony_ci /* something prevented us from registering this device */ 3668c2ecf20Sopenharmony_ci dev_err(&interface->dev, "Unable to allocate minor number.\n"); 3678c2ecf20Sopenharmony_ci idmouse_delete(dev); 3688c2ecf20Sopenharmony_ci return result; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* be noisy */ 3728c2ecf20Sopenharmony_ci dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic void idmouse_disconnect(struct usb_interface *interface) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct usb_idmouse *dev = usb_get_intfdata(interface); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* give back our minor */ 3828c2ecf20Sopenharmony_ci usb_deregister_dev(interface, &idmouse_class); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* lock the device */ 3858c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* prevent device read, write and ioctl */ 3888c2ecf20Sopenharmony_ci dev->present = 0; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* if the device is opened, idmouse_release will clean this up */ 3918c2ecf20Sopenharmony_ci if (!dev->open) { 3928c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 3938c2ecf20Sopenharmony_ci idmouse_delete(dev); 3948c2ecf20Sopenharmony_ci } else { 3958c2ecf20Sopenharmony_ci /* unlock */ 3968c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci dev_info(&interface->dev, "disconnected\n"); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cimodule_usb_driver(idmouse_driver); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 4058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 4068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4078c2ecf20Sopenharmony_ci 408