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