
These are easiest to construct on the stack, but in the async case it is likely that the submitting function returns (hence stack is destroyed) before URB completion.
556 lines
11 KiB
C
556 lines
11 KiB
C
/*
|
|
* fpusb example program to manipulate U.are.U 4000B fingerprint scanner.
|
|
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
|
*
|
|
* Basic image capture program only, does not consider the powerup quirks or
|
|
* the fact that image encryption may be enabled. Not expected to work
|
|
* flawlessly all of the time.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <libfpusb/fpusb.h>
|
|
|
|
#define EP_INTR (1 | USB_ENDPOINT_IN)
|
|
#define EP_DATA (2 | USB_ENDPOINT_IN)
|
|
#define CTRL_IN (USB_TYPE_VENDOR | USB_ENDPOINT_IN)
|
|
#define CTRL_OUT (USB_TYPE_VENDOR | USB_ENDPOINT_OUT)
|
|
#define USB_RQ 0x04
|
|
#define INTR_LENGTH 64
|
|
|
|
enum {
|
|
MODE_INIT = 0x00,
|
|
MODE_AWAIT_FINGER_ON = 0x10,
|
|
MODE_AWAIT_FINGER_OFF = 0x12,
|
|
MODE_CAPTURE = 0x20,
|
|
MODE_SHUT_UP = 0x30,
|
|
MODE_READY = 0x80,
|
|
};
|
|
|
|
static int next_state(void);
|
|
static int submit_irq_urb(void);
|
|
static int submit_img_urb(void);
|
|
|
|
enum {
|
|
STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1,
|
|
STATE_AWAIT_IRQ_FINGER_DETECTED,
|
|
STATE_AWAIT_MODE_CHANGE_CAPTURE,
|
|
STATE_AWAIT_IMAGE,
|
|
STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF,
|
|
STATE_AWAIT_IRQ_FINGER_REMOVED,
|
|
};
|
|
|
|
static int state = 0;
|
|
static struct fpusb_dev_handle *devh = NULL;
|
|
static unsigned char imgbuf[0x1b340];
|
|
static unsigned char irqbuf[INTR_LENGTH];
|
|
static fpusb_urb_handle *img_urbh = NULL;
|
|
static fpusb_urb_handle *irq_urbh = NULL;
|
|
static int img_idx = 0;
|
|
static int do_exit = 0;
|
|
|
|
static struct fpusb_bulk_msg imgmsg = {
|
|
.endpoint = EP_DATA,
|
|
.data = imgbuf,
|
|
.length = sizeof(imgbuf),
|
|
};
|
|
|
|
static struct fpusb_bulk_msg irqmsg = {
|
|
.endpoint = EP_INTR,
|
|
.data = irqbuf,
|
|
.length = sizeof(irqbuf),
|
|
};
|
|
|
|
static struct fpusb_dev *find_dpfp_device(void)
|
|
{
|
|
struct fpusb_dev *dev;
|
|
|
|
fpusb_find_devices();
|
|
|
|
for (dev = fpusb_get_devices(); dev; dev = fpusb_dev_next(dev)) {
|
|
struct usb_dev_descriptor *desc = fpusb_dev_get_descriptor(dev);
|
|
if (desc->idVendor == 0x05ba && desc->idProduct == 0x000a)
|
|
return dev;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int print_f0_data(void)
|
|
{
|
|
unsigned char data[0x10];
|
|
struct fpusb_ctrl_msg msg = {
|
|
.requesttype = CTRL_IN,
|
|
.request = USB_RQ,
|
|
.value = 0xf0,
|
|
.index = 0,
|
|
.length = sizeof(data),
|
|
.data = data,
|
|
};
|
|
int r;
|
|
unsigned int i;
|
|
|
|
r = fpusb_ctrl_msg(devh, &msg, 0);
|
|
if (r < 0) {
|
|
fprintf(stderr, "F0 error %d\n", r);
|
|
return r;
|
|
}
|
|
if ((unsigned int) r < sizeof(data)) {
|
|
fprintf(stderr, "short read (%d)\n", r);
|
|
return -1;
|
|
}
|
|
|
|
printf("F0 data:");
|
|
for (i = 0; i < sizeof(data); i++)
|
|
printf("%02x ", data[i]);
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
static int get_hwstat(unsigned char *status)
|
|
{
|
|
struct fpusb_ctrl_msg msg = {
|
|
.requesttype = CTRL_IN,
|
|
.request = USB_RQ,
|
|
.value = 0x07,
|
|
.index = 0,
|
|
.length = 1,
|
|
.data = status,
|
|
};
|
|
int r;
|
|
|
|
r = fpusb_ctrl_msg(devh, &msg, 0);
|
|
if (r < 0) {
|
|
fprintf(stderr, "read hwstat error %d\n", r);
|
|
return r;
|
|
}
|
|
if ((unsigned int) r < 1) {
|
|
fprintf(stderr, "short read (%d)\n", r);
|
|
return -1;
|
|
}
|
|
|
|
printf("hwstat reads %02x\n", *status);
|
|
return 0;
|
|
}
|
|
|
|
static int set_hwstat(unsigned char data)
|
|
{
|
|
int r;
|
|
struct fpusb_ctrl_msg msg = {
|
|
.requesttype = CTRL_OUT,
|
|
.request = USB_RQ,
|
|
.value = 0x07,
|
|
.index = 0,
|
|
.length = 1,
|
|
.data = &data,
|
|
};
|
|
|
|
printf("set hwstat to %02x\n", data);
|
|
|
|
r = fpusb_ctrl_msg(devh, &msg, 0);
|
|
if (r < 0) {
|
|
fprintf(stderr, "set hwstat error %d\n", r);
|
|
return r;
|
|
}
|
|
if ((unsigned int) r < 1) {
|
|
fprintf(stderr, "short write (%d)", r);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int set_mode(unsigned char data)
|
|
{
|
|
int r;
|
|
struct fpusb_ctrl_msg msg = {
|
|
.requesttype = CTRL_OUT,
|
|
.request = USB_RQ,
|
|
.value = 0x4e,
|
|
.index = 0,
|
|
.length = 1,
|
|
.data = &data,
|
|
};
|
|
|
|
printf("set mode %02x\n", data);
|
|
|
|
r = fpusb_ctrl_msg(devh, &msg, 0);
|
|
if (r < 0) {
|
|
fprintf(stderr, "set mode error %d\n", r);
|
|
return r;
|
|
}
|
|
if ((unsigned int) r < 1) {
|
|
fprintf(stderr, "short write (%d)", r);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void cb_mode_changed(struct fpusb_dev_handle *_devh,
|
|
struct fpusb_urb_handle *urbh, enum fp_urb_cb_status status,
|
|
struct usb_ctrl_setup *setup, unsigned char *data, int actual_length,
|
|
void *user_data)
|
|
{
|
|
if (status != FP_URB_COMPLETED) {
|
|
fprintf(stderr, "mode change URB not completed!\n");
|
|
do_exit = 2;
|
|
}
|
|
|
|
printf("async cb_mode_changed\n");
|
|
if (next_state() < 0)
|
|
do_exit = 2;
|
|
}
|
|
|
|
static int set_mode_async(unsigned char data)
|
|
{
|
|
fpusb_urb_handle *urbh;
|
|
struct fpusb_ctrl_msg msg = {
|
|
.requesttype = CTRL_OUT,
|
|
.request = USB_RQ,
|
|
.value = 0x4e,
|
|
.index = 0,
|
|
.length = 1,
|
|
.data = &data,
|
|
};
|
|
|
|
printf("async set mode %02x\n", data);
|
|
|
|
urbh = fpusb_submit_ctrl_msg(devh, &msg, cb_mode_changed, NULL, 1000);
|
|
if (!urbh) {
|
|
fprintf(stderr, "set mode submit error\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_sync_intr(unsigned char *data)
|
|
{
|
|
struct fpusb_bulk_msg msg = {
|
|
.endpoint = EP_INTR,
|
|
.data = data,
|
|
.length = INTR_LENGTH,
|
|
};
|
|
int r;
|
|
int transferred;
|
|
|
|
r = fpusb_intr_msg(devh, &msg, &transferred, 1000);
|
|
if (r < 0) {
|
|
fprintf(stderr, "intr error %d\n", r);
|
|
return r;
|
|
}
|
|
if (transferred < INTR_LENGTH) {
|
|
fprintf(stderr, "short read (%d)\n", r);
|
|
return -1;
|
|
}
|
|
|
|
printf("recv interrupt %04x\n", *((uint16_t *) data));
|
|
return 0;
|
|
}
|
|
|
|
static int sync_intr(unsigned char type)
|
|
{
|
|
int r;
|
|
unsigned char data[INTR_LENGTH];
|
|
|
|
while (1) {
|
|
r = do_sync_intr(data);
|
|
if (r < 0)
|
|
return r;
|
|
if (data[0] == type)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int save_to_file(unsigned char *data)
|
|
{
|
|
FILE *fd;
|
|
char filename[64];
|
|
|
|
sprintf(filename, "finger%d.pgm", img_idx++);
|
|
fd = fopen(filename, "w");
|
|
if (!fd)
|
|
return -1;
|
|
|
|
fputs("P5 384 289 255 ", fd);
|
|
fwrite(data + 64, 1, 384*289, fd);
|
|
fclose(fd);
|
|
printf("saved image to %s\n", filename);
|
|
return 0;
|
|
}
|
|
|
|
static int next_state(void)
|
|
{
|
|
int r = 0;
|
|
printf("old state: %d\n", state);
|
|
switch (state) {
|
|
case STATE_AWAIT_IRQ_FINGER_REMOVED:
|
|
state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON;
|
|
r = set_mode_async(MODE_AWAIT_FINGER_ON);
|
|
break;
|
|
case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON:
|
|
state = STATE_AWAIT_IRQ_FINGER_DETECTED;
|
|
break;
|
|
case STATE_AWAIT_IRQ_FINGER_DETECTED:
|
|
state = STATE_AWAIT_MODE_CHANGE_CAPTURE;
|
|
r = set_mode_async(MODE_CAPTURE);
|
|
break;
|
|
case STATE_AWAIT_MODE_CHANGE_CAPTURE:
|
|
state = STATE_AWAIT_IMAGE;
|
|
break;
|
|
case STATE_AWAIT_IMAGE:
|
|
state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF;
|
|
r = set_mode_async(MODE_AWAIT_FINGER_OFF);
|
|
break;
|
|
case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF:
|
|
state = STATE_AWAIT_IRQ_FINGER_REMOVED;
|
|
break;
|
|
default:
|
|
printf("unrecognised state %d\n", state);
|
|
}
|
|
if (r < 0) {
|
|
fprintf(stderr, "error detected changing state");
|
|
return r;
|
|
}
|
|
|
|
printf("new state: %d\n", state);
|
|
return 0;
|
|
}
|
|
|
|
static void cb_irq(fpusb_dev_handle *_devh, fpusb_urb_handle *urbh,
|
|
enum fp_urb_cb_status status, unsigned char endpoint, int rqlength,
|
|
unsigned char *data, int actual_length, void *user_data)
|
|
{
|
|
unsigned char irqtype = data[0];
|
|
|
|
if (status != FP_URB_COMPLETED) {
|
|
fprintf(stderr, "irq URB status %d?\n", status);
|
|
do_exit = 2;
|
|
return;
|
|
}
|
|
|
|
printf("IRQ callback %02x\n", irqtype);
|
|
switch (state) {
|
|
case STATE_AWAIT_IRQ_FINGER_DETECTED:
|
|
if (irqtype == 0x01) {
|
|
if (next_state() < 0) {
|
|
do_exit = 2;
|
|
return;
|
|
}
|
|
} else {
|
|
printf("finger-on-sensor detected in wrong state!\n");
|
|
}
|
|
break;
|
|
case STATE_AWAIT_IRQ_FINGER_REMOVED:
|
|
if (irqtype == 0x02) {
|
|
if (next_state() < 0) {
|
|
do_exit = 2;
|
|
return;
|
|
}
|
|
} else {
|
|
printf("finger-on-sensor detected in wrong state!\n");
|
|
}
|
|
break;
|
|
}
|
|
if (submit_irq_urb() < 0)
|
|
do_exit = 2;
|
|
}
|
|
|
|
static void cb_img(fpusb_dev_handle *_devh, fpusb_urb_handle *urbh,
|
|
enum fp_urb_cb_status status, unsigned char endpoint, int rqlength,
|
|
unsigned char *data, int actual_length, void *user_data)
|
|
{
|
|
if (status != FP_URB_COMPLETED) {
|
|
fprintf(stderr, "img URB status %d?\n", status);
|
|
do_exit = 2;
|
|
return;
|
|
}
|
|
|
|
printf("Image callback\n");
|
|
save_to_file(imgbuf);
|
|
if (next_state() < 0) {
|
|
do_exit = 2;
|
|
return;
|
|
}
|
|
if (submit_img_urb() < 0)
|
|
do_exit = 2;
|
|
}
|
|
|
|
static int submit_irq_urb(void)
|
|
{
|
|
fpusb_urb_handle_free(irq_urbh);
|
|
irq_urbh = fpusb_submit_intr_msg(devh, &irqmsg, cb_irq, NULL, 0);
|
|
return irq_urbh != NULL;
|
|
}
|
|
|
|
static int submit_img_urb(void)
|
|
{
|
|
fpusb_urb_handle_free(img_urbh);
|
|
img_urbh = fpusb_submit_bulk_msg(devh, &imgmsg, cb_img, NULL, 0);
|
|
return img_urbh != NULL;
|
|
}
|
|
|
|
static int init_capture(void)
|
|
{
|
|
int r;
|
|
|
|
r = submit_irq_urb();
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = submit_img_urb();
|
|
if (r < 0) {
|
|
fpusb_urb_handle_cancel_sync(devh, img_urbh);
|
|
return r;
|
|
}
|
|
|
|
/* start state machine */
|
|
state = STATE_AWAIT_IRQ_FINGER_REMOVED;
|
|
return next_state();
|
|
}
|
|
|
|
static int do_init(void)
|
|
{
|
|
unsigned char status;
|
|
int r;
|
|
|
|
r = get_hwstat(&status);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (!(status & 0x80)) {
|
|
r = set_hwstat(status | 0x80);
|
|
if (r < 0)
|
|
return r;
|
|
r = get_hwstat(&status);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
status &= ~0x80;
|
|
r = set_hwstat(status);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = get_hwstat(&status);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sync_intr(0x56);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sighandler(int signum)
|
|
{
|
|
do_exit = 1;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
struct fpusb_dev *dev;
|
|
struct sigaction sigact;
|
|
int r = 1;
|
|
|
|
r = fpusb_init(0);
|
|
if (r < 0) {
|
|
fprintf(stderr, "failed to initialise fpusb\n");
|
|
exit(1);
|
|
}
|
|
|
|
dev = find_dpfp_device();
|
|
if (!dev) {
|
|
fprintf(stderr, "No device found\n");
|
|
goto out;
|
|
}
|
|
printf("found device\n");
|
|
|
|
devh = fpusb_devh_open(dev);
|
|
if (!devh) {
|
|
fprintf(stderr, "Could not open device\n");
|
|
goto out;
|
|
}
|
|
printf("opened device\n");
|
|
|
|
r = fpusb_devh_claim_intf(devh, 0);
|
|
if (r < 0) {
|
|
fprintf(stderr, "usb_claim_interface error %d %s\n", r, strerror(-r));
|
|
goto out;
|
|
}
|
|
printf("claimed interface\n");
|
|
|
|
r = print_f0_data();
|
|
if (r < 0)
|
|
goto out_release;
|
|
|
|
r = do_init();
|
|
if (r < 0)
|
|
goto out_deinit;
|
|
|
|
/* async from here onwards */
|
|
|
|
r = init_capture();
|
|
if (r < 0)
|
|
goto out_deinit;
|
|
|
|
sigact.sa_handler = sighandler;
|
|
sigemptyset(&sigact.sa_mask);
|
|
sigact.sa_flags = 0;
|
|
sigaction(SIGINT, &sigact, NULL);
|
|
sigaction(SIGTERM, &sigact, NULL);
|
|
sigaction(SIGQUIT, &sigact, NULL);
|
|
|
|
while (!do_exit) {
|
|
r = fpusb_poll();
|
|
if (r < 0)
|
|
goto out_deinit;
|
|
}
|
|
|
|
printf("shutting down...\n");
|
|
|
|
r = fpusb_urb_handle_cancel_sync(devh, irq_urbh);
|
|
if (r < 0)
|
|
goto out_deinit;
|
|
|
|
r = fpusb_urb_handle_cancel_sync(devh, img_urbh);
|
|
if (r < 0)
|
|
goto out_deinit;
|
|
|
|
if (do_exit == 1)
|
|
r = 0;
|
|
else
|
|
r = 1;
|
|
|
|
out_deinit:
|
|
fpusb_urb_handle_free(img_urbh);
|
|
fpusb_urb_handle_free(irq_urbh);
|
|
set_mode(0);
|
|
set_hwstat(0x80);
|
|
out_release:
|
|
fpusb_devh_release_intf(devh, 0);
|
|
out:
|
|
fpusb_devh_close(devh);
|
|
fpusb_exit();
|
|
return r >= 0 ? r : -r;
|
|
}
|
|
|