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