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 <signal.h> | |
26 #include <string.h> | |
27 #include <stdio.h> | |
28 #include <stdlib.h> | |
29 | |
30 #include <libusb.h> | |
31 | |
32 #define EP_INTR (1 | LIBUSB_ENDPOINT_IN) | |
33 #define EP_DATA (2 | LIBUSB_ENDPOINT_IN) | |
34 #define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN
) | |
35 #define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OU
T) | |
36 #define USB_RQ 0x04 | |
37 #define INTR_LENGTH 64 | |
38 | |
39 enum { | |
40 MODE_INIT = 0x00, | |
41 MODE_AWAIT_FINGER_ON = 0x10, | |
42 MODE_AWAIT_FINGER_OFF = 0x12, | |
43 MODE_CAPTURE = 0x20, | |
44 MODE_SHUT_UP = 0x30, | |
45 MODE_READY = 0x80, | |
46 }; | |
47 | |
48 static int next_state(void); | |
49 | |
50 enum { | |
51 STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1, | |
52 STATE_AWAIT_IRQ_FINGER_DETECTED, | |
53 STATE_AWAIT_MODE_CHANGE_CAPTURE, | |
54 STATE_AWAIT_IMAGE, | |
55 STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF, | |
56 STATE_AWAIT_IRQ_FINGER_REMOVED, | |
57 }; | |
58 | |
59 static int state = 0; | |
60 static struct libusb_device_handle *devh = NULL; | |
61 static unsigned char imgbuf[0x1b340]; | |
62 static unsigned char irqbuf[INTR_LENGTH]; | |
63 static struct libusb_transfer *img_transfer = NULL; | |
64 static struct libusb_transfer *irq_transfer = NULL; | |
65 static int img_idx = 0; | |
66 static int do_exit = 0; | |
67 | |
68 static int find_dpfp_device(void) | |
69 { | |
70 devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a); | |
71 return devh ? 0 : -EIO; | |
72 } | |
73 | |
74 static int print_f0_data(void) | |
75 { | |
76 unsigned char data[0x10]; | |
77 int r; | |
78 unsigned int i; | |
79 | |
80 r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data, | |
81 sizeof(data), 0); | |
82 if (r < 0) { | |
83 fprintf(stderr, "F0 error %d\n", r); | |
84 return r; | |
85 } | |
86 if ((unsigned int) r < sizeof(data)) { | |
87 fprintf(stderr, "short read (%d)\n", r); | |
88 return -1; | |
89 } | |
90 | |
91 printf("F0 data:"); | |
92 for (i = 0; i < sizeof(data); i++) | |
93 printf("%02x ", data[i]); | |
94 printf("\n"); | |
95 return 0; | |
96 } | |
97 | |
98 static int get_hwstat(unsigned char *status) | |
99 { | |
100 int r; | |
101 | |
102 r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0
); | |
103 if (r < 0) { | |
104 fprintf(stderr, "read hwstat error %d\n", r); | |
105 return r; | |
106 } | |
107 if ((unsigned int) r < 1) { | |
108 fprintf(stderr, "short read (%d)\n", r); | |
109 return -1; | |
110 } | |
111 | |
112 printf("hwstat reads %02x\n", *status); | |
113 return 0; | |
114 } | |
115 | |
116 static int set_hwstat(unsigned char data) | |
117 { | |
118 int r; | |
119 | |
120 printf("set hwstat to %02x\n", data); | |
121 r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0
); | |
122 if (r < 0) { | |
123 fprintf(stderr, "set hwstat error %d\n", r); | |
124 return r; | |
125 } | |
126 if ((unsigned int) r < 1) { | |
127 fprintf(stderr, "short write (%d)", r); | |
128 return -1; | |
129 } | |
130 | |
131 return 0; | |
132 } | |
133 | |
134 static int set_mode(unsigned char data) | |
135 { | |
136 int r; | |
137 printf("set mode %02x\n", data); | |
138 | |
139 r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0
); | |
140 if (r < 0) { | |
141 fprintf(stderr, "set mode error %d\n", r); | |
142 return r; | |
143 } | |
144 if ((unsigned int) r < 1) { | |
145 fprintf(stderr, "short write (%d)", r); | |
146 return -1; | |
147 } | |
148 | |
149 return 0; | |
150 } | |
151 | |
152 static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) | |
153 { | |
154 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { | |
155 fprintf(stderr, "mode change transfer not completed!\n"); | |
156 do_exit = 2; | |
157 } | |
158 | |
159 printf("async cb_mode_changed length=%d actual_length=%d\n", | |
160 transfer->length, transfer->actual_length); | |
161 if (next_state() < 0) | |
162 do_exit = 2; | |
163 } | |
164 | |
165 static int set_mode_async(unsigned char data) | |
166 { | |
167 unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); | |
168 struct libusb_transfer *transfer; | |
169 | |
170 if (!buf) | |
171 return -ENOMEM; | |
172 | |
173 transfer = libusb_alloc_transfer(0); | |
174 if (!transfer) { | |
175 free(buf); | |
176 return -ENOMEM; | |
177 } | |
178 | |
179 printf("async set mode %02x\n", data); | |
180 libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1); | |
181 buf[LIBUSB_CONTROL_SETUP_SIZE] = data; | |
182 libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL, | |
183 1000); | |
184 | |
185 transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK | |
186 | LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; | |
187 return libusb_submit_transfer(transfer); | |
188 } | |
189 | |
190 static int do_sync_intr(unsigned char *data) | |
191 { | |
192 int r; | |
193 int transferred; | |
194 | |
195 r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH, | |
196 &transferred, 1000); | |
197 if (r < 0) { | |
198 fprintf(stderr, "intr error %d\n", r); | |
199 return r; | |
200 } | |
201 if (transferred < INTR_LENGTH) { | |
202 fprintf(stderr, "short read (%d)\n", r); | |
203 return -1; | |
204 } | |
205 | |
206 printf("recv interrupt %04x\n", *((uint16_t *) data)); | |
207 return 0; | |
208 } | |
209 | |
210 static int sync_intr(unsigned char type) | |
211 { | |
212 int r; | |
213 unsigned char data[INTR_LENGTH]; | |
214 | |
215 while (1) { | |
216 r = do_sync_intr(data); | |
217 if (r < 0) | |
218 return r; | |
219 if (data[0] == type) | |
220 return 0; | |
221 } | |
222 } | |
223 | |
224 static int save_to_file(unsigned char *data) | |
225 { | |
226 FILE *fd; | |
227 char filename[64]; | |
228 | |
229 sprintf(filename, "finger%d.pgm", img_idx++); | |
230 fd = fopen(filename, "w"); | |
231 if (!fd) | |
232 return -1; | |
233 | |
234 fputs("P5 384 289 255 ", fd); | |
235 (void) fwrite(data + 64, 1, 384*289, fd); | |
236 fclose(fd); | |
237 printf("saved image to %s\n", filename); | |
238 return 0; | |
239 } | |
240 | |
241 static int next_state(void) | |
242 { | |
243 int r = 0; | |
244 printf("old state: %d\n", state); | |
245 switch (state) { | |
246 case STATE_AWAIT_IRQ_FINGER_REMOVED: | |
247 state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON; | |
248 r = set_mode_async(MODE_AWAIT_FINGER_ON); | |
249 break; | |
250 case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON: | |
251 state = STATE_AWAIT_IRQ_FINGER_DETECTED; | |
252 break; | |
253 case STATE_AWAIT_IRQ_FINGER_DETECTED: | |
254 state = STATE_AWAIT_MODE_CHANGE_CAPTURE; | |
255 r = set_mode_async(MODE_CAPTURE); | |
256 break; | |
257 case STATE_AWAIT_MODE_CHANGE_CAPTURE: | |
258 state = STATE_AWAIT_IMAGE; | |
259 break; | |
260 case STATE_AWAIT_IMAGE: | |
261 state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF; | |
262 r = set_mode_async(MODE_AWAIT_FINGER_OFF); | |
263 break; | |
264 case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF: | |
265 state = STATE_AWAIT_IRQ_FINGER_REMOVED; | |
266 break; | |
267 default: | |
268 printf("unrecognised state %d\n", state); | |
269 } | |
270 if (r < 0) { | |
271 fprintf(stderr, "error detected changing state\n"); | |
272 return r; | |
273 } | |
274 | |
275 printf("new state: %d\n", state); | |
276 return 0; | |
277 } | |
278 | |
279 static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer) | |
280 { | |
281 unsigned char irqtype = transfer->buffer[0]; | |
282 | |
283 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { | |
284 fprintf(stderr, "irq transfer status %d?\n", transfer->status); | |
285 do_exit = 2; | |
286 libusb_free_transfer(transfer); | |
287 irq_transfer = NULL; | |
288 return; | |
289 } | |
290 | |
291 printf("IRQ callback %02x\n", irqtype); | |
292 switch (state) { | |
293 case STATE_AWAIT_IRQ_FINGER_DETECTED: | |
294 if (irqtype == 0x01) { | |
295 if (next_state() < 0) { | |
296 do_exit = 2; | |
297 return; | |
298 } | |
299 } else { | |
300 printf("finger-on-sensor detected in wrong state!\n"); | |
301 } | |
302 break; | |
303 case STATE_AWAIT_IRQ_FINGER_REMOVED: | |
304 if (irqtype == 0x02) { | |
305 if (next_state() < 0) { | |
306 do_exit = 2; | |
307 return; | |
308 } | |
309 } else { | |
310 printf("finger-on-sensor detected in wrong state!\n"); | |
311 } | |
312 break; | |
313 } | |
314 if (libusb_submit_transfer(irq_transfer) < 0) | |
315 do_exit = 2; | |
316 } | |
317 | |
318 static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer) | |
319 { | |
320 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { | |
321 fprintf(stderr, "img transfer status %d?\n", transfer->status); | |
322 do_exit = 2; | |
323 libusb_free_transfer(transfer); | |
324 img_transfer = NULL; | |
325 return; | |
326 } | |
327 | |
328 printf("Image callback\n"); | |
329 save_to_file(imgbuf); | |
330 if (next_state() < 0) { | |
331 do_exit = 2; | |
332 return; | |
333 } | |
334 if (libusb_submit_transfer(img_transfer) < 0) | |
335 do_exit = 2; | |
336 } | |
337 | |
338 static int init_capture(void) | |
339 { | |
340 int r; | |
341 | |
342 r = libusb_submit_transfer(irq_transfer); | |
343 if (r < 0) | |
344 return r; | |
345 | |
346 r = libusb_submit_transfer(img_transfer); | |
347 if (r < 0) { | |
348 libusb_cancel_transfer(irq_transfer); | |
349 while (irq_transfer) | |
350 if (libusb_handle_events(NULL) < 0) | |
351 break; | |
352 return r; | |
353 } | |
354 | |
355 /* start state machine */ | |
356 state = STATE_AWAIT_IRQ_FINGER_REMOVED; | |
357 return next_state(); | |
358 } | |
359 | |
360 static int do_init(void) | |
361 { | |
362 unsigned char status; | |
363 int r; | |
364 | |
365 r = get_hwstat(&status); | |
366 if (r < 0) | |
367 return r; | |
368 | |
369 if (!(status & 0x80)) { | |
370 r = set_hwstat(status | 0x80); | |
371 if (r < 0) | |
372 return r; | |
373 r = get_hwstat(&status); | |
374 if (r < 0) | |
375 return r; | |
376 } | |
377 | |
378 status &= ~0x80; | |
379 r = set_hwstat(status); | |
380 if (r < 0) | |
381 return r; | |
382 | |
383 r = get_hwstat(&status); | |
384 if (r < 0) | |
385 return r; | |
386 | |
387 r = sync_intr(0x56); | |
388 if (r < 0) | |
389 return r; | |
390 | |
391 return 0; | |
392 } | |
393 | |
394 static int alloc_transfers(void) | |
395 { | |
396 img_transfer = libusb_alloc_transfer(0); | |
397 if (!img_transfer) | |
398 return -ENOMEM; | |
399 | |
400 irq_transfer = libusb_alloc_transfer(0); | |
401 if (!irq_transfer) | |
402 return -ENOMEM; | |
403 | |
404 libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf, | |
405 sizeof(imgbuf), cb_img, NULL, 0); | |
406 libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf, | |
407 sizeof(irqbuf), cb_irq, NULL, 0); | |
408 | |
409 return 0; | |
410 } | |
411 | |
412 static void sighandler(int signum) | |
413 { | |
414 do_exit = 1; | |
415 } | |
416 | |
417 int main(void) | |
418 { | |
419 struct sigaction sigact; | |
420 int r = 1; | |
421 | |
422 r = libusb_init(NULL); | |
423 if (r < 0) { | |
424 fprintf(stderr, "failed to initialise libusb\n"); | |
425 exit(1); | |
426 } | |
427 | |
428 r = find_dpfp_device(); | |
429 if (r < 0) { | |
430 fprintf(stderr, "Could not find/open device\n"); | |
431 goto out; | |
432 } | |
433 | |
434 r = libusb_claim_interface(devh, 0); | |
435 if (r < 0) { | |
436 fprintf(stderr, "usb_claim_interface error %d\n", r); | |
437 goto out; | |
438 } | |
439 printf("claimed interface\n"); | |
440 | |
441 r = print_f0_data(); | |
442 if (r < 0) | |
443 goto out_release; | |
444 | |
445 r = do_init(); | |
446 if (r < 0) | |
447 goto out_deinit; | |
448 | |
449 /* async from here onwards */ | |
450 | |
451 r = alloc_transfers(); | |
452 if (r < 0) | |
453 goto out_deinit; | |
454 | |
455 r = init_capture(); | |
456 if (r < 0) | |
457 goto out_deinit; | |
458 | |
459 sigact.sa_handler = sighandler; | |
460 sigemptyset(&sigact.sa_mask); | |
461 sigact.sa_flags = 0; | |
462 sigaction(SIGINT, &sigact, NULL); | |
463 sigaction(SIGTERM, &sigact, NULL); | |
464 sigaction(SIGQUIT, &sigact, NULL); | |
465 | |
466 while (!do_exit) { | |
467 r = libusb_handle_events(NULL); | |
468 if (r < 0) | |
469 goto out_deinit; | |
470 } | |
471 | |
472 printf("shutting down...\n"); | |
473 | |
474 if (irq_transfer) { | |
475 r = libusb_cancel_transfer(irq_transfer); | |
476 if (r < 0) | |
477 goto out_deinit; | |
478 } | |
479 | |
480 if (img_transfer) { | |
481 r = libusb_cancel_transfer(img_transfer); | |
482 if (r < 0) | |
483 goto out_deinit; | |
484 } | |
485 | |
486 while (irq_transfer || img_transfer) | |
487 if (libusb_handle_events(NULL) < 0) | |
488 break; | |
489 | |
490 if (do_exit == 1) | |
491 r = 0; | |
492 else | |
493 r = 1; | |
494 | |
495 out_deinit: | |
496 libusb_free_transfer(img_transfer); | |
497 libusb_free_transfer(irq_transfer); | |
498 set_mode(0); | |
499 set_hwstat(0x80); | |
500 out_release: | |
501 libusb_release_interface(devh, 0); | |
502 out: | |
503 libusb_close(devh); | |
504 libusb_exit(NULL); | |
505 return r >= 0 ? r : -r; | |
506 } | |
507 | |
OLD | NEW |