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