• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libusb example program to manipulate U.are.U 4000B fingerprint scanner.
3  * Copyright © 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_OUT)
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 
find_dpfp_device(void)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 
print_f0_data(void)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 
get_hwstat(unsigned char * status)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 
set_hwstat(unsigned char data)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 
set_mode(unsigned char data)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 
cb_mode_changed(struct libusb_transfer * transfer)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 
set_mode_async(unsigned char data)165 static int set_mode_async(unsigned char data)
166 {
167 	unsigned char *buf = (unsigned char*) 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 
do_sync_intr(unsigned char * data)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 
sync_intr(unsigned char type)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 
save_to_file(unsigned char * data)224 static int save_to_file(unsigned char *data)
225 {
226 	FILE *fd;
227 	char filename[64];
228 
229 	snprintf(filename, sizeof(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 
next_state(void)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 
cb_irq(struct libusb_transfer * transfer)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 
cb_img(struct libusb_transfer * transfer)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 
init_capture(void)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 
do_init(void)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 
alloc_transfers(void)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 
sighandler(int signum)412 static void sighandler(int signum)
413 {
414 	do_exit = 1;
415 }
416 
main(void)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