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