OLD | NEW |
| (Empty) |
1 /* | |
2 * libusb example program to manipulate U.are.U 4000B fingerprint scanner. | |
3 * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> | |
4 * | |
5 * Basic image capture program only, does not consider the powerup quirks or | |
6 * the fact that image encryption may be enabled. Not expected to work | |
7 * flawlessly all of the time. | |
8 * | |
9 * This library is free software; you can redistribute it and/or | |
10 * modify it under the terms of the GNU Lesser General Public | |
11 * License as published by the Free Software Foundation; either | |
12 * version 2.1 of the License, or (at your option) any later version. | |
13 * | |
14 * This library is distributed in the hope that it will be useful, | |
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 * Lesser General Public License for more details. | |
18 * | |
19 * You should have received a copy of the GNU Lesser General Public | |
20 * License along with this library; if not, write to the Free Software | |
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
22 */ | |
23 | |
24 #include <errno.h> | |
25 #include <pthread.h> | |
26 #include <signal.h> | |
27 #include <string.h> | |
28 #include <stdio.h> | |
29 #include <stdlib.h> | |
30 | |
31 #include <libusb.h> | |
32 | |
33 #define EP_INTR (1 | LIBUSB_ENDPOINT_IN) | |
34 #define EP_DATA (2 | LIBUSB_ENDPOINT_IN) | |
35 #define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN
) | |
36 #define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OU
T) | |
37 #define USB_RQ 0x04 | |
38 #define INTR_LENGTH 64 | |
39 | |
40 enum { | |
41 MODE_INIT = 0x00, | |
42 MODE_AWAIT_FINGER_ON = 0x10, | |
43 MODE_AWAIT_FINGER_OFF = 0x12, | |
44 MODE_CAPTURE = 0x20, | |
45 MODE_SHUT_UP = 0x30, | |
46 MODE_READY = 0x80, | |
47 }; | |
48 | |
49 static int next_state(void); | |
50 | |
51 enum { | |
52 STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1, | |
53 STATE_AWAIT_IRQ_FINGER_DETECTED, | |
54 STATE_AWAIT_MODE_CHANGE_CAPTURE, | |
55 STATE_AWAIT_IMAGE, | |
56 STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF, | |
57 STATE_AWAIT_IRQ_FINGER_REMOVED, | |
58 }; | |
59 | |
60 static int state = 0; | |
61 static struct libusb_device_handle *devh = NULL; | |
62 static unsigned char imgbuf[0x1b340]; | |
63 static unsigned char irqbuf[INTR_LENGTH]; | |
64 static struct libusb_transfer *img_transfer = NULL; | |
65 static struct libusb_transfer *irq_transfer = NULL; | |
66 static int img_idx = 0; | |
67 static int do_exit = 0; | |
68 | |
69 static pthread_t poll_thread; | |
70 static pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER; | |
71 static pthread_mutex_t exit_cond_lock = PTHREAD_MUTEX_INITIALIZER; | |
72 | |
73 static void request_exit(int code) | |
74 { | |
75 do_exit = code; | |
76 pthread_cond_signal(&exit_cond); | |
77 } | |
78 | |
79 static void *poll_thread_main(void *arg) | |
80 { | |
81 int r = 0; | |
82 printf("poll thread running\n"); | |
83 | |
84 while (!do_exit) { | |
85 struct timeval tv = { 1, 0 }; | |
86 r = libusb_handle_events_timeout(NULL, &tv); | |
87 if (r < 0) { | |
88 request_exit(2); | |
89 break; | |
90 } | |
91 } | |
92 | |
93 printf("poll thread shutting down\n"); | |
94 return NULL; | |
95 } | |
96 | |
97 static int find_dpfp_device(void) | |
98 { | |
99 devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a); | |
100 return devh ? 0 : -EIO; | |
101 } | |
102 | |
103 static int print_f0_data(void) | |
104 { | |
105 unsigned char data[0x10]; | |
106 int r; | |
107 unsigned int i; | |
108 | |
109 r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data, | |
110 sizeof(data), 0); | |
111 if (r < 0) { | |
112 fprintf(stderr, "F0 error %d\n", r); | |
113 return r; | |
114 } | |
115 if ((unsigned int) r < sizeof(data)) { | |
116 fprintf(stderr, "short read (%d)\n", r); | |
117 return -1; | |
118 } | |
119 | |
120 printf("F0 data:"); | |
121 for (i = 0; i < sizeof(data); i++) | |
122 printf("%02x ", data[i]); | |
123 printf("\n"); | |
124 return 0; | |
125 } | |
126 | |
127 static int get_hwstat(unsigned char *status) | |
128 { | |
129 int r; | |
130 | |
131 r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0
); | |
132 if (r < 0) { | |
133 fprintf(stderr, "read hwstat error %d\n", r); | |
134 return r; | |
135 } | |
136 if ((unsigned int) r < 1) { | |
137 fprintf(stderr, "short read (%d)\n", r); | |
138 return -1; | |
139 } | |
140 | |
141 printf("hwstat reads %02x\n", *status); | |
142 return 0; | |
143 } | |
144 | |
145 static int set_hwstat(unsigned char data) | |
146 { | |
147 int r; | |
148 | |
149 printf("set hwstat to %02x\n", data); | |
150 r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0
); | |
151 if (r < 0) { | |
152 fprintf(stderr, "set hwstat error %d\n", r); | |
153 return r; | |
154 } | |
155 if ((unsigned int) r < 1) { | |
156 fprintf(stderr, "short write (%d)", r); | |
157 return -1; | |
158 } | |
159 | |
160 return 0; | |
161 } | |
162 | |
163 static int set_mode(unsigned char data) | |
164 { | |
165 int r; | |
166 printf("set mode %02x\n", data); | |
167 | |
168 r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0
); | |
169 if (r < 0) { | |
170 fprintf(stderr, "set mode error %d\n", r); | |
171 return r; | |
172 } | |
173 if ((unsigned int) r < 1) { | |
174 fprintf(stderr, "short write (%d)", r); | |
175 return -1; | |
176 } | |
177 | |
178 return 0; | |
179 } | |
180 | |
181 static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) | |
182 { | |
183 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { | |
184 fprintf(stderr, "mode change transfer not completed!\n"); | |
185 request_exit(2); | |
186 } | |
187 | |
188 printf("async cb_mode_changed length=%d actual_length=%d\n", | |
189 transfer->length, transfer->actual_length); | |
190 if (next_state() < 0) | |
191 request_exit(2); | |
192 } | |
193 | |
194 static int set_mode_async(unsigned char data) | |
195 { | |
196 unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); | |
197 struct libusb_transfer *transfer; | |
198 | |
199 if (!buf) | |
200 return -ENOMEM; | |
201 | |
202 transfer = libusb_alloc_transfer(0); | |
203 if (!transfer) { | |
204 free(buf); | |
205 return -ENOMEM; | |
206 } | |
207 | |
208 printf("async set mode %02x\n", data); | |
209 libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1); | |
210 buf[LIBUSB_CONTROL_SETUP_SIZE] = data; | |
211 libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL, | |
212 1000); | |
213 | |
214 transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK | |
215 | LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; | |
216 return libusb_submit_transfer(transfer); | |
217 } | |
218 | |
219 static int do_sync_intr(unsigned char *data) | |
220 { | |
221 int r; | |
222 int transferred; | |
223 | |
224 r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH, | |
225 &transferred, 1000); | |
226 if (r < 0) { | |
227 fprintf(stderr, "intr error %d\n", r); | |
228 return r; | |
229 } | |
230 if (transferred < INTR_LENGTH) { | |
231 fprintf(stderr, "short read (%d)\n", r); | |
232 return -1; | |
233 } | |
234 | |
235 printf("recv interrupt %04x\n", *((uint16_t *) data)); | |
236 return 0; | |
237 } | |
238 | |
239 static int sync_intr(unsigned char type) | |
240 { | |
241 int r; | |
242 unsigned char data[INTR_LENGTH]; | |
243 | |
244 while (1) { | |
245 r = do_sync_intr(data); | |
246 if (r < 0) | |
247 return r; | |
248 if (data[0] == type) | |
249 return 0; | |
250 } | |
251 } | |
252 | |
253 static int save_to_file(unsigned char *data) | |
254 { | |
255 FILE *fd; | |
256 char filename[64]; | |
257 | |
258 sprintf(filename, "finger%d.pgm", img_idx++); | |
259 fd = fopen(filename, "w"); | |
260 if (!fd) | |
261 return -1; | |
262 | |
263 fputs("P5 384 289 255 ", fd); | |
264 (void) fwrite(data + 64, 1, 384*289, fd); | |
265 fclose(fd); | |
266 printf("saved image to %s\n", filename); | |
267 return 0; | |
268 } | |
269 | |
270 static int next_state(void) | |
271 { | |
272 int r = 0; | |
273 printf("old state: %d\n", state); | |
274 switch (state) { | |
275 case STATE_AWAIT_IRQ_FINGER_REMOVED: | |
276 state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON; | |
277 r = set_mode_async(MODE_AWAIT_FINGER_ON); | |
278 break; | |
279 case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON: | |
280 state = STATE_AWAIT_IRQ_FINGER_DETECTED; | |
281 break; | |
282 case STATE_AWAIT_IRQ_FINGER_DETECTED: | |
283 state = STATE_AWAIT_MODE_CHANGE_CAPTURE; | |
284 r = set_mode_async(MODE_CAPTURE); | |
285 break; | |
286 case STATE_AWAIT_MODE_CHANGE_CAPTURE: | |
287 state = STATE_AWAIT_IMAGE; | |
288 break; | |
289 case STATE_AWAIT_IMAGE: | |
290 state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF; | |
291 r = set_mode_async(MODE_AWAIT_FINGER_OFF); | |
292 break; | |
293 case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF: | |
294 state = STATE_AWAIT_IRQ_FINGER_REMOVED; | |
295 break; | |
296 default: | |
297 printf("unrecognised state %d\n", state); | |
298 } | |
299 if (r < 0) { | |
300 fprintf(stderr, "error detected changing state\n"); | |
301 return r; | |
302 } | |
303 | |
304 printf("new state: %d\n", state); | |
305 return 0; | |
306 } | |
307 | |
308 static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer) | |
309 { | |
310 unsigned char irqtype = transfer->buffer[0]; | |
311 | |
312 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { | |
313 fprintf(stderr, "irq transfer status %d?\n", transfer->status); | |
314 irq_transfer = NULL; | |
315 request_exit(2); | |
316 return; | |
317 } | |
318 | |
319 printf("IRQ callback %02x\n", irqtype); | |
320 switch (state) { | |
321 case STATE_AWAIT_IRQ_FINGER_DETECTED: | |
322 if (irqtype == 0x01) { | |
323 if (next_state() < 0) { | |
324 request_exit(2); | |
325 return; | |
326 } | |
327 } else { | |
328 printf("finger-on-sensor detected in wrong state!\n"); | |
329 } | |
330 break; | |
331 case STATE_AWAIT_IRQ_FINGER_REMOVED: | |
332 if (irqtype == 0x02) { | |
333 if (next_state() < 0) { | |
334 request_exit(2); | |
335 return; | |
336 } | |
337 } else { | |
338 printf("finger-on-sensor detected in wrong state!\n"); | |
339 } | |
340 break; | |
341 } | |
342 if (libusb_submit_transfer(irq_transfer) < 0) | |
343 request_exit(2); | |
344 } | |
345 | |
346 static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer) | |
347 { | |
348 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { | |
349 fprintf(stderr, "img transfer status %d?\n", transfer->status); | |
350 img_transfer = NULL; | |
351 request_exit(2); | |
352 return; | |
353 } | |
354 | |
355 printf("Image callback\n"); | |
356 save_to_file(imgbuf); | |
357 if (next_state() < 0) { | |
358 request_exit(2); | |
359 return; | |
360 } | |
361 if (libusb_submit_transfer(img_transfer) < 0) | |
362 request_exit(2); | |
363 } | |
364 | |
365 static int init_capture(void) | |
366 { | |
367 int r; | |
368 | |
369 r = libusb_submit_transfer(irq_transfer); | |
370 if (r < 0) | |
371 return r; | |
372 | |
373 r = libusb_submit_transfer(img_transfer); | |
374 if (r < 0) { | |
375 libusb_cancel_transfer(irq_transfer); | |
376 while (irq_transfer) | |
377 if (libusb_handle_events(NULL) < 0) | |
378 break; | |
379 return r; | |
380 } | |
381 | |
382 /* start state machine */ | |
383 state = STATE_AWAIT_IRQ_FINGER_REMOVED; | |
384 return next_state(); | |
385 } | |
386 | |
387 static int do_init(void) | |
388 { | |
389 unsigned char status; | |
390 int r; | |
391 | |
392 r = get_hwstat(&status); | |
393 if (r < 0) | |
394 return r; | |
395 | |
396 if (!(status & 0x80)) { | |
397 r = set_hwstat(status | 0x80); | |
398 if (r < 0) | |
399 return r; | |
400 r = get_hwstat(&status); | |
401 if (r < 0) | |
402 return r; | |
403 } | |
404 | |
405 status &= ~0x80; | |
406 r = set_hwstat(status); | |
407 if (r < 0) | |
408 return r; | |
409 | |
410 r = get_hwstat(&status); | |
411 if (r < 0) | |
412 return r; | |
413 | |
414 r = sync_intr(0x56); | |
415 if (r < 0) | |
416 return r; | |
417 | |
418 return 0; | |
419 } | |
420 | |
421 static int alloc_transfers(void) | |
422 { | |
423 img_transfer = libusb_alloc_transfer(0); | |
424 if (!img_transfer) | |
425 return -ENOMEM; | |
426 | |
427 irq_transfer = libusb_alloc_transfer(0); | |
428 if (!irq_transfer) | |
429 return -ENOMEM; | |
430 | |
431 libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf, | |
432 sizeof(imgbuf), cb_img, NULL, 0); | |
433 libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf, | |
434 sizeof(irqbuf), cb_irq, NULL, 0); | |
435 | |
436 return 0; | |
437 } | |
438 | |
439 static void sighandler(int signum) | |
440 { | |
441 request_exit(1); | |
442 } | |
443 | |
444 int main(void) | |
445 { | |
446 struct sigaction sigact; | |
447 int r = 1; | |
448 | |
449 r = libusb_init(NULL); | |
450 if (r < 0) { | |
451 fprintf(stderr, "failed to initialise libusb\n"); | |
452 exit(1); | |
453 } | |
454 | |
455 r = find_dpfp_device(); | |
456 if (r < 0) { | |
457 fprintf(stderr, "Could not find/open device\n"); | |
458 goto out; | |
459 } | |
460 | |
461 r = libusb_claim_interface(devh, 0); | |
462 if (r < 0) { | |
463 fprintf(stderr, "usb_claim_interface error %d %s\n", r, strerror
(-r)); | |
464 goto out; | |
465 } | |
466 printf("claimed interface\n"); | |
467 | |
468 r = print_f0_data(); | |
469 if (r < 0) | |
470 goto out_release; | |
471 | |
472 r = do_init(); | |
473 if (r < 0) | |
474 goto out_deinit; | |
475 | |
476 /* async from here onwards */ | |
477 | |
478 sigact.sa_handler = sighandler; | |
479 sigemptyset(&sigact.sa_mask); | |
480 sigact.sa_flags = 0; | |
481 sigaction(SIGINT, &sigact, NULL); | |
482 sigaction(SIGTERM, &sigact, NULL); | |
483 sigaction(SIGQUIT, &sigact, NULL); | |
484 | |
485 r = pthread_create(&poll_thread, NULL, poll_thread_main, NULL); | |
486 if (r) | |
487 goto out_deinit; | |
488 | |
489 r = alloc_transfers(); | |
490 if (r < 0) { | |
491 request_exit(1); | |
492 pthread_join(poll_thread, NULL); | |
493 goto out_deinit; | |
494 } | |
495 | |
496 r = init_capture(); | |
497 if (r < 0) { | |
498 request_exit(1); | |
499 pthread_join(poll_thread, NULL); | |
500 goto out_deinit; | |
501 } | |
502 | |
503 while (!do_exit) { | |
504 pthread_mutex_lock(&exit_cond_lock); | |
505 pthread_cond_wait(&exit_cond, &exit_cond_lock); | |
506 pthread_mutex_unlock(&exit_cond_lock); | |
507 } | |
508 | |
509 printf("shutting down...\n"); | |
510 pthread_join(poll_thread, NULL); | |
511 | |
512 r = libusb_cancel_transfer(irq_transfer); | |
513 if (r < 0) { | |
514 request_exit(1); | |
515 goto out_deinit; | |
516 } | |
517 | |
518 r = libusb_cancel_transfer(img_transfer); | |
519 if (r < 0) { | |
520 request_exit(1); | |
521 goto out_deinit; | |
522 } | |
523 | |
524 while (img_transfer || irq_transfer) | |
525 if (libusb_handle_events(NULL) < 0) | |
526 break; | |
527 | |
528 if (do_exit == 1) | |
529 r = 0; | |
530 else | |
531 r = 1; | |
532 | |
533 out_deinit: | |
534 libusb_free_transfer(img_transfer); | |
535 libusb_free_transfer(irq_transfer); | |
536 set_mode(0); | |
537 set_hwstat(0x80); | |
538 out_release: | |
539 libusb_release_interface(devh, 0); | |
540 out: | |
541 libusb_close(devh); | |
542 libusb_exit(NULL); | |
543 return r >= 0 ? r : -r; | |
544 } | |
545 | |
OLD | NEW |