1 /* 2 * libusb example program to manipulate U.are.U 4000B fingerprint scanner. 3 * Copyright (C) 2007 Daniel Drake <dsd (at) 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/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_OUT) 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 pthread_exit(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 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 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 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 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 546