idmouse.c 11.4 KB
Newer Older
1
/* Siemens ID Mouse driver v0.6
L
Linus Torvalds 已提交
2 3 4 5 6 7 8 9 10 11 12 13

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License as
  published by the Free Software Foundation; either version 2 of
  the License, or (at your option) any later version.

  Copyright (C) 2004-5 by Florian 'Floe' Echtler  <echtler@fs.tum.de>
                      and Andreas  'ad'  Deresch <aderesch@fs.tum.de>

  Derived from the USB Skeleton driver 1.1,
  Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)

14 15 16
  Additional information provided by Martin Reising
  <Martin.Reising@natural-computing.de>

L
Linus Torvalds 已提交
17 18 19 20 21 22 23 24 25
*/

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/completion.h>
26
#include <linux/mutex.h>
L
Linus Torvalds 已提交
27 28 29
#include <asm/uaccess.h>
#include <linux/usb.h>

30
/* image constants */
L
Linus Torvalds 已提交
31
#define WIDTH 225
32 33
#define HEIGHT 289
#define HEADER "P5 225 289 255 "
L
Linus Torvalds 已提交
34 35
#define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1)

36 37
/* version information */
#define DRIVER_VERSION "0.6"
L
Linus Torvalds 已提交
38 39 40 41
#define DRIVER_SHORT   "idmouse"
#define DRIVER_AUTHOR  "Florian 'Floe' Echtler <echtler@fs.tum.de>"
#define DRIVER_DESC    "Siemens ID Mouse FingerTIP Sensor Driver"

42
/* minor number for misc USB devices */
L
Linus Torvalds 已提交
43 44
#define USB_IDMOUSE_MINOR_BASE 132

45 46 47 48 49 50
/* vendor and device IDs */
#define ID_SIEMENS 0x0681
#define ID_IDMOUSE 0x0005
#define ID_CHERRY  0x0010

/* device ID table */
51
static const struct usb_device_id idmouse_table[] = {
52 53 54
	{USB_DEVICE(ID_SIEMENS, ID_IDMOUSE)}, /* Siemens ID Mouse (Professional) */
	{USB_DEVICE(ID_SIEMENS, ID_CHERRY )}, /* Cherry FingerTIP ID Board       */
	{}                                    /* terminating null entry          */
L
Linus Torvalds 已提交
55 56
};

57 58 59 60 61 62 63 64 65 66 67
/* sensor commands */
#define FTIP_RESET   0x20
#define FTIP_ACQUIRE 0x21
#define FTIP_RELEASE 0x22
#define FTIP_BLINK   0x23  /* LSB of value = blink pulse width */
#define FTIP_SCROLL  0x24

#define ftip_command(dev, command, value, index) \
	usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), command, \
	USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000)

L
Linus Torvalds 已提交
68
MODULE_DEVICE_TABLE(usb, idmouse_table);
O
Oliver Neukum 已提交
69
static DEFINE_MUTEX(open_disc_mutex);
L
Linus Torvalds 已提交
70 71 72 73 74 75 76 77

/* structure to hold all of our device specific stuff */
struct usb_idmouse {

	struct usb_device *udev; /* save off the usb device pointer */
	struct usb_interface *interface; /* the interface for this device */

	unsigned char *bulk_in_buffer; /* the buffer to receive data */
78 79
	size_t bulk_in_size; /* the maximum bulk packet size */
	size_t orig_bi_size; /* same as above, but reported by the device */
L
Linus Torvalds 已提交
80 81 82 83
	__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */

	int open; /* if the port is open or not */
	int present; /* if the device is not disconnected */
O
Oliver Neukum 已提交
84
	struct mutex lock; /* locks this structure */
L
Linus Torvalds 已提交
85 86 87 88 89 90 91 92 93 94 95 96 97 98

};

/* local function prototypes */
static ssize_t idmouse_read(struct file *file, char __user *buffer,
				size_t count, loff_t * ppos);

static int idmouse_open(struct inode *inode, struct file *file);
static int idmouse_release(struct inode *inode, struct file *file);

static int idmouse_probe(struct usb_interface *interface,
				const struct usb_device_id *id);

static void idmouse_disconnect(struct usb_interface *interface);
99 100
static int idmouse_suspend(struct usb_interface *intf, pm_message_t message);
static int idmouse_resume(struct usb_interface *intf);
L
Linus Torvalds 已提交
101 102

/* file operation pointers */
103
static const struct file_operations idmouse_fops = {
L
Linus Torvalds 已提交
104 105 106 107
	.owner = THIS_MODULE,
	.read = idmouse_read,
	.open = idmouse_open,
	.release = idmouse_release,
108
	.llseek = default_llseek,
L
Linus Torvalds 已提交
109 110
};

111
/* class driver information */
L
Linus Torvalds 已提交
112
static struct usb_class_driver idmouse_class = {
113
	.name = "idmouse%d",
L
Linus Torvalds 已提交
114 115 116 117 118 119 120 121 122
	.fops = &idmouse_fops,
	.minor_base = USB_IDMOUSE_MINOR_BASE,
};

/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver idmouse_driver = {
	.name = DRIVER_SHORT,
	.probe = idmouse_probe,
	.disconnect = idmouse_disconnect,
123 124 125
	.suspend = idmouse_suspend,
	.resume = idmouse_resume,
	.reset_resume = idmouse_resume,
L
Linus Torvalds 已提交
126
	.id_table = idmouse_table,
127
	.supports_autosuspend = 1,
L
Linus Torvalds 已提交
128 129 130 131
};

static int idmouse_create_image(struct usb_idmouse *dev)
{
M
Mariusz Kozlowski 已提交
132 133 134
	int bytes_read;
	int bulk_read;
	int result;
L
Linus Torvalds 已提交
135

136
	memcpy(dev->bulk_in_buffer, HEADER, sizeof(HEADER)-1);
M
Mariusz Kozlowski 已提交
137
	bytes_read = sizeof(HEADER)-1;
L
Linus Torvalds 已提交
138

139 140
	/* reset the device and set a fast blink rate */
	result = ftip_command(dev, FTIP_RELEASE, 0, 0);
L
Linus Torvalds 已提交
141
	if (result < 0)
142 143
		goto reset;
	result = ftip_command(dev, FTIP_BLINK,   1, 0);
L
Linus Torvalds 已提交
144
	if (result < 0)
145
		goto reset;
L
Linus Torvalds 已提交
146

147 148 149
	/* initialize the sensor - sending this command twice */
	/* significantly reduces the rate of failed reads     */
	result = ftip_command(dev, FTIP_ACQUIRE, 0, 0);
L
Linus Torvalds 已提交
150
	if (result < 0)
151 152
		goto reset;
	result = ftip_command(dev, FTIP_ACQUIRE, 0, 0);
L
Linus Torvalds 已提交
153
	if (result < 0)
154 155 156 157 158
		goto reset;

	/* start the readout - sending this command twice */
	/* presumably enables the high dynamic range mode */
	result = ftip_command(dev, FTIP_RESET,   0, 0);
L
Linus Torvalds 已提交
159
	if (result < 0)
160 161 162 163
		goto reset;
	result = ftip_command(dev, FTIP_RESET,   0, 0);
	if (result < 0)
		goto reset;
L
Linus Torvalds 已提交
164 165 166 167 168 169 170

	/* loop over a blocking bulk read to get data from the device */
	while (bytes_read < IMGSIZE) {
		result = usb_bulk_msg (dev->udev,
				usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr),
				dev->bulk_in_buffer + bytes_read,
				dev->bulk_in_size, &bulk_read, 5000);
171 172 173 174 175 176 177 178 179 180 181 182 183
		if (result < 0) {
			/* Maybe this error was caused by the increased packet size? */
			/* Reset to the original value and tell userspace to retry.  */
			if (dev->bulk_in_size != dev->orig_bi_size) {
				dev->bulk_in_size = dev->orig_bi_size;
				result = -EAGAIN;
			}
			break;
		}
		if (signal_pending(current)) {
			result = -EINTR;
			break;
		}
L
Linus Torvalds 已提交
184 185 186 187
		bytes_read += bulk_read;
	}

	/* reset the device */
188 189 190 191 192 193 194 195
reset:
	ftip_command(dev, FTIP_RELEASE, 0, 0);

	/* check for valid image */
	/* right border should be black (0x00) */
	for (bytes_read = sizeof(HEADER)-1 + WIDTH-1; bytes_read < IMGSIZE; bytes_read += WIDTH)
		if (dev->bulk_in_buffer[bytes_read] != 0x00)
			return -EAGAIN;
L
Linus Torvalds 已提交
196

197 198 199 200 201 202
	/* lower border should be white (0xFF) */
	for (bytes_read = IMGSIZE-WIDTH; bytes_read < IMGSIZE-1; bytes_read++)
		if (dev->bulk_in_buffer[bytes_read] != 0xFF)
			return -EAGAIN;

	/* should be IMGSIZE == 65040 */
L
Linus Torvalds 已提交
203
	dbg("read %d bytes fingerprint data", bytes_read);
204
	return result;
L
Linus Torvalds 已提交
205 206
}

207 208 209 210 211 212 213 214 215 216 217
/* PM operations are nops as this driver does IO only during open() */
static int idmouse_suspend(struct usb_interface *intf, pm_message_t message)
{
	return 0;
}

static int idmouse_resume(struct usb_interface *intf)
{
	return 0;
}

L
Linus Torvalds 已提交
218 219 220 221 222 223 224 225
static inline void idmouse_delete(struct usb_idmouse *dev)
{
	kfree(dev->bulk_in_buffer);
	kfree(dev);
}

static int idmouse_open(struct inode *inode, struct file *file)
{
M
Mariusz Kozlowski 已提交
226
	struct usb_idmouse *dev;
L
Linus Torvalds 已提交
227
	struct usb_interface *interface;
M
Mariusz Kozlowski 已提交
228
	int result;
L
Linus Torvalds 已提交
229 230 231

	/* get the interface from minor number and driver information */
	interface = usb_find_interface (&idmouse_driver, iminor (inode));
O
Oliver Neukum 已提交
232
	if (!interface)
L
Linus Torvalds 已提交
233
		return -ENODEV;
234

O
Oliver Neukum 已提交
235
	mutex_lock(&open_disc_mutex);
L
Linus Torvalds 已提交
236 237
	/* get the device information block from the interface */
	dev = usb_get_intfdata(interface);
O
Oliver Neukum 已提交
238 239
	if (!dev) {
		mutex_unlock(&open_disc_mutex);
L
Linus Torvalds 已提交
240
		return -ENODEV;
O
Oliver Neukum 已提交
241
	}
L
Linus Torvalds 已提交
242 243

	/* lock this device */
O
Oliver Neukum 已提交
244 245
	mutex_lock(&dev->lock);
	mutex_unlock(&open_disc_mutex);
L
Linus Torvalds 已提交
246 247 248 249 250 251 252 253 254 255

	/* check if already open */
	if (dev->open) {

		/* already open, so fail */
		result = -EBUSY;

	} else {

		/* create a new image and check for success */
256 257 258
		result = usb_autopm_get_interface(interface);
		if (result)
			goto error;
L
Linus Torvalds 已提交
259 260 261
		result = idmouse_create_image (dev);
		if (result)
			goto error;
262
		usb_autopm_put_interface(interface);
L
Linus Torvalds 已提交
263 264 265 266 267 268 269 270 271 272 273 274

		/* increment our usage count for the driver */
		++dev->open;

		/* save our object in the file's private structure */
		file->private_data = dev;

	} 

error:

	/* unlock this device */
O
Oliver Neukum 已提交
275
	mutex_unlock(&dev->lock);
L
Linus Torvalds 已提交
276 277 278 279 280 281 282
	return result;
}

static int idmouse_release(struct inode *inode, struct file *file)
{
	struct usb_idmouse *dev;

283
	dev = file->private_data;
L
Linus Torvalds 已提交
284

285
	if (dev == NULL)
L
Linus Torvalds 已提交
286 287
		return -ENODEV;

O
Oliver Neukum 已提交
288
	mutex_lock(&open_disc_mutex);
L
Linus Torvalds 已提交
289
	/* lock our device */
O
Oliver Neukum 已提交
290
	mutex_lock(&dev->lock);
L
Linus Torvalds 已提交
291 292 293

	/* are we really open? */
	if (dev->open <= 0) {
O
Oliver Neukum 已提交
294 295
		mutex_unlock(&dev->lock);
		mutex_unlock(&open_disc_mutex);
L
Linus Torvalds 已提交
296 297 298 299 300 301 302
		return -ENODEV;
	}

	--dev->open;

	if (!dev->present) {
		/* the device was unplugged before the file was released */
O
Oliver Neukum 已提交
303 304
		mutex_unlock(&dev->lock);
		mutex_unlock(&open_disc_mutex);
L
Linus Torvalds 已提交
305
		idmouse_delete(dev);
306
	} else {
O
Oliver Neukum 已提交
307 308
		mutex_unlock(&dev->lock);
		mutex_unlock(&open_disc_mutex);
L
Linus Torvalds 已提交
309 310 311 312 313 314 315
	}
	return 0;
}

static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count,
				loff_t * ppos)
{
316
	struct usb_idmouse *dev = file->private_data;
M
Mariusz Kozlowski 已提交
317
	int result;
L
Linus Torvalds 已提交
318

319
	/* lock this object */
O
Oliver Neukum 已提交
320
	mutex_lock(&dev->lock);
L
Linus Torvalds 已提交
321

322
	/* verify that the device wasn't unplugged */
L
Linus Torvalds 已提交
323
	if (!dev->present) {
O
Oliver Neukum 已提交
324
		mutex_unlock(&dev->lock);
L
Linus Torvalds 已提交
325 326 327
		return -ENODEV;
	}

A
Al Viro 已提交
328 329
	result = simple_read_from_buffer(buffer, count, ppos,
					dev->bulk_in_buffer, IMGSIZE);
330
	/* unlock the device */
O
Oliver Neukum 已提交
331
	mutex_unlock(&dev->lock);
L
Linus Torvalds 已提交
332 333 334 335 336 337 338
	return result;
}

static int idmouse_probe(struct usb_interface *interface,
				const struct usb_device_id *id)
{
	struct usb_device *udev = interface_to_usbdev(interface);
M
Mariusz Kozlowski 已提交
339
	struct usb_idmouse *dev;
L
Linus Torvalds 已提交
340 341 342 343 344 345 346 347 348 349
	struct usb_host_interface *iface_desc;
	struct usb_endpoint_descriptor *endpoint;
	int result;

	/* check if we have gotten the data or the hid interface */
	iface_desc = &interface->altsetting[0];
	if (iface_desc->desc.bInterfaceClass != 0x0A)
		return -ENODEV;

	/* allocate memory for our device state and initialize it */
O
Oliver Neukum 已提交
350
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
L
Linus Torvalds 已提交
351 352 353
	if (dev == NULL)
		return -ENOMEM;

O
Oliver Neukum 已提交
354
	mutex_init(&dev->lock);
L
Linus Torvalds 已提交
355 356 357 358 359
	dev->udev = udev;
	dev->interface = interface;

	/* set up the endpoint information - use only the first bulk-in endpoint */
	endpoint = &iface_desc->endpoint[0].desc;
360
	if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) {
L
Linus Torvalds 已提交
361
		/* we found a bulk in endpoint */
362
		dev->orig_bi_size = usb_endpoint_maxp(endpoint);
363
		dev->bulk_in_size = 0x200; /* works _much_ faster */
L
Linus Torvalds 已提交
364 365
		dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
		dev->bulk_in_buffer =
366
			kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL);
L
Linus Torvalds 已提交
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409

		if (!dev->bulk_in_buffer) {
			err("Unable to allocate input buffer.");
			idmouse_delete(dev);
			return -ENOMEM;
		}
	}

	if (!(dev->bulk_in_endpointAddr)) {
		err("Unable to find bulk-in endpoint.");
		idmouse_delete(dev);
		return -ENODEV;
	}
	/* allow device read, write and ioctl */
	dev->present = 1;

	/* we can register the device now, as it is ready */
	usb_set_intfdata(interface, dev);
	result = usb_register_dev(interface, &idmouse_class);
	if (result) {
		/* something prevented us from registering this device */
		err("Unble to allocate minor number.");
		usb_set_intfdata(interface, NULL);
		idmouse_delete(dev);
		return result;
	}

	/* be noisy */
	dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);

	return 0;
}

static void idmouse_disconnect(struct usb_interface *interface)
{
	struct usb_idmouse *dev;

	/* get device structure */
	dev = usb_get_intfdata(interface);

	/* give back our minor */
	usb_deregister_dev(interface, &idmouse_class);

O
Oliver Neukum 已提交
410 411 412 413 414
	mutex_lock(&open_disc_mutex);
	usb_set_intfdata(interface, NULL);
	/* lock the device */
	mutex_lock(&dev->lock);
	mutex_unlock(&open_disc_mutex);
415

L
Linus Torvalds 已提交
416 417 418 419
	/* prevent device read, write and ioctl */
	dev->present = 0;

	/* if the device is opened, idmouse_release will clean this up */
420
	if (!dev->open) {
O
Oliver Neukum 已提交
421
		mutex_unlock(&dev->lock);
L
Linus Torvalds 已提交
422
		idmouse_delete(dev);
423 424
	} else {
		/* unlock */
O
Oliver Neukum 已提交
425
		mutex_unlock(&dev->lock);
426
	}
L
Linus Torvalds 已提交
427

428
	dev_info(&interface->dev, "disconnected\n");
L
Linus Torvalds 已提交
429 430
}

431
module_usb_driver(idmouse_driver);
L
Linus Torvalds 已提交
432 433 434 435 436

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");