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 <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
40enum {
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
49static int next_state(void);
50
51enum {
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
60static int state = 0;
61static struct libusb_device_handle *devh = NULL;
62static unsigned char imgbuf[0x1b340];
63static unsigned char irqbuf[INTR_LENGTH];
64static struct libusb_transfer *img_transfer = NULL;
65static struct libusb_transfer *irq_transfer = NULL;
66static int img_idx = 0;
67static int do_exit = 0;
68
69static pthread_t poll_thread;
70static pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER;
71static pthread_mutex_t exit_cond_lock = PTHREAD_MUTEX_INITIALIZER;
72
73static void request_exit(int code)
74{
75	do_exit = code;
76	pthread_cond_signal(&exit_cond);
77}
78
79static 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
97static int find_dpfp_device(void)
98{
99	devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a);
100	return devh ? 0 : -EIO;
101}
102
103static 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
127static 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
145static 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
163static 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
181static 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
194static 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
219static 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
239static 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
253static 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
270static 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
308static 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
346static 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
365static 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
387static 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
421static 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
439static void sighandler(int signum)
440{
441	request_exit(1);
442}
443
444int 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
533out_deinit:
534	libusb_free_transfer(img_transfer);
535	libusb_free_transfer(irq_transfer);
536	set_mode(0);
537	set_hwstat(0x80);
538out_release:
539	libusb_release_interface(devh, 0);
540out:
541	libusb_close(devh);
542	libusb_exit(NULL);
543	return r >= 0 ? r : -r;
544}
545
546