Home | History | Annotate | Download | only in examples
      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