Beginnings of cross-platform abstraction
[platform/upstream/libusb.git] / examples / dpfp.c
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 <signal.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #include <libusb/libusb.h>
31
32 #define EP_INTR                 (1 | LIBUSB_ENDPOINT_IN)
33 #define EP_DATA                 (2 | LIBUSB_ENDPOINT_IN)
34 #define CTRL_IN                 (LIBUSB_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
35 #define CTRL_OUT                (LIBUSB_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 static int submit_irq_transfer(void);
50 static int submit_img_transfer(void);
51
52 enum {
53         STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1,
54         STATE_AWAIT_IRQ_FINGER_DETECTED,
55         STATE_AWAIT_MODE_CHANGE_CAPTURE,
56         STATE_AWAIT_IMAGE,
57         STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF,
58         STATE_AWAIT_IRQ_FINGER_REMOVED,
59 };
60
61 static int state = 0;
62 static struct libusb_device_handle *devh = NULL;
63 static unsigned char imgbuf[0x1b340];
64 static unsigned char irqbuf[INTR_LENGTH];
65 static struct libusb_transfer *img_transfer = NULL;
66 static struct libusb_transfer *irq_transfer = NULL;
67 static int img_idx = 0;
68 static int do_exit = 0;
69
70 static int find_dpfp_device(void)
71 {
72         devh = libusb_open_device_with_vid_pid(0x05ba, 0x000a);
73         return devh ? 0 : -EIO;
74 }
75
76 static int print_f0_data(void)
77 {
78         unsigned char data[0x10];
79         int r;
80         unsigned int i;
81
82         r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data,
83                 sizeof(data), 0);
84         if (r < 0) {
85                 fprintf(stderr, "F0 error %d\n", r);
86                 return r;
87         }
88         if ((unsigned int) r < sizeof(data)) {
89                 fprintf(stderr, "short read (%d)\n", r);
90                 return -1;
91         }
92
93         printf("F0 data:");
94         for (i = 0; i < sizeof(data); i++)
95                 printf("%02x ", data[i]);
96         printf("\n");
97         return 0;
98 }
99
100 static int get_hwstat(unsigned char *status)
101 {
102         int r;
103
104         r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0);
105         if (r < 0) {
106                 fprintf(stderr, "read hwstat error %d\n", r);
107                 return r;
108         }
109         if ((unsigned int) r < 1) {
110                 fprintf(stderr, "short read (%d)\n", r);
111                 return -1;
112         }
113
114         printf("hwstat reads %02x\n", *status);
115         return 0;
116 }
117
118 static int set_hwstat(unsigned char data)
119 {
120         int r;
121
122         printf("set hwstat to %02x\n", data);
123         r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0);
124         if (r < 0) {
125                 fprintf(stderr, "set hwstat error %d\n", r);
126                 return r;
127         }
128         if ((unsigned int) r < 1) {
129                 fprintf(stderr, "short write (%d)", r);
130                 return -1;
131         }
132
133         return 0;
134 }
135
136 static int set_mode(unsigned char data)
137 {
138         int r;
139         printf("set mode %02x\n", data);
140
141         r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0);
142         if (r < 0) {
143                 fprintf(stderr, "set mode error %d\n", r);
144                 return r;
145         }
146         if ((unsigned int) r < 1) {
147                 fprintf(stderr, "short write (%d)", r);
148                 return -1;
149         }
150
151         return 0;
152 }
153
154 static void cb_mode_changed(struct libusb_transfer *transfer)
155 {
156         if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
157                 fprintf(stderr, "mode change transfer not completed!\n");
158                 do_exit = 2;
159         }
160
161         printf("async cb_mode_changed length=%d actual_length=%d\n",
162                 transfer->length, transfer->actual_length);
163         if (next_state() < 0)
164                 do_exit = 2;
165 }
166
167 static int set_mode_async(unsigned char data)
168 {
169         unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1);
170         struct libusb_transfer *transfer;
171
172         if (!buf)
173                 return -ENOMEM;
174         
175         transfer = libusb_alloc_transfer();
176         if (!transfer) {
177                 free(buf);
178                 return -ENOMEM;
179         }
180
181         printf("async set mode %02x\n", data);
182         libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1);
183         buf[LIBUSB_CONTROL_SETUP_SIZE] = data;
184         libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL,
185                 1000);
186
187         transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK
188                 | LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER;
189         return libusb_submit_transfer(transfer);
190 }
191
192 static int do_sync_intr(unsigned char *data)
193 {
194         int r;
195         int transferred;
196
197         r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH,
198                 &transferred, 1000);
199         if (r < 0) {
200                 fprintf(stderr, "intr error %d\n", r);
201                 return r;
202         }
203         if (transferred < INTR_LENGTH) {
204                 fprintf(stderr, "short read (%d)\n", r);
205                 return -1;
206         }
207
208         printf("recv interrupt %04x\n", *((uint16_t *) data));
209         return 0;
210 }
211
212 static int sync_intr(unsigned char type)
213 {       
214         int r;
215         unsigned char data[INTR_LENGTH];
216
217         while (1) {
218                 r = do_sync_intr(data);
219                 if (r < 0)
220                         return r;
221                 if (data[0] == type)
222                         return 0;
223         }
224 }
225
226 static int save_to_file(unsigned char *data)
227 {
228         FILE *fd;
229         char filename[64];
230
231         sprintf(filename, "finger%d.pgm", img_idx++);
232         fd = fopen(filename, "w");
233         if (!fd)
234                 return -1;
235
236         fputs("P5 384 289 255 ", fd);
237         fwrite(data + 64, 1, 384*289, fd);
238         fclose(fd);
239         printf("saved image to %s\n", filename);
240         return 0;
241 }
242
243 static int next_state(void)
244 {
245         int r = 0;
246         printf("old state: %d\n", state);
247         switch (state) {
248         case STATE_AWAIT_IRQ_FINGER_REMOVED:
249                 state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON;
250                 r = set_mode_async(MODE_AWAIT_FINGER_ON);
251                 break;
252         case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON:
253                 state = STATE_AWAIT_IRQ_FINGER_DETECTED;
254                 break;
255         case STATE_AWAIT_IRQ_FINGER_DETECTED:
256                 state = STATE_AWAIT_MODE_CHANGE_CAPTURE;
257                 r = set_mode_async(MODE_CAPTURE);
258                 break;
259         case STATE_AWAIT_MODE_CHANGE_CAPTURE:
260                 state = STATE_AWAIT_IMAGE;
261                 break;
262         case STATE_AWAIT_IMAGE:
263                 state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF;
264                 r = set_mode_async(MODE_AWAIT_FINGER_OFF);
265                 break;
266         case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF:
267                 state = STATE_AWAIT_IRQ_FINGER_REMOVED;
268                 break;
269         default:
270                 printf("unrecognised state %d\n", state);
271         }
272         if (r < 0) {
273                 fprintf(stderr, "error detected changing state\n");
274                 return r;
275         }
276
277         printf("new state: %d\n", state);
278         return 0;
279 }
280
281 static void cb_irq(struct libusb_transfer *transfer)
282 {
283         unsigned char irqtype = transfer->buffer[0];
284
285         if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
286                 fprintf(stderr, "irq transfer status %d?\n", transfer->status);
287                 do_exit = 2;
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 (submit_irq_transfer() < 0)
315                 do_exit = 2;
316 }
317
318 static void 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                 return;
324         }
325
326         printf("Image callback\n");
327         save_to_file(imgbuf);
328         if (next_state() < 0) {
329                 do_exit = 2;
330                 return;
331         }
332         if (submit_img_transfer() < 0)
333                 do_exit = 2;
334 }
335
336 static int submit_irq_transfer(void)
337 {
338         return libusb_submit_transfer(irq_transfer);
339 }
340
341 static int submit_img_transfer(void)
342 {
343         return libusb_submit_transfer(img_transfer);
344 }
345
346 static int init_capture(void)
347 {
348         int r;
349
350         r = submit_irq_transfer();
351         if (r < 0)
352                 return r;
353
354         r = submit_img_transfer();
355         if (r < 0) {
356                 libusb_cancel_transfer_sync(img_transfer);
357                 return r;
358         }
359
360         /* start state machine */
361         state = STATE_AWAIT_IRQ_FINGER_REMOVED;
362         return next_state();
363 }
364
365 static int do_init(void)
366 {
367         unsigned char status;
368         int r;
369
370         r = get_hwstat(&status);
371         if (r < 0)
372                 return r;
373
374         if (!(status & 0x80)) {
375                 r = set_hwstat(status | 0x80);
376                 if (r < 0)
377                         return r;
378                 r = get_hwstat(&status);
379                 if (r < 0)
380                         return r;
381         }
382
383         status &= ~0x80;
384         r = set_hwstat(status);
385         if (r < 0)
386                 return r;
387
388         r = get_hwstat(&status);
389         if (r < 0)
390                 return r;
391
392         r = sync_intr(0x56);
393         if (r < 0)
394                 return r;
395
396         return 0;
397 }
398
399 static int alloc_transfers(void)
400 {
401         img_transfer = libusb_alloc_transfer();
402         if (!img_transfer)
403                 return -ENOMEM;
404         
405         irq_transfer = libusb_alloc_transfer();
406         if (!irq_transfer)
407                 return -ENOMEM;
408
409         libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf,
410                 sizeof(imgbuf), cb_img, NULL, 0);
411         libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf,
412                 sizeof(irqbuf), cb_irq, NULL, 0);
413
414         return 0;
415 }
416
417 static void sighandler(int signum)
418 {
419         do_exit = 1;    
420 }
421
422 int main(void)
423 {
424         struct sigaction sigact;
425         int r = 1;
426
427         r = libusb_init();
428         if (r < 0) {
429                 fprintf(stderr, "failed to initialise libusb\n");
430                 exit(1);
431         }
432
433         r = find_dpfp_device();
434         if (r < 0) {
435                 fprintf(stderr, "Could not find/open device\n");
436                 goto out;
437         }
438
439         r = libusb_claim_interface(devh, 0);
440         if (r < 0) {
441                 fprintf(stderr, "usb_claim_interface error %d %s\n", r, strerror(-r));
442                 goto out;
443         }
444         printf("claimed interface\n");
445
446         r = print_f0_data();
447         if (r < 0)
448                 goto out_release;
449
450         r = do_init();
451         if (r < 0)
452                 goto out_deinit;
453
454         /* async from here onwards */
455
456         r = alloc_transfers();
457         if (r < 0)
458                 goto out_deinit;
459
460         r = init_capture();
461         if (r < 0)
462                 goto out_deinit;
463
464         sigact.sa_handler = sighandler;
465         sigemptyset(&sigact.sa_mask);
466         sigact.sa_flags = 0;
467         sigaction(SIGINT, &sigact, NULL);
468         sigaction(SIGTERM, &sigact, NULL);
469         sigaction(SIGQUIT, &sigact, NULL);
470
471         while (!do_exit) {
472                 r = libusb_poll();
473                 if (r < 0)
474                         goto out_deinit;
475         }
476
477         printf("shutting down...\n");
478
479         r = libusb_cancel_transfer_sync(irq_transfer);
480         if (r < 0)
481                 goto out_deinit;
482
483         r = libusb_cancel_transfer_sync(img_transfer);
484         if (r < 0)
485                 goto out_deinit;
486         
487         if (do_exit == 1)
488                 r = 0;
489         else
490                 r = 1;
491
492 out_deinit:
493         libusb_free_transfer(img_transfer);
494         libusb_free_transfer(irq_transfer);
495         set_mode(0);
496         set_hwstat(0x80);
497 out_release:
498         libusb_release_interface(devh, 0);
499 out:
500         libusb_close(devh);
501         libusb_exit();
502         return r >= 0 ? r : -r;
503 }
504