162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Siemens ID Mouse driver v0.6 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci Copyright (C) 2004-5 by Florian 'Floe' Echtler <echtler@fs.tum.de> 562306a36Sopenharmony_ci and Andreas 'ad' Deresch <aderesch@fs.tum.de> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Derived from the USB Skeleton driver 1.1, 862306a36Sopenharmony_ci Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com) 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci Additional information provided by Martin Reising 1162306a36Sopenharmony_ci <Martin.Reising@natural-computing.de> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci*/ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/sched/signal.h> 1762306a36Sopenharmony_ci#include <linux/errno.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/completion.h> 2262306a36Sopenharmony_ci#include <linux/mutex.h> 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <linux/usb.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* image constants */ 2762306a36Sopenharmony_ci#define WIDTH 225 2862306a36Sopenharmony_ci#define HEIGHT 289 2962306a36Sopenharmony_ci#define HEADER "P5 225 289 255 " 3062306a36Sopenharmony_ci#define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define DRIVER_SHORT "idmouse" 3362306a36Sopenharmony_ci#define DRIVER_AUTHOR "Florian 'Floe' Echtler <echtler@fs.tum.de>" 3462306a36Sopenharmony_ci#define DRIVER_DESC "Siemens ID Mouse FingerTIP Sensor Driver" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* minor number for misc USB devices */ 3762306a36Sopenharmony_ci#define USB_IDMOUSE_MINOR_BASE 132 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* vendor and device IDs */ 4062306a36Sopenharmony_ci#define ID_SIEMENS 0x0681 4162306a36Sopenharmony_ci#define ID_IDMOUSE 0x0005 4262306a36Sopenharmony_ci#define ID_CHERRY 0x0010 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* device ID table */ 4562306a36Sopenharmony_cistatic const struct usb_device_id idmouse_table[] = { 4662306a36Sopenharmony_ci {USB_DEVICE(ID_SIEMENS, ID_IDMOUSE)}, /* Siemens ID Mouse (Professional) */ 4762306a36Sopenharmony_ci {USB_DEVICE(ID_SIEMENS, ID_CHERRY )}, /* Cherry FingerTIP ID Board */ 4862306a36Sopenharmony_ci {} /* terminating null entry */ 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* sensor commands */ 5262306a36Sopenharmony_ci#define FTIP_RESET 0x20 5362306a36Sopenharmony_ci#define FTIP_ACQUIRE 0x21 5462306a36Sopenharmony_ci#define FTIP_RELEASE 0x22 5562306a36Sopenharmony_ci#define FTIP_BLINK 0x23 /* LSB of value = blink pulse width */ 5662306a36Sopenharmony_ci#define FTIP_SCROLL 0x24 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define ftip_command(dev, command, value, index) \ 5962306a36Sopenharmony_ci usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), command, \ 6062306a36Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, idmouse_table); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* structure to hold all of our device specific stuff */ 6562306a36Sopenharmony_cistruct usb_idmouse { 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci struct usb_device *udev; /* save off the usb device pointer */ 6862306a36Sopenharmony_ci struct usb_interface *interface; /* the interface for this device */ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci unsigned char *bulk_in_buffer; /* the buffer to receive data */ 7162306a36Sopenharmony_ci size_t bulk_in_size; /* the maximum bulk packet size */ 7262306a36Sopenharmony_ci size_t orig_bi_size; /* same as above, but reported by the device */ 7362306a36Sopenharmony_ci __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci int open; /* if the port is open or not */ 7662306a36Sopenharmony_ci int present; /* if the device is not disconnected */ 7762306a36Sopenharmony_ci struct mutex lock; /* locks this structure */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* local function prototypes */ 8262306a36Sopenharmony_cistatic ssize_t idmouse_read(struct file *file, char __user *buffer, 8362306a36Sopenharmony_ci size_t count, loff_t * ppos); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int idmouse_open(struct inode *inode, struct file *file); 8662306a36Sopenharmony_cistatic int idmouse_release(struct inode *inode, struct file *file); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int idmouse_probe(struct usb_interface *interface, 8962306a36Sopenharmony_ci const struct usb_device_id *id); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void idmouse_disconnect(struct usb_interface *interface); 9262306a36Sopenharmony_cistatic int idmouse_suspend(struct usb_interface *intf, pm_message_t message); 9362306a36Sopenharmony_cistatic int idmouse_resume(struct usb_interface *intf); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* file operation pointers */ 9662306a36Sopenharmony_cistatic const struct file_operations idmouse_fops = { 9762306a36Sopenharmony_ci .owner = THIS_MODULE, 9862306a36Sopenharmony_ci .read = idmouse_read, 9962306a36Sopenharmony_ci .open = idmouse_open, 10062306a36Sopenharmony_ci .release = idmouse_release, 10162306a36Sopenharmony_ci .llseek = default_llseek, 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* class driver information */ 10562306a36Sopenharmony_cistatic struct usb_class_driver idmouse_class = { 10662306a36Sopenharmony_ci .name = "idmouse%d", 10762306a36Sopenharmony_ci .fops = &idmouse_fops, 10862306a36Sopenharmony_ci .minor_base = USB_IDMOUSE_MINOR_BASE, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */ 11262306a36Sopenharmony_cistatic struct usb_driver idmouse_driver = { 11362306a36Sopenharmony_ci .name = DRIVER_SHORT, 11462306a36Sopenharmony_ci .probe = idmouse_probe, 11562306a36Sopenharmony_ci .disconnect = idmouse_disconnect, 11662306a36Sopenharmony_ci .suspend = idmouse_suspend, 11762306a36Sopenharmony_ci .resume = idmouse_resume, 11862306a36Sopenharmony_ci .reset_resume = idmouse_resume, 11962306a36Sopenharmony_ci .id_table = idmouse_table, 12062306a36Sopenharmony_ci .supports_autosuspend = 1, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int idmouse_create_image(struct usb_idmouse *dev) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci int bytes_read; 12662306a36Sopenharmony_ci int bulk_read; 12762306a36Sopenharmony_ci int result; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci memcpy(dev->bulk_in_buffer, HEADER, sizeof(HEADER)-1); 13062306a36Sopenharmony_ci bytes_read = sizeof(HEADER)-1; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* reset the device and set a fast blink rate */ 13362306a36Sopenharmony_ci result = ftip_command(dev, FTIP_RELEASE, 0, 0); 13462306a36Sopenharmony_ci if (result < 0) 13562306a36Sopenharmony_ci goto reset; 13662306a36Sopenharmony_ci result = ftip_command(dev, FTIP_BLINK, 1, 0); 13762306a36Sopenharmony_ci if (result < 0) 13862306a36Sopenharmony_ci goto reset; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* initialize the sensor - sending this command twice */ 14162306a36Sopenharmony_ci /* significantly reduces the rate of failed reads */ 14262306a36Sopenharmony_ci result = ftip_command(dev, FTIP_ACQUIRE, 0, 0); 14362306a36Sopenharmony_ci if (result < 0) 14462306a36Sopenharmony_ci goto reset; 14562306a36Sopenharmony_ci result = ftip_command(dev, FTIP_ACQUIRE, 0, 0); 14662306a36Sopenharmony_ci if (result < 0) 14762306a36Sopenharmony_ci goto reset; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* start the readout - sending this command twice */ 15062306a36Sopenharmony_ci /* presumably enables the high dynamic range mode */ 15162306a36Sopenharmony_ci result = ftip_command(dev, FTIP_RESET, 0, 0); 15262306a36Sopenharmony_ci if (result < 0) 15362306a36Sopenharmony_ci goto reset; 15462306a36Sopenharmony_ci result = ftip_command(dev, FTIP_RESET, 0, 0); 15562306a36Sopenharmony_ci if (result < 0) 15662306a36Sopenharmony_ci goto reset; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* loop over a blocking bulk read to get data from the device */ 15962306a36Sopenharmony_ci while (bytes_read < IMGSIZE) { 16062306a36Sopenharmony_ci result = usb_bulk_msg(dev->udev, 16162306a36Sopenharmony_ci usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), 16262306a36Sopenharmony_ci dev->bulk_in_buffer + bytes_read, 16362306a36Sopenharmony_ci dev->bulk_in_size, &bulk_read, 5000); 16462306a36Sopenharmony_ci if (result < 0) { 16562306a36Sopenharmony_ci /* Maybe this error was caused by the increased packet size? */ 16662306a36Sopenharmony_ci /* Reset to the original value and tell userspace to retry. */ 16762306a36Sopenharmony_ci if (dev->bulk_in_size != dev->orig_bi_size) { 16862306a36Sopenharmony_ci dev->bulk_in_size = dev->orig_bi_size; 16962306a36Sopenharmony_ci result = -EAGAIN; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci if (signal_pending(current)) { 17462306a36Sopenharmony_ci result = -EINTR; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci bytes_read += bulk_read; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* check for valid image */ 18162306a36Sopenharmony_ci /* right border should be black (0x00) */ 18262306a36Sopenharmony_ci for (bytes_read = sizeof(HEADER)-1 + WIDTH-1; bytes_read < IMGSIZE; bytes_read += WIDTH) 18362306a36Sopenharmony_ci if (dev->bulk_in_buffer[bytes_read] != 0x00) 18462306a36Sopenharmony_ci return -EAGAIN; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* lower border should be white (0xFF) */ 18762306a36Sopenharmony_ci for (bytes_read = IMGSIZE-WIDTH; bytes_read < IMGSIZE-1; bytes_read++) 18862306a36Sopenharmony_ci if (dev->bulk_in_buffer[bytes_read] != 0xFF) 18962306a36Sopenharmony_ci return -EAGAIN; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* reset the device */ 19262306a36Sopenharmony_cireset: 19362306a36Sopenharmony_ci ftip_command(dev, FTIP_RELEASE, 0, 0); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* should be IMGSIZE == 65040 */ 19662306a36Sopenharmony_ci dev_dbg(&dev->interface->dev, "read %d bytes fingerprint data\n", 19762306a36Sopenharmony_ci bytes_read); 19862306a36Sopenharmony_ci return result; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* PM operations are nops as this driver does IO only during open() */ 20262306a36Sopenharmony_cistatic int idmouse_suspend(struct usb_interface *intf, pm_message_t message) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int idmouse_resume(struct usb_interface *intf) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic inline void idmouse_delete(struct usb_idmouse *dev) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci kfree(dev->bulk_in_buffer); 21562306a36Sopenharmony_ci kfree(dev); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int idmouse_open(struct inode *inode, struct file *file) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct usb_idmouse *dev; 22162306a36Sopenharmony_ci struct usb_interface *interface; 22262306a36Sopenharmony_ci int result; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* get the interface from minor number and driver information */ 22562306a36Sopenharmony_ci interface = usb_find_interface(&idmouse_driver, iminor(inode)); 22662306a36Sopenharmony_ci if (!interface) 22762306a36Sopenharmony_ci return -ENODEV; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* get the device information block from the interface */ 23062306a36Sopenharmony_ci dev = usb_get_intfdata(interface); 23162306a36Sopenharmony_ci if (!dev) 23262306a36Sopenharmony_ci return -ENODEV; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* lock this device */ 23562306a36Sopenharmony_ci mutex_lock(&dev->lock); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* check if already open */ 23862306a36Sopenharmony_ci if (dev->open) { 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* already open, so fail */ 24162306a36Sopenharmony_ci result = -EBUSY; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* create a new image and check for success */ 24662306a36Sopenharmony_ci result = usb_autopm_get_interface(interface); 24762306a36Sopenharmony_ci if (result) 24862306a36Sopenharmony_ci goto error; 24962306a36Sopenharmony_ci result = idmouse_create_image(dev); 25062306a36Sopenharmony_ci usb_autopm_put_interface(interface); 25162306a36Sopenharmony_ci if (result) 25262306a36Sopenharmony_ci goto error; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* increment our usage count for the driver */ 25562306a36Sopenharmony_ci ++dev->open; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* save our object in the file's private structure */ 25862306a36Sopenharmony_ci file->private_data = dev; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cierror: 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* unlock this device */ 26562306a36Sopenharmony_ci mutex_unlock(&dev->lock); 26662306a36Sopenharmony_ci return result; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int idmouse_release(struct inode *inode, struct file *file) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct usb_idmouse *dev; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci dev = file->private_data; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (dev == NULL) 27662306a36Sopenharmony_ci return -ENODEV; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* lock our device */ 27962306a36Sopenharmony_ci mutex_lock(&dev->lock); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci --dev->open; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!dev->present) { 28462306a36Sopenharmony_ci /* the device was unplugged before the file was released */ 28562306a36Sopenharmony_ci mutex_unlock(&dev->lock); 28662306a36Sopenharmony_ci idmouse_delete(dev); 28762306a36Sopenharmony_ci } else { 28862306a36Sopenharmony_ci mutex_unlock(&dev->lock); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count, 29462306a36Sopenharmony_ci loff_t * ppos) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct usb_idmouse *dev = file->private_data; 29762306a36Sopenharmony_ci int result; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* lock this object */ 30062306a36Sopenharmony_ci mutex_lock(&dev->lock); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* verify that the device wasn't unplugged */ 30362306a36Sopenharmony_ci if (!dev->present) { 30462306a36Sopenharmony_ci mutex_unlock(&dev->lock); 30562306a36Sopenharmony_ci return -ENODEV; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci result = simple_read_from_buffer(buffer, count, ppos, 30962306a36Sopenharmony_ci dev->bulk_in_buffer, IMGSIZE); 31062306a36Sopenharmony_ci /* unlock the device */ 31162306a36Sopenharmony_ci mutex_unlock(&dev->lock); 31262306a36Sopenharmony_ci return result; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int idmouse_probe(struct usb_interface *interface, 31662306a36Sopenharmony_ci const struct usb_device_id *id) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(interface); 31962306a36Sopenharmony_ci struct usb_idmouse *dev; 32062306a36Sopenharmony_ci struct usb_host_interface *iface_desc; 32162306a36Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 32262306a36Sopenharmony_ci int result; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* check if we have gotten the data or the hid interface */ 32562306a36Sopenharmony_ci iface_desc = interface->cur_altsetting; 32662306a36Sopenharmony_ci if (iface_desc->desc.bInterfaceClass != 0x0A) 32762306a36Sopenharmony_ci return -ENODEV; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (iface_desc->desc.bNumEndpoints < 1) 33062306a36Sopenharmony_ci return -ENODEV; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* allocate memory for our device state and initialize it */ 33362306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 33462306a36Sopenharmony_ci if (dev == NULL) 33562306a36Sopenharmony_ci return -ENOMEM; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci mutex_init(&dev->lock); 33862306a36Sopenharmony_ci dev->udev = udev; 33962306a36Sopenharmony_ci dev->interface = interface; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* set up the endpoint information - use only the first bulk-in endpoint */ 34262306a36Sopenharmony_ci result = usb_find_bulk_in_endpoint(iface_desc, &endpoint); 34362306a36Sopenharmony_ci if (result) { 34462306a36Sopenharmony_ci dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n"); 34562306a36Sopenharmony_ci idmouse_delete(dev); 34662306a36Sopenharmony_ci return result; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci dev->orig_bi_size = usb_endpoint_maxp(endpoint); 35062306a36Sopenharmony_ci dev->bulk_in_size = 0x200; /* works _much_ faster */ 35162306a36Sopenharmony_ci dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; 35262306a36Sopenharmony_ci dev->bulk_in_buffer = kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL); 35362306a36Sopenharmony_ci if (!dev->bulk_in_buffer) { 35462306a36Sopenharmony_ci idmouse_delete(dev); 35562306a36Sopenharmony_ci return -ENOMEM; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* allow device read, write and ioctl */ 35962306a36Sopenharmony_ci dev->present = 1; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* we can register the device now, as it is ready */ 36262306a36Sopenharmony_ci usb_set_intfdata(interface, dev); 36362306a36Sopenharmony_ci result = usb_register_dev(interface, &idmouse_class); 36462306a36Sopenharmony_ci if (result) { 36562306a36Sopenharmony_ci /* something prevented us from registering this device */ 36662306a36Sopenharmony_ci dev_err(&interface->dev, "Unable to allocate minor number.\n"); 36762306a36Sopenharmony_ci idmouse_delete(dev); 36862306a36Sopenharmony_ci return result; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* be noisy */ 37262306a36Sopenharmony_ci dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void idmouse_disconnect(struct usb_interface *interface) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct usb_idmouse *dev = usb_get_intfdata(interface); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* give back our minor */ 38262306a36Sopenharmony_ci usb_deregister_dev(interface, &idmouse_class); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* lock the device */ 38562306a36Sopenharmony_ci mutex_lock(&dev->lock); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* prevent device read, write and ioctl */ 38862306a36Sopenharmony_ci dev->present = 0; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* if the device is opened, idmouse_release will clean this up */ 39162306a36Sopenharmony_ci if (!dev->open) { 39262306a36Sopenharmony_ci mutex_unlock(&dev->lock); 39362306a36Sopenharmony_ci idmouse_delete(dev); 39462306a36Sopenharmony_ci } else { 39562306a36Sopenharmony_ci /* unlock */ 39662306a36Sopenharmony_ci mutex_unlock(&dev->lock); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci dev_info(&interface->dev, "disconnected\n"); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cimodule_usb_driver(idmouse_driver); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 40562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 40662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 40762306a36Sopenharmony_ci 408