Add convenience function to find and open a device by USB VID+PID
[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_urb(void);
50 static int submit_img_urb(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_dev_handle *devh = NULL;
63 static unsigned char imgbuf[0x1b340];
64 static unsigned char irqbuf[INTR_LENGTH];
65 static libusb_urb_handle *img_urbh = NULL;
66 static libusb_urb_handle *irq_urbh = NULL;
67 static int img_idx = 0;
68 static int do_exit = 0;
69
70 static struct libusb_bulk_transfer imgtrf = {
71         .endpoint = EP_DATA,
72         .data = imgbuf,
73         .length = sizeof(imgbuf),
74 };
75
76 static struct libusb_bulk_transfer irqtrf = {
77         .endpoint = EP_INTR,
78         .data = irqbuf,
79         .length = sizeof(irqbuf),
80 };
81
82 static int find_dpfp_device(void)
83 {
84         devh = libusb_open_device_with_vid_pid(0x05ba, 0x000a);
85         return devh ? 0 : -EIO;
86 }
87
88 static int print_f0_data(void)
89 {
90         unsigned char data[0x10];
91         struct libusb_control_transfer transfer = {
92                 .requesttype = CTRL_IN,
93                 .request = USB_RQ,
94                 .value = 0xf0,
95                 .index = 0,
96                 .length = sizeof(data),
97                 .data = data,
98         };
99         int r;
100         unsigned int i;
101
102         r = libusb_control_transfer(devh, &transfer, 0);
103         if (r < 0) {
104                 fprintf(stderr, "F0 error %d\n", r);
105                 return r;
106         }
107         if ((unsigned int) r < sizeof(data)) {
108                 fprintf(stderr, "short read (%d)\n", r);
109                 return -1;
110         }
111
112         printf("F0 data:");
113         for (i = 0; i < sizeof(data); i++)
114                 printf("%02x ", data[i]);
115         printf("\n");
116         return 0;
117 }
118
119 static int get_hwstat(unsigned char *status)
120 {
121         struct libusb_control_transfer transfer = {
122                 .requesttype = CTRL_IN,
123                 .request = USB_RQ,
124                 .value = 0x07,
125                 .index = 0,
126                 .length = 1,
127                 .data = status,
128         };
129         int r;
130
131         r = libusb_control_transfer(devh, &transfer, 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         struct libusb_control_transfer transfer = {
149                 .requesttype = CTRL_OUT,
150                 .request = USB_RQ,
151                 .value = 0x07,
152                 .index = 0,
153                 .length = 1,
154                 .data = &data,
155         };
156
157         printf("set hwstat to %02x\n", data);
158
159         r = libusb_control_transfer(devh, &transfer, 0);
160         if (r < 0) {
161                 fprintf(stderr, "set hwstat error %d\n", r);
162                 return r;
163         }
164         if ((unsigned int) r < 1) {
165                 fprintf(stderr, "short write (%d)", r);
166                 return -1;
167         }
168
169         return 0;
170 }
171
172 static int set_mode(unsigned char data)
173 {
174         int r;
175         struct libusb_control_transfer transfer = {
176                 .requesttype = CTRL_OUT,
177                 .request = USB_RQ,
178                 .value = 0x4e,
179                 .index = 0,
180                 .length = 1,
181                 .data = &data,
182         };
183
184         printf("set mode %02x\n", data);
185
186         r = libusb_control_transfer(devh, &transfer, 0);
187         if (r < 0) {
188                 fprintf(stderr, "set mode error %d\n", r);
189                 return r;
190         }
191         if ((unsigned int) r < 1) {
192                 fprintf(stderr, "short write (%d)", r);
193                 return -1;
194         }
195
196         return 0;
197 }
198
199 static void cb_mode_changed(struct libusb_dev_handle *_devh,
200         struct libusb_urb_handle *urbh, enum libusb_urb_cb_status status,
201         struct libusb_ctrl_setup *setup, unsigned char *data, int actual_length,
202         void *user_data)
203 {
204         if (status != FP_URB_COMPLETED) {
205                 fprintf(stderr, "mode change URB not completed!\n");
206                 do_exit = 2;
207         }
208
209         printf("async cb_mode_changed\n");
210         if (next_state() < 0)
211                 do_exit = 2;
212 }
213
214 static int set_mode_async(unsigned char data)
215 {
216         libusb_urb_handle *urbh;
217         struct libusb_control_transfer transfer = {
218                 .requesttype = CTRL_OUT,
219                 .request = USB_RQ,
220                 .value = 0x4e,
221                 .index = 0,
222                 .length = 1,
223                 .data = &data,
224         };
225
226         printf("async set mode %02x\n", data);
227
228         urbh = libusb_async_control_transfer(devh, &transfer, cb_mode_changed, NULL,
229                 1000);
230         if (!urbh) {
231                 fprintf(stderr, "set mode submit error\n");
232                 return -1;
233         }
234
235         return 0;
236 }
237
238 static int do_sync_intr(unsigned char *data)
239 {
240         struct libusb_bulk_transfer transfer = {
241                 .endpoint = EP_INTR,
242                 .data = data,
243                 .length = INTR_LENGTH,
244         };
245         int r;
246         int transferred;
247
248         r = libusb_interrupt_transfer(devh, &transfer, &transferred, 1000);
249         if (r < 0) {
250                 fprintf(stderr, "intr error %d\n", r);
251                 return r;
252         }
253         if (transferred < INTR_LENGTH) {
254                 fprintf(stderr, "short read (%d)\n", r);
255                 return -1;
256         }
257
258         printf("recv interrupt %04x\n", *((uint16_t *) data));
259         return 0;
260 }
261
262 static int sync_intr(unsigned char type)
263 {       
264         int r;
265         unsigned char data[INTR_LENGTH];
266
267         while (1) {
268                 r = do_sync_intr(data);
269                 if (r < 0)
270                         return r;
271                 if (data[0] == type)
272                         return 0;
273         }
274 }
275
276 static int save_to_file(unsigned char *data)
277 {
278         FILE *fd;
279         char filename[64];
280
281         sprintf(filename, "finger%d.pgm", img_idx++);
282         fd = fopen(filename, "w");
283         if (!fd)
284                 return -1;
285
286         fputs("P5 384 289 255 ", fd);
287         fwrite(data + 64, 1, 384*289, fd);
288         fclose(fd);
289         printf("saved image to %s\n", filename);
290         return 0;
291 }
292
293 static int next_state(void)
294 {
295         int r = 0;
296         printf("old state: %d\n", state);
297         switch (state) {
298         case STATE_AWAIT_IRQ_FINGER_REMOVED:
299                 state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON;
300                 r = set_mode_async(MODE_AWAIT_FINGER_ON);
301                 break;
302         case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON:
303                 state = STATE_AWAIT_IRQ_FINGER_DETECTED;
304                 break;
305         case STATE_AWAIT_IRQ_FINGER_DETECTED:
306                 state = STATE_AWAIT_MODE_CHANGE_CAPTURE;
307                 r = set_mode_async(MODE_CAPTURE);
308                 break;
309         case STATE_AWAIT_MODE_CHANGE_CAPTURE:
310                 state = STATE_AWAIT_IMAGE;
311                 break;
312         case STATE_AWAIT_IMAGE:
313                 state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF;
314                 r = set_mode_async(MODE_AWAIT_FINGER_OFF);
315                 break;
316         case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF:
317                 state = STATE_AWAIT_IRQ_FINGER_REMOVED;
318                 break;
319         default:
320                 printf("unrecognised state %d\n", state);
321         }
322         if (r < 0) {
323                 fprintf(stderr, "error detected changing state");
324                 return r;
325         }
326
327         printf("new state: %d\n", state);
328         return 0;
329 }
330
331 static void cb_irq(libusb_dev_handle *_devh, libusb_urb_handle *urbh,
332         enum libusb_urb_cb_status status, unsigned char endpoint, int rqlength,
333         unsigned char *data, int actual_length, void *user_data)
334 {
335         unsigned char irqtype = data[0];
336
337         if (status != FP_URB_COMPLETED) {
338                 fprintf(stderr, "irq URB status %d?\n", status);
339                 do_exit = 2;
340                 return;
341         }
342
343         printf("IRQ callback %02x\n", irqtype);
344         switch (state) {
345         case STATE_AWAIT_IRQ_FINGER_DETECTED:
346                 if (irqtype == 0x01) {
347                         if (next_state() < 0) {
348                                 do_exit = 2;
349                                 return;
350                         }
351                 } else {
352                         printf("finger-on-sensor detected in wrong state!\n");
353                 }
354                 break;
355         case STATE_AWAIT_IRQ_FINGER_REMOVED:
356                 if (irqtype == 0x02) {
357                         if (next_state() < 0) {
358                                 do_exit = 2;
359                                 return;
360                         }
361                 } else {
362                         printf("finger-on-sensor detected in wrong state!\n");
363                 }
364                 break;
365         }
366         if (submit_irq_urb() < 0)
367                 do_exit = 2;
368 }
369
370 static void cb_img(libusb_dev_handle *_devh, libusb_urb_handle *urbh,
371         enum libusb_urb_cb_status status, unsigned char endpoint, int rqlength,
372         unsigned char *data, int actual_length, void *user_data)
373 {
374         if (status != FP_URB_COMPLETED) {
375                 fprintf(stderr, "img URB status %d?\n", status);
376                 do_exit = 2;
377                 return;
378         }
379
380         printf("Image callback\n");
381         save_to_file(imgbuf);
382         if (next_state() < 0) {
383                 do_exit = 2;
384                 return;
385         }
386         if (submit_img_urb() < 0)
387                 do_exit = 2;
388 }
389
390 static int submit_irq_urb(void)
391 {
392         libusb_urb_handle_free(irq_urbh);
393         irq_urbh = libusb_async_interrupt_transfer(devh, &irqtrf, cb_irq, NULL, 0);
394         return irq_urbh != NULL;
395 }
396
397 static int submit_img_urb(void)
398 {
399         libusb_urb_handle_free(img_urbh);
400         img_urbh = libusb_async_bulk_transfer(devh, &imgtrf, cb_img, NULL, 0);
401         return img_urbh != NULL;
402 }
403
404 static int init_capture(void)
405 {
406         int r;
407
408         r = submit_irq_urb();
409         if (r < 0)
410                 return r;
411
412         r = submit_img_urb();
413         if (r < 0) {
414                 libusb_urb_handle_cancel_sync(devh, img_urbh);
415                 return r;
416         }
417
418         /* start state machine */
419         state = STATE_AWAIT_IRQ_FINGER_REMOVED;
420         return next_state();
421 }
422
423 static int do_init(void)
424 {
425         unsigned char status;
426         int r;
427
428         r = get_hwstat(&status);
429         if (r < 0)
430                 return r;
431
432         if (!(status & 0x80)) {
433                 r = set_hwstat(status | 0x80);
434                 if (r < 0)
435                         return r;
436                 r = get_hwstat(&status);
437                 if (r < 0)
438                         return r;
439         }
440
441         status &= ~0x80;
442         r = set_hwstat(status);
443         if (r < 0)
444                 return r;
445
446         r = get_hwstat(&status);
447         if (r < 0)
448                 return r;
449
450         r = sync_intr(0x56);
451         if (r < 0)
452                 return r;
453
454         return 0;
455 }
456
457 static void sighandler(int signum)
458 {
459         do_exit = 1;    
460 }
461
462 int main(void)
463 {
464         struct sigaction sigact;
465         int r = 1;
466
467         r = libusb_init();
468         if (r < 0) {
469                 fprintf(stderr, "failed to initialise libusb\n");
470                 exit(1);
471         }
472
473         r = find_dpfp_device();
474         if (r < 0) {
475                 fprintf(stderr, "Could not find/open device\n");
476                 goto out;
477         }
478
479         r = libusb_claim_interface(devh, 0);
480         if (r < 0) {
481                 fprintf(stderr, "usb_claim_interface error %d %s\n", r, strerror(-r));
482                 goto out;
483         }
484         printf("claimed interface\n");
485
486         r = print_f0_data();
487         if (r < 0)
488                 goto out_release;
489
490         r = do_init();
491         if (r < 0)
492                 goto out_deinit;
493
494         /* async from here onwards */
495
496         r = init_capture();
497         if (r < 0)
498                 goto out_deinit;
499
500         sigact.sa_handler = sighandler;
501         sigemptyset(&sigact.sa_mask);
502         sigact.sa_flags = 0;
503         sigaction(SIGINT, &sigact, NULL);
504         sigaction(SIGTERM, &sigact, NULL);
505         sigaction(SIGQUIT, &sigact, NULL);
506
507         while (!do_exit) {
508                 r = libusb_poll();
509                 if (r < 0)
510                         goto out_deinit;
511         }
512
513         printf("shutting down...\n");
514
515         r = libusb_urb_handle_cancel_sync(devh, irq_urbh);
516         if (r < 0)
517                 goto out_deinit;
518
519         r = libusb_urb_handle_cancel_sync(devh, img_urbh);
520         if (r < 0)
521                 goto out_deinit;
522         
523         if (do_exit == 1)
524                 r = 0;
525         else
526                 r = 1;
527
528 out_deinit:
529         libusb_urb_handle_free(img_urbh);
530         libusb_urb_handle_free(irq_urbh);
531         set_mode(0);
532         set_hwstat(0x80);
533 out_release:
534         libusb_release_interface(devh, 0);
535 out:
536         libusb_close(devh);
537         libusb_exit();
538         return r >= 0 ? r : -r;
539 }
540