diff options
Diffstat (limited to 'drivers/usb/misc/idmouse.c')
-rw-r--r-- | drivers/usb/misc/idmouse.c | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c new file mode 100644 index 000000000000..ce030d1f1c1f --- /dev/null +++ b/drivers/usb/misc/idmouse.c | |||
@@ -0,0 +1,443 @@ | |||
1 | /* Siemens ID Mouse driver v0.5 | ||
2 | |||
3 | This program is free software; you can redistribute it and/or | ||
4 | modify it under the terms of the GNU General Public License as | ||
5 | published by the Free Software Foundation; either version 2 of | ||
6 | the License, or (at your option) any later version. | ||
7 | |||
8 | Copyright (C) 2004-5 by Florian 'Floe' Echtler <echtler@fs.tum.de> | ||
9 | and Andreas 'ad' Deresch <aderesch@fs.tum.de> | ||
10 | |||
11 | Derived from the USB Skeleton driver 1.1, | ||
12 | Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com) | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/smp_lock.h> | ||
24 | #include <linux/completion.h> | ||
25 | #include <asm/uaccess.h> | ||
26 | #include <linux/usb.h> | ||
27 | |||
28 | #define WIDTH 225 | ||
29 | #define HEIGHT 288 | ||
30 | #define HEADER "P5 225 288 255 " | ||
31 | #define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1) | ||
32 | |||
33 | /* Version Information */ | ||
34 | #define DRIVER_VERSION "0.5" | ||
35 | #define DRIVER_SHORT "idmouse" | ||
36 | #define DRIVER_AUTHOR "Florian 'Floe' Echtler <echtler@fs.tum.de>" | ||
37 | #define DRIVER_DESC "Siemens ID Mouse FingerTIP Sensor Driver" | ||
38 | |||
39 | /* Siemens ID Mouse */ | ||
40 | #define USB_IDMOUSE_VENDOR_ID 0x0681 | ||
41 | #define USB_IDMOUSE_PRODUCT_ID 0x0005 | ||
42 | |||
43 | /* we still need a minor number */ | ||
44 | #define USB_IDMOUSE_MINOR_BASE 132 | ||
45 | |||
46 | static struct usb_device_id idmouse_table[] = { | ||
47 | {USB_DEVICE(USB_IDMOUSE_VENDOR_ID, USB_IDMOUSE_PRODUCT_ID)}, | ||
48 | {} /* null entry at the end */ | ||
49 | }; | ||
50 | |||
51 | MODULE_DEVICE_TABLE(usb, idmouse_table); | ||
52 | |||
53 | /* structure to hold all of our device specific stuff */ | ||
54 | struct usb_idmouse { | ||
55 | |||
56 | struct usb_device *udev; /* save off the usb device pointer */ | ||
57 | struct usb_interface *interface; /* the interface for this device */ | ||
58 | |||
59 | unsigned char *bulk_in_buffer; /* the buffer to receive data */ | ||
60 | size_t bulk_in_size; /* the size of the receive buffer */ | ||
61 | __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ | ||
62 | |||
63 | int open; /* if the port is open or not */ | ||
64 | int present; /* if the device is not disconnected */ | ||
65 | struct semaphore sem; /* locks this structure */ | ||
66 | |||
67 | }; | ||
68 | |||
69 | /* local function prototypes */ | ||
70 | static ssize_t idmouse_read(struct file *file, char __user *buffer, | ||
71 | size_t count, loff_t * ppos); | ||
72 | |||
73 | static int idmouse_open(struct inode *inode, struct file *file); | ||
74 | static int idmouse_release(struct inode *inode, struct file *file); | ||
75 | |||
76 | static int idmouse_probe(struct usb_interface *interface, | ||
77 | const struct usb_device_id *id); | ||
78 | |||
79 | static void idmouse_disconnect(struct usb_interface *interface); | ||
80 | |||
81 | /* file operation pointers */ | ||
82 | static struct file_operations idmouse_fops = { | ||
83 | .owner = THIS_MODULE, | ||
84 | .read = idmouse_read, | ||
85 | .open = idmouse_open, | ||
86 | .release = idmouse_release, | ||
87 | }; | ||
88 | |||
89 | /* class driver information for devfs */ | ||
90 | static struct usb_class_driver idmouse_class = { | ||
91 | .name = "usb/idmouse%d", | ||
92 | .fops = &idmouse_fops, | ||
93 | .mode = S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH, /* filemode (char, 444) */ | ||
94 | .minor_base = USB_IDMOUSE_MINOR_BASE, | ||
95 | }; | ||
96 | |||
97 | /* usb specific object needed to register this driver with the usb subsystem */ | ||
98 | static struct usb_driver idmouse_driver = { | ||
99 | .owner = THIS_MODULE, | ||
100 | .name = DRIVER_SHORT, | ||
101 | .probe = idmouse_probe, | ||
102 | .disconnect = idmouse_disconnect, | ||
103 | .id_table = idmouse_table, | ||
104 | }; | ||
105 | |||
106 | // prevent races between open() and disconnect() | ||
107 | static DECLARE_MUTEX(disconnect_sem); | ||
108 | |||
109 | static int idmouse_create_image(struct usb_idmouse *dev) | ||
110 | { | ||
111 | int bytes_read = 0; | ||
112 | int bulk_read = 0; | ||
113 | int result = 0; | ||
114 | |||
115 | if (dev->bulk_in_size < sizeof(HEADER)) | ||
116 | return -ENOMEM; | ||
117 | |||
118 | memcpy(dev->bulk_in_buffer,HEADER,sizeof(HEADER)-1); | ||
119 | bytes_read += sizeof(HEADER)-1; | ||
120 | |||
121 | /* Dump the setup packets. Yes, they are uncommented, simply | ||
122 | because they were sniffed under Windows using SnoopyPro. | ||
123 | I _guess_ that 0x22 is a kind of reset command and 0x21 | ||
124 | means init.. | ||
125 | */ | ||
126 | result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), | ||
127 | 0x21, 0x42, 0x0001, 0x0002, NULL, 0, 1000); | ||
128 | if (result < 0) | ||
129 | return result; | ||
130 | result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), | ||
131 | 0x20, 0x42, 0x0001, 0x0002, NULL, 0, 1000); | ||
132 | if (result < 0) | ||
133 | return result; | ||
134 | result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), | ||
135 | 0x22, 0x42, 0x0000, 0x0002, NULL, 0, 1000); | ||
136 | if (result < 0) | ||
137 | return result; | ||
138 | |||
139 | result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), | ||
140 | 0x21, 0x42, 0x0001, 0x0002, NULL, 0, 1000); | ||
141 | if (result < 0) | ||
142 | return result; | ||
143 | result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), | ||
144 | 0x20, 0x42, 0x0001, 0x0002, NULL, 0, 1000); | ||
145 | if (result < 0) | ||
146 | return result; | ||
147 | result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), | ||
148 | 0x20, 0x42, 0x0000, 0x0002, NULL, 0, 1000); | ||
149 | if (result < 0) | ||
150 | return result; | ||
151 | |||
152 | /* loop over a blocking bulk read to get data from the device */ | ||
153 | while (bytes_read < IMGSIZE) { | ||
154 | result = usb_bulk_msg (dev->udev, | ||
155 | usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr), | ||
156 | dev->bulk_in_buffer + bytes_read, | ||
157 | dev->bulk_in_size, &bulk_read, 5000); | ||
158 | if (result < 0) | ||
159 | return result; | ||
160 | if (signal_pending(current)) | ||
161 | return -EINTR; | ||
162 | bytes_read += bulk_read; | ||
163 | } | ||
164 | |||
165 | /* reset the device */ | ||
166 | result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), | ||
167 | 0x22, 0x42, 0x0000, 0x0002, NULL, 0, 1000); | ||
168 | if (result < 0) | ||
169 | return result; | ||
170 | |||
171 | /* should be IMGSIZE == 64815 */ | ||
172 | dbg("read %d bytes fingerprint data", bytes_read); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static inline void idmouse_delete(struct usb_idmouse *dev) | ||
177 | { | ||
178 | kfree(dev->bulk_in_buffer); | ||
179 | kfree(dev); | ||
180 | } | ||
181 | |||
182 | static int idmouse_open(struct inode *inode, struct file *file) | ||
183 | { | ||
184 | struct usb_idmouse *dev = NULL; | ||
185 | struct usb_interface *interface; | ||
186 | int result = 0; | ||
187 | |||
188 | /* prevent disconnects */ | ||
189 | down(&disconnect_sem); | ||
190 | |||
191 | /* get the interface from minor number and driver information */ | ||
192 | interface = usb_find_interface (&idmouse_driver, iminor (inode)); | ||
193 | if (!interface) { | ||
194 | up(&disconnect_sem); | ||
195 | return -ENODEV; | ||
196 | } | ||
197 | /* get the device information block from the interface */ | ||
198 | dev = usb_get_intfdata(interface); | ||
199 | if (!dev) { | ||
200 | up(&disconnect_sem); | ||
201 | return -ENODEV; | ||
202 | } | ||
203 | |||
204 | /* lock this device */ | ||
205 | down(&dev->sem); | ||
206 | |||
207 | /* check if already open */ | ||
208 | if (dev->open) { | ||
209 | |||
210 | /* already open, so fail */ | ||
211 | result = -EBUSY; | ||
212 | |||
213 | } else { | ||
214 | |||
215 | /* create a new image and check for success */ | ||
216 | result = idmouse_create_image (dev); | ||
217 | if (result) | ||
218 | goto error; | ||
219 | |||
220 | /* increment our usage count for the driver */ | ||
221 | ++dev->open; | ||
222 | |||
223 | /* save our object in the file's private structure */ | ||
224 | file->private_data = dev; | ||
225 | |||
226 | } | ||
227 | |||
228 | error: | ||
229 | |||
230 | /* unlock this device */ | ||
231 | up(&dev->sem); | ||
232 | |||
233 | /* unlock the disconnect semaphore */ | ||
234 | up(&disconnect_sem); | ||
235 | return result; | ||
236 | } | ||
237 | |||
238 | static int idmouse_release(struct inode *inode, struct file *file) | ||
239 | { | ||
240 | struct usb_idmouse *dev; | ||
241 | |||
242 | /* prevent a race condition with open() */ | ||
243 | down(&disconnect_sem); | ||
244 | |||
245 | dev = (struct usb_idmouse *) file->private_data; | ||
246 | |||
247 | if (dev == NULL) { | ||
248 | up(&disconnect_sem); | ||
249 | return -ENODEV; | ||
250 | } | ||
251 | |||
252 | /* lock our device */ | ||
253 | down(&dev->sem); | ||
254 | |||
255 | /* are we really open? */ | ||
256 | if (dev->open <= 0) { | ||
257 | up(&dev->sem); | ||
258 | up(&disconnect_sem); | ||
259 | return -ENODEV; | ||
260 | } | ||
261 | |||
262 | --dev->open; | ||
263 | |||
264 | if (!dev->present) { | ||
265 | /* the device was unplugged before the file was released */ | ||
266 | up(&dev->sem); | ||
267 | idmouse_delete(dev); | ||
268 | up(&disconnect_sem); | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | up(&dev->sem); | ||
273 | up(&disconnect_sem); | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count, | ||
278 | loff_t * ppos) | ||
279 | { | ||
280 | struct usb_idmouse *dev; | ||
281 | int result = 0; | ||
282 | |||
283 | dev = (struct usb_idmouse *) file->private_data; | ||
284 | |||
285 | // lock this object | ||
286 | down (&dev->sem); | ||
287 | |||
288 | // verify that the device wasn't unplugged | ||
289 | if (!dev->present) { | ||
290 | up (&dev->sem); | ||
291 | return -ENODEV; | ||
292 | } | ||
293 | |||
294 | if (*ppos >= IMGSIZE) { | ||
295 | up (&dev->sem); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | if (count > IMGSIZE - *ppos) | ||
300 | count = IMGSIZE - *ppos; | ||
301 | |||
302 | if (copy_to_user (buffer, dev->bulk_in_buffer + *ppos, count)) { | ||
303 | result = -EFAULT; | ||
304 | } else { | ||
305 | result = count; | ||
306 | *ppos += count; | ||
307 | } | ||
308 | |||
309 | // unlock the device | ||
310 | up(&dev->sem); | ||
311 | return result; | ||
312 | } | ||
313 | |||
314 | static int idmouse_probe(struct usb_interface *interface, | ||
315 | const struct usb_device_id *id) | ||
316 | { | ||
317 | struct usb_device *udev = interface_to_usbdev(interface); | ||
318 | struct usb_idmouse *dev = NULL; | ||
319 | struct usb_host_interface *iface_desc; | ||
320 | struct usb_endpoint_descriptor *endpoint; | ||
321 | size_t buffer_size; | ||
322 | int result; | ||
323 | |||
324 | /* check if we have gotten the data or the hid interface */ | ||
325 | iface_desc = &interface->altsetting[0]; | ||
326 | if (iface_desc->desc.bInterfaceClass != 0x0A) | ||
327 | return -ENODEV; | ||
328 | |||
329 | /* allocate memory for our device state and initialize it */ | ||
330 | dev = kmalloc(sizeof(*dev), GFP_KERNEL); | ||
331 | if (dev == NULL) | ||
332 | return -ENOMEM; | ||
333 | memset(dev, 0x00, sizeof(*dev)); | ||
334 | |||
335 | init_MUTEX(&dev->sem); | ||
336 | dev->udev = udev; | ||
337 | dev->interface = interface; | ||
338 | |||
339 | /* set up the endpoint information - use only the first bulk-in endpoint */ | ||
340 | endpoint = &iface_desc->endpoint[0].desc; | ||
341 | if (!dev->bulk_in_endpointAddr | ||
342 | && (endpoint->bEndpointAddress & USB_DIR_IN) | ||
343 | && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == | ||
344 | USB_ENDPOINT_XFER_BULK)) { | ||
345 | |||
346 | /* we found a bulk in endpoint */ | ||
347 | buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); | ||
348 | dev->bulk_in_size = buffer_size; | ||
349 | dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; | ||
350 | dev->bulk_in_buffer = | ||
351 | kmalloc(IMGSIZE + buffer_size, GFP_KERNEL); | ||
352 | |||
353 | if (!dev->bulk_in_buffer) { | ||
354 | err("Unable to allocate input buffer."); | ||
355 | idmouse_delete(dev); | ||
356 | return -ENOMEM; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | if (!(dev->bulk_in_endpointAddr)) { | ||
361 | err("Unable to find bulk-in endpoint."); | ||
362 | idmouse_delete(dev); | ||
363 | return -ENODEV; | ||
364 | } | ||
365 | /* allow device read, write and ioctl */ | ||
366 | dev->present = 1; | ||
367 | |||
368 | /* we can register the device now, as it is ready */ | ||
369 | usb_set_intfdata(interface, dev); | ||
370 | result = usb_register_dev(interface, &idmouse_class); | ||
371 | if (result) { | ||
372 | /* something prevented us from registering this device */ | ||
373 | err("Unble to allocate minor number."); | ||
374 | usb_set_intfdata(interface, NULL); | ||
375 | idmouse_delete(dev); | ||
376 | return result; | ||
377 | } | ||
378 | |||
379 | /* be noisy */ | ||
380 | dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC); | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static void idmouse_disconnect(struct usb_interface *interface) | ||
386 | { | ||
387 | struct usb_idmouse *dev; | ||
388 | |||
389 | /* prevent races with open() */ | ||
390 | down(&disconnect_sem); | ||
391 | |||
392 | /* get device structure */ | ||
393 | dev = usb_get_intfdata(interface); | ||
394 | usb_set_intfdata(interface, NULL); | ||
395 | |||
396 | /* lock it */ | ||
397 | down(&dev->sem); | ||
398 | |||
399 | /* give back our minor */ | ||
400 | usb_deregister_dev(interface, &idmouse_class); | ||
401 | |||
402 | /* prevent device read, write and ioctl */ | ||
403 | dev->present = 0; | ||
404 | |||
405 | /* unlock */ | ||
406 | up(&dev->sem); | ||
407 | |||
408 | /* if the device is opened, idmouse_release will clean this up */ | ||
409 | if (!dev->open) | ||
410 | idmouse_delete(dev); | ||
411 | |||
412 | up(&disconnect_sem); | ||
413 | |||
414 | info("%s disconnected", DRIVER_DESC); | ||
415 | } | ||
416 | |||
417 | static int __init usb_idmouse_init(void) | ||
418 | { | ||
419 | int result; | ||
420 | |||
421 | info(DRIVER_DESC " " DRIVER_VERSION); | ||
422 | |||
423 | /* register this driver with the USB subsystem */ | ||
424 | result = usb_register(&idmouse_driver); | ||
425 | if (result) | ||
426 | err("Unable to register device (error %d).", result); | ||
427 | |||
428 | return result; | ||
429 | } | ||
430 | |||
431 | static void __exit usb_idmouse_exit(void) | ||
432 | { | ||
433 | /* deregister this driver with the USB subsystem */ | ||
434 | usb_deregister(&idmouse_driver); | ||
435 | } | ||
436 | |||
437 | module_init(usb_idmouse_init); | ||
438 | module_exit(usb_idmouse_exit); | ||
439 | |||
440 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
441 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
442 | MODULE_LICENSE("GPL"); | ||
443 | |||