From aeeec97fb2efafdc22d55e505e28c41ff9d117ed Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 4 Feb 2008 10:24:27 +0000 Subject: [PATCH] upekts: port to asynchronous model --- libfprint/Makefile.am | 5 +- libfprint/core.c | 1 + libfprint/drivers/upekts.c | 1555 ++++++++++++++++++++++++------------ 3 files changed, 1069 insertions(+), 492 deletions(-) diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am index 88c3cf3..2a8f08e 100644 --- a/libfprint/Makefile.am +++ b/libfprint/Makefile.am @@ -8,8 +8,8 @@ AES2501_SRC = drivers/aes2501.c drivers/aes2501.h AES4000_SRC = drivers/aes4000.c FDU2000_SRC = drivers/fdu2000.c -#DRIVER_SRC = $(UPEKTS_SRC) $(URU4000_SRC) $(AES1610_SRC) $(AES2501_SRC) $(AES4000_SRC) $(UPEKTC_SRC) $(FDU2000_SRC) -DRIVER_SRC = +DRIVER_SRC = $(UPEKTS_SRC) +#DRIVER_SRC = $(URU4000_SRC) $(AES1610_SRC) $(AES2501_SRC) $(AES4000_SRC) $(UPEKTC_SRC) $(FDU2000_SRC) NBIS_SRC = \ nbis/include/bozorth.h \ @@ -56,6 +56,7 @@ libfprint_la_SOURCES = \ fp_internal.h \ core.c \ data.c \ + drv.c \ img.c \ imgdev.c \ aeslib.c \ diff --git a/libfprint/core.c b/libfprint/core.c index fdaa519..a7741ad 100644 --- a/libfprint/core.c +++ b/libfprint/core.c @@ -325,6 +325,7 @@ static void register_driver(struct fp_driver *drv) } static struct fp_driver * const primitive_drivers[] = { + &upekts_driver, }; static struct fp_img_driver * const img_drivers[] = { diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index caf5f6f..c63f779 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -1,6 +1,6 @@ /* * UPEK TouchStrip driver for libfprint - * Copyright (C) 2007 Daniel Drake + * Copyright (C) 2007-2008 Daniel Drake * * Based in part on libthinkfinger: * Copyright (C) 2006-2007 Timo Hoenig @@ -39,8 +39,14 @@ #define EP_OUT (2 | LIBUSB_ENDPOINT_OUT) #define TIMEOUT 5000 +#define MSG_READ_BUF_SIZE 0x40 +#define MAX_DATA_IN_READ_BUF (MSG_READ_BUF_SIZE - 9) + struct upekts_dev { - uint8_t seq; + gboolean enroll_passed; + gboolean first_verify_iteration; + gboolean stop_verify; + uint8_t seq; /* FIXME: improve/automate seq handling */ }; static const uint16_t crc_table[256] = { @@ -127,11 +133,10 @@ static uint16_t udf_crc(unsigned char *buffer, size_t size) #define CMD_SEQ_INCREMENT 0x10 -static int send_cmd(struct fp_dev *dev, unsigned char seq_a, - unsigned char seq_b, unsigned char *data, uint16_t len) +static void fill_send_cmd_urb(struct libusb_bulk_transfer *msg, + unsigned char seq_a, unsigned char seq_b, const unsigned char *data, + uint16_t len) { - int r; - int transferred; uint16_t crc; /* 9 bytes extra for: 4 byte 'Ciao', 1 byte A, 1 byte B | lenHI, @@ -139,17 +144,15 @@ static int send_cmd(struct fp_dev *dev, unsigned char seq_a, size_t urblen = len + 9; unsigned char *buf; - struct libusb_bulk_transfer msg = { - .endpoint = EP_OUT, - .length = urblen, - }; - if (!data && len > 0) { fp_err("len>0 but no data?"); - return -EINVAL; + return; } + msg->endpoint = EP_OUT; + msg->length = urblen; buf = g_malloc(urblen); + msg->data = buf; /* Write header */ strncpy(buf, "Ciao", 4); @@ -166,30 +169,17 @@ static int send_cmd(struct fp_dev *dev, unsigned char seq_a, crc = GUINT16_TO_BE(udf_crc(buf + 4, urblen - 6)); buf[urblen - 2] = crc >> 8; buf[urblen - 1] = crc & 0xff; - - msg.data = buf; - r = libusb_bulk_transfer(dev->udev, &msg, &transferred, TIMEOUT); - g_free(buf); - if (r < 0) { - fp_err("cmd write failed, code %d", r); - return r; - } else if ((unsigned int) transferred < urblen) { - fp_err("cmd write too short (%d/%d)", r, urblen); - return -EIO; - } - - return 0; } -static int send_cmd28(struct fp_dev *dev, unsigned char subcmd, - unsigned char *data, uint16_t innerlen) +static void fill_send_cmd28_urb(struct fp_dev *dev, + struct libusb_bulk_transfer *msg, unsigned char subcmd, + const unsigned char *data, uint16_t innerlen) { uint16_t _innerlen = innerlen; size_t len = innerlen + 6; unsigned char *buf = g_malloc0(len); struct upekts_dev *upekdev = dev->priv; uint8_t seq = upekdev->seq + CMD_SEQ_INCREMENT; - int r; fp_dbg("seq=%02x subcmd=%02x with %d bytes of data", seq, subcmd, innerlen); @@ -200,122 +190,88 @@ static int send_cmd28(struct fp_dev *dev, unsigned char subcmd, buf[5] = subcmd; memcpy(buf + 6, data, innerlen); - r = send_cmd(dev, 0, seq, buf, len); - if (r == 0) - upekdev->seq = seq; + fill_send_cmd_urb(msg, 0, seq, buf, len); + upekdev->seq = seq; g_free(buf); - return r; } -static int send_cmdresponse(struct fp_dev *dev, unsigned char seq, - unsigned char *data, uint8_t len) +static void fill_send_cmdresponse_urb(struct libusb_bulk_transfer *msg, + unsigned char seq, const unsigned char *data, uint8_t len) { fp_dbg("seq=%02x len=%d", seq, len); - return send_cmd(dev, seq, 0, data, len); -} - -static unsigned char *__read_msg(struct fp_dev *dev, size_t *data_len) -{ -#define MSG_READ_BUF_SIZE 0x40 -#define MAX_DATA_IN_READ_BUF (MSG_READ_BUF_SIZE - 9) - unsigned char *buf = g_malloc(MSG_READ_BUF_SIZE); - size_t buf_size = MSG_READ_BUF_SIZE; - uint16_t computed_crc, msg_crc; - uint16_t len; - int r; - int transferred; - - struct libusb_bulk_transfer msg = { - .endpoint = EP_IN, - .data = buf, - .length = buf_size, - }; - - r = libusb_bulk_transfer(dev->udev, &msg, &transferred, TIMEOUT); - if (r < 0) { - fp_err("msg read failed, code %d", r); - goto err; - } else if (transferred < 9) { - fp_err("msg read too short (%d/%d)", r, buf_size); - goto err; - } - - if (strncmp(buf, "Ciao", 4) != 0) { - fp_err("no Ciao for you!!"); - goto err; - } - - len = GUINT16_FROM_LE(((buf[5] & 0xf) << 8) | buf[6]); - - if (r != MSG_READ_BUF_SIZE && (len + 9) < r) { - /* Check that the length claimed inside the message is in line with - * the amount of data that was transferred over USB. */ - fp_err("msg didn't include enough data, expected=%d recv=%d", - len + 9, r); - goto err; - } - - /* We use a 64 byte buffer for reading messages. However, sometimes - * messages are longer, in which case we have to do another USB bulk read - * to read the remainder. This is handled below. */ - if (len > MAX_DATA_IN_READ_BUF) { - int needed = len - MAX_DATA_IN_READ_BUF; - struct libusb_bulk_transfer extend_msg = { - .endpoint = EP_IN, - .length = needed, - }; - - fp_dbg("didn't fit in buffer, need to extend by %d bytes", needed); - buf = g_realloc((gpointer) buf, MSG_READ_BUF_SIZE + needed); - extend_msg.data = buf + MSG_READ_BUF_SIZE; - r = libusb_bulk_transfer(dev->udev, &extend_msg, &transferred, TIMEOUT); - if (r < 0) { - fp_err("extended msg read failed, code %d", r); - goto err; - } else if (transferred < needed) { - fp_err("extended msg short read (%d/%d)", r, needed); - goto err; - } - buf_size += needed; - } - - computed_crc = udf_crc(buf + 4, len + 3); - msg_crc = GUINT16_FROM_LE((buf[len + 8] << 8) | buf[len + 7]); - if (computed_crc != msg_crc) { - fp_err("CRC failed, got %04x expected %04x", msg_crc, computed_crc); - goto err; - } - - *data_len = buf_size; - return buf; -err: - g_free(buf); - return NULL; + fill_send_cmd_urb(msg, seq, 0, data, len); } enum read_msg_status { - READ_MSG_ERROR = -1, - READ_MSG_CMD = 1, - READ_MSG_RESPONSE = 2, + READ_MSG_ERROR, + READ_MSG_CMD, + READ_MSG_RESPONSE, }; -static enum read_msg_status read_msg(struct fp_dev *dev, uint8_t *seq, - unsigned char *subcmd, unsigned char **data, size_t *data_len) -{ -#define MSG_READ_BUF_SIZE 0x40 -#define MAX_DATA_IN_READ_BUF (MSG_READ_BUF_SIZE - 9) - unsigned char *buf; - size_t buf_size; - unsigned char code_a; - unsigned char code_b; - uint16_t len; - enum read_msg_status ret = READ_MSG_ERROR; +typedef void (*read_msg_cb_fn)(struct fp_dev *dev, enum read_msg_status status, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data); -retry: - buf = __read_msg(dev, &buf_size); - if (!buf) - return READ_MSG_ERROR; +struct read_msg_data { + struct fp_dev *dev; + read_msg_cb_fn callback; + void *user_data; +}; + +static int __read_msg_async(struct read_msg_data *udata); + +#define READ_MSG_DATA_CB_ERR(udata) (udata)->callback((udata)->dev, \ + READ_MSG_ERROR, 0, 0, NULL, 0, (udata)->user_data) + +static void busy_ack_sent_cb(libusb_dev_handle *devh, libusb_urb_handle *urbh, + enum libusb_urb_cb_status status, unsigned char endpoint, int rqlength, + unsigned char *data, int actual_length, void *_data) +{ + struct read_msg_data *udata = (struct read_msg_data *) _data; + if (status != FP_URB_COMPLETED || rqlength != actual_length) { + READ_MSG_DATA_CB_ERR(udata); + g_free(udata); + } else { + int r = __read_msg_async(udata); + if (r < 0) { + READ_MSG_DATA_CB_ERR(udata); + g_free(udata); + } + } + libusb_urb_handle_free(urbh); +} + +static int busy_ack_retry_read(struct read_msg_data *udata) +{ + struct libusb_bulk_transfer msg; + libusb_urb_handle *urbh; + + fill_send_cmdresponse_urb(&msg, 0x09, NULL, 0); + urbh = libusb_async_bulk_transfer(udata->dev->udev, &msg, busy_ack_sent_cb, + udata, TIMEOUT); + if (!urbh) { + g_free(msg.data); + return -EIO; + } + return 0; +} + +/* Returns 0 if message was handled, 1 if it was a device-busy message, and + * negative on error. */ +static int __handle_incoming_msg(struct read_msg_data *udata, + unsigned char *buf) +{ + uint16_t len = GUINT16_FROM_LE(((buf[5] & 0xf) << 8) | buf[6]); + uint16_t computed_crc = udf_crc(buf + 4, len + 3); + uint16_t msg_crc = GUINT16_FROM_LE((buf[len + 8] << 8) | buf[len + 7]); + unsigned char *retdata = NULL; + unsigned char code_a, code_b; + + if (computed_crc != msg_crc) { + fp_err("CRC failed, got %04x expected %04x", msg_crc, computed_crc); + return -1; + } code_a = buf[4]; code_b = buf[5] & 0xf0; @@ -327,23 +283,19 @@ retry: fp_dbg("cmd %x from device to driver", code_a); if (code_a == 0x08) { + int r; fp_dbg("device busy, send busy-ack"); - send_cmdresponse(dev, 0x09, NULL, 0); - g_free(buf); - goto retry; + r = busy_ack_retry_read(udata); + return (r < 0) ? r : 1; } - if (seq) - *seq = code_a; - if (data) { - if (len > 0) { - unsigned char *tmp = g_malloc(len); - memcpy(tmp, buf + 7, len); - *data = tmp; - } - *data_len = len; + if (len > 0) { + retdata = g_malloc(len); + memcpy(retdata, buf + 7, len); } - ret = READ_MSG_CMD; + udata->callback(udata->dev, READ_MSG_CMD, code_a, 0, retdata, len, + udata->user_data); + g_free(retdata); } else if (!code_a) { /* device sends response to a previously executed command */ unsigned char *innerbuf = buf + 7; @@ -352,70 +304,167 @@ retry: if (len < 6) { fp_err("cmd response too short (%d)", len); - goto out; + return -1; } if (innerbuf[0] != 0x28) { fp_err("cmd response without 28 byte?"); - goto out; + return -1; } if (innerbuf[3] || innerbuf[4]) { fp_err("non-zero bytes in cmd response"); - goto out; + return -1; } innerlen = innerbuf[1] | (innerbuf[2] << 8); innerlen = GUINT16_FROM_LE(innerlen) - 3; _subcmd = innerbuf[5]; fp_dbg("device responds to subcmd %x with %d bytes", _subcmd, innerlen); - if (seq) - *seq = code_b; - if (subcmd) - *subcmd = _subcmd; - if (data) { - if (innerlen > 0) { - unsigned char *tmp = g_malloc(innerlen); - memcpy(tmp, innerbuf + 6, innerlen); - *data = tmp; - } - *data_len = innerlen; + if (innerlen > 0) { + retdata = g_malloc(innerlen); + memcpy(retdata, innerbuf + 6, innerlen); } - ret = READ_MSG_RESPONSE; + udata->callback(udata->dev, READ_MSG_RESPONSE, code_b, _subcmd, + retdata, innerlen, udata->user_data); + g_free(retdata); } else { fp_err("don't know how to handle this message"); + return -1; } - -out: - g_free(buf); - return ret; + return 0; } -static int read_msg28(struct fp_dev *dev, unsigned char subcmd, - unsigned char **data, size_t *data_len) +static void read_msg_extend_cb(libusb_dev_handle *devh, libusb_urb_handle *urbh, + enum libusb_urb_cb_status status, unsigned char endpoint, int rqlength, + unsigned char *data, int actual_length, void *user_data) { - struct upekts_dev *upekdev = dev->priv; - uint8_t _seq; - unsigned char _subcmd; - enum read_msg_status msgstat; + struct read_msg_data *udata = user_data; + unsigned char *buf = data - MSG_READ_BUF_SIZE; + int handle_result = 0; - msgstat = read_msg(dev, &_seq, &_subcmd, data, data_len); - if (msgstat != READ_MSG_RESPONSE) { - fp_err("expected response, got %d seq=%x", msgstat, _seq); - return -EPROTO; + if (status != FP_URB_COMPLETED) { + fp_err("extended msg read failed, code %d", status); + goto err; } - if (_subcmd != subcmd) { - fp_warn("expected response to subcmd %02x, got response to %02x", - subcmd, _subcmd); - return -EPROTO; - } - if (_seq != upekdev->seq) { - fp_err("expected response to cmd seq=%02x, got response to %02x", - upekdev->seq, _seq); - return -EPROTO; + if (actual_length < rqlength) { + fp_err("extended msg short read (%d/%d)", actual_length, rqlength); + goto err; } + handle_result = __handle_incoming_msg(udata, buf); + if (handle_result < 0) + goto err; + goto out; + +err: + READ_MSG_DATA_CB_ERR(udata); +out: + if (handle_result != 1) + g_free(udata); + g_free(buf); + libusb_urb_handle_free(urbh); +} + +static void read_msg_cb(libusb_dev_handle *devh, libusb_urb_handle *urbh, + enum libusb_urb_cb_status status, unsigned char endpoint, + int rqlength, unsigned char *data, int actual_length, void *user_data) +{ + struct read_msg_data *udata = user_data; + uint16_t len; + int handle_result = 0; + + if (status != FP_URB_COMPLETED) { + fp_err("async msg read failed, code %d", status); + goto err; + } + if (actual_length < 9) { + fp_err("async msg read too short (%d)", actual_length); + goto err; + } + + if (strncmp(data, "Ciao", 4) != 0) { + fp_err("no Ciao for you!!"); + goto err; + } + + len = GUINT16_FROM_LE(((data[5] & 0xf) << 8) | data[6]); + if (actual_length != MSG_READ_BUF_SIZE && (len + 9) > actual_length) { + /* Check that the length claimed inside the message is in line with + * the amount of data that was transferred over USB. */ + fp_err("msg didn't include enough data, expected=%d recv=%d", + len + 9, actual_length); + goto err; + } + + /* We use a 64 byte buffer for reading messages. However, sometimes + * messages are longer, in which case we have to do another USB bulk read + * to read the remainder. This is handled below. */ + if (len > MAX_DATA_IN_READ_BUF) { + int needed = len - MAX_DATA_IN_READ_BUF; + libusb_urb_handle *eurbh; + struct libusb_bulk_transfer extend_msg = { + .endpoint = EP_IN, + .length = needed, + }; + + fp_dbg("didn't fit in buffer, need to extend by %d bytes", needed); + data = g_realloc((gpointer) data, MSG_READ_BUF_SIZE + needed); + extend_msg.data = data + MSG_READ_BUF_SIZE; + eurbh = libusb_async_bulk_transfer(udata->dev->udev, &extend_msg, + read_msg_extend_cb, udata, TIMEOUT); + if (!eurbh) { + fp_err("extended read submission failed"); + goto err; + } + libusb_urb_handle_free(urbh); + return; + } + + handle_result = __handle_incoming_msg(udata, data); + if (handle_result < 0) + goto err; + goto out; + +err: + READ_MSG_DATA_CB_ERR(udata); +out: + libusb_urb_handle_free(urbh); + if (handle_result != 1) + g_free(udata); + g_free(data); +} + +static int __read_msg_async(struct read_msg_data *udata) +{ + unsigned char *buf = g_malloc(MSG_READ_BUF_SIZE); + struct libusb_bulk_transfer msg = { + .endpoint = EP_IN, + .data = buf, + .length = MSG_READ_BUF_SIZE, + }; + libusb_urb_handle *urbh = libusb_async_bulk_transfer(udata->dev->udev, &msg, + read_msg_cb, udata, TIMEOUT); + if (!urbh) { + g_free(buf); + return -EIO; + } return 0; } +static int read_msg_async(struct fp_dev *dev, read_msg_cb_fn callback, + void *user_data) +{ + struct read_msg_data *udata = g_malloc(sizeof(*udata)); + int r; + + udata->dev = dev; + udata->callback = callback; + udata->user_data = user_data; + r = __read_msg_async(udata); + if (r) + g_free(udata); + return r; +} + static const unsigned char init_resp03[] = { 0x01, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xff, 0x07 }; @@ -439,115 +488,342 @@ static const unsigned char init28_0b[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 }; -static int do_init(struct fp_dev *dev) +/* device initialisation state machine */ + +enum initsm_states { + WRITE_CTRL400 = 0, + READ_MSG03, + SEND_RESP03, + READ_MSG05, + SEND28_06, + READ28_06, + SEND28_07, + READ28_07, + SEND28_08, + READ28_08, + SEND28_0C, + READ28_0C, + SEND28_0B, + READ28_0B, + INITSM_NUM_STATES, +}; + +static void initsm_read_msg_response_cb(struct fpi_ssm *ssm, + enum read_msg_status status, uint8_t seq, + unsigned char expect_subcmd, unsigned char subcmd) { - enum read_msg_status msgstat; - unsigned char dummy = 0x10; - uint8_t seq; - int r; + struct fp_dev *dev = ssm->dev; + struct upekts_dev *upekdev = dev->priv; - struct libusb_control_transfer msg = { - .requesttype = LIBUSB_TYPE_VENDOR | LIBUSB_RECIP_DEVICE, - .request = 0x0c, - .value = 0x0100, - .index = 0x0400, - .length = sizeof(dummy), - .data = &dummy, - }; - - r = libusb_control_transfer(dev->udev, &msg, TIMEOUT); - if (r < 0) { - fp_dbg("control write failed\n"); - return r; + if (status != READ_MSG_RESPONSE) { + fp_err("expected response, got %d seq=%x in state %d", status, seq, + ssm->cur_state); + fpi_ssm_mark_aborted(ssm, -1); + } else if (subcmd != expect_subcmd) { + fp_warn("expected response to subcmd 0x%02x, got response to %02x in " + "state %d", expect_subcmd, subcmd, ssm->cur_state); + fpi_ssm_mark_aborted(ssm, -1); + } else if (seq != upekdev->seq) { + fp_err("expected response to cmd seq=%02x, got response to %02x " + "in state %d", upekdev->seq, seq, ssm->cur_state); + fpi_ssm_mark_aborted(ssm, -1); + } else { + fp_dbg("state %d completed", ssm->cur_state); + fpi_ssm_next_state(ssm); } - - msgstat = read_msg(dev, &seq, NULL, NULL, NULL); - if (msgstat != READ_MSG_CMD) { - fp_err("expected command, got %d seq=%x", msgstat, seq); - return -EPROTO; - } - if (seq != 3) { - fp_err("expected seq=3, got %x", seq); - return -EPROTO; - } - - r = send_cmdresponse(dev, ++seq, (unsigned char *) init_resp03, - sizeof(init_resp03)); - if (r < 0) - return r; - - msgstat = read_msg(dev, &seq, NULL, NULL, NULL); - if (msgstat != READ_MSG_CMD) { - fp_err("expected command, got %d seq=%x", msgstat, seq); - return -EPROTO; - } - if (seq != 5) { - fp_err("expected seq=5, got %x", seq); - return -EPROTO; - } - - dummy = 0x04; - r = send_cmd28(dev, 0x06, &dummy, 1); - if (r < 0) - return r; - if (read_msg28(dev, 0x06, NULL, NULL) < 0) - return r; - - dummy = 0x04; - r = send_cmd28(dev, 0x07, &dummy, 1); - if (r < 0) - return r; - if (read_msg28(dev, 0x07, NULL, NULL) < 0) - return r; - - r = send_cmd28(dev, 0x08, (unsigned char *) init28_08, - sizeof(init28_08)); - if (r < 0) - return r; - if (read_msg28(dev, 0x08, NULL, NULL) < 0) - return r; - - r = send_cmd28(dev, 0x0c, (unsigned char *) init28_0c, - sizeof(init28_0c)); - if (r < 0) - return r; - if (read_msg28(dev, 0x0c, NULL, NULL) < 0) - return r; - - r = send_cmd28(dev, 0x0b, (unsigned char *) init28_0b, - sizeof(init28_0b)); - if (r < 0) - return r; - if (read_msg28(dev, 0x0b, NULL, NULL) < 0) - return r; - - return 0; } -static int do_deinit(struct fp_dev *dev) +static void read28_0b_cb(struct fp_dev *dev, enum read_msg_status status, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data) { - unsigned char dummy = 0; - enum read_msg_status msgstat; - uint8_t seq; - int r; + initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq, + 0x0b, subcmd); +} - /* FIXME: either i've misunderstood the message system or this is illegal - * here, since we arent responding to anything. */ - r = send_cmdresponse(dev, 0x07, &dummy, 1); - if (r < 0) - return r; - - msgstat = read_msg(dev, &seq, NULL, NULL, NULL); - if (msgstat != READ_MSG_CMD) { - fp_err("expected command, got %d seq=%x", msgstat, seq); - return -EPROTO; +static void read28_0c_cb(struct fp_dev *dev, enum read_msg_status status, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data) +{ + initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq, + 0x0c, subcmd); +} + +static void read28_08_cb(struct fp_dev *dev, enum read_msg_status status, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data) +{ + initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq, + 0x08, subcmd); +} + +static void read28_07_cb(struct fp_dev *dev, enum read_msg_status status, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data) +{ + initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq, + 0x07, subcmd); +} + +static void read28_06_cb(struct fp_dev *dev, enum read_msg_status status, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data) +{ + initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq, + 0x06, subcmd); +} + +static void initsm_read_msg_cmd_cb(struct fpi_ssm *ssm, + enum read_msg_status status, uint8_t expect_seq, uint8_t seq) +{ + struct fp_dev *dev = ssm->dev; + struct upekts_dev *upekdev = dev->priv; + + if (status == READ_MSG_ERROR) { + fpi_ssm_mark_aborted(ssm, -1); + return; + } else if (status != READ_MSG_CMD) { + fp_err("expected command, got %d seq=%x in state %d", status, seq, + ssm->cur_state); + fpi_ssm_mark_aborted(ssm, -1); + return; } + upekdev->seq = seq; + if (seq != expect_seq) { + fp_err("expected seq=%x, got %x in state %d", expect_seq, seq, + ssm->cur_state); + fpi_ssm_mark_aborted(ssm, -1); + return; + } + + fpi_ssm_next_state(ssm); +} + +static void read_msg05_cb(struct fp_dev *dev, enum read_msg_status status, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data) +{ + initsm_read_msg_cmd_cb((struct fpi_ssm *) user_data, status, 5, seq); +} + +static void read_msg03_cb(struct fp_dev *dev, enum read_msg_status status, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data) +{ + initsm_read_msg_cmd_cb((struct fpi_ssm *) user_data, status, 3, seq); +} + +static void ctrl400_cb(libusb_dev_handle *devh, libusb_urb_handle *urbh, + enum libusb_urb_cb_status status, struct libusb_ctrl_setup *setup, + unsigned char *data, int actual_length, void *user_data) +{ + struct fpi_ssm *ssm = user_data; + if (status == FP_URB_COMPLETED) + fpi_ssm_next_state(ssm); + else + fpi_ssm_mark_aborted(ssm, -1); + libusb_urb_handle_free(urbh); +} + +static void initsm_read_msg_handler(struct fpi_ssm *ssm, + read_msg_cb_fn callback) +{ + int r = read_msg_async(ssm->dev, callback, ssm); + if (r < 0) { + fp_err("async read msg failed in state %d", ssm->cur_state); + fpi_ssm_mark_aborted(ssm, r); + } +} + +static void initsm_send_msg_cb(libusb_dev_handle *devh, libusb_urb_handle *urbh, + enum libusb_urb_cb_status status, unsigned char endpoint, + int rqlength, unsigned char *data, int actual_length, void *user_data) +{ + struct fpi_ssm *ssm = user_data; + if (status == FP_URB_COMPLETED && rqlength == actual_length) { + fp_dbg("state %d completed", ssm->cur_state); + fpi_ssm_next_state(ssm); + } else { + fp_err("failed, state=%d rqlength=%d actual_length=%d", ssm->cur_state, + rqlength, actual_length); + fpi_ssm_mark_aborted(ssm, -1); + } + libusb_urb_handle_free(urbh); +} + +static void initsm_send_msg28_handler(struct fpi_ssm *ssm, + unsigned char subcmd, const unsigned char *data, uint16_t innerlen) +{ + struct fp_dev *dev = ssm->dev; + struct libusb_urb_handle *urbh; + struct libusb_bulk_transfer trf; + + fill_send_cmd28_urb(dev, &trf, subcmd, data, innerlen); + urbh = libusb_async_bulk_transfer(dev->udev, &trf, initsm_send_msg_cb, + ssm, TIMEOUT); + if (!urbh) { + fp_err("urb submission failed in state %d", ssm->cur_state); + g_free(trf.data); + fpi_ssm_mark_aborted(ssm, -EIO); + } +} + +static void initsm_run_state(struct fpi_ssm *ssm) +{ + struct fp_dev *dev = ssm->dev; + struct upekts_dev *upekdev = dev->priv; + struct libusb_urb_handle *urbh; + + switch (ssm->cur_state) { + case WRITE_CTRL400: ; + unsigned char dummy = 0x10; + struct libusb_control_transfer ctrl400_trf = { + .requesttype = LIBUSB_TYPE_VENDOR | LIBUSB_RECIP_DEVICE, + .request = 0x0c, + .value = 0x0100, + .index = 0x0400, + .length = sizeof(dummy), + .data = &dummy, + }; + + urbh = libusb_async_control_transfer(ssm->dev->udev, &ctrl400_trf, + ctrl400_cb, ssm, TIMEOUT); + if (!urbh) + fpi_ssm_mark_aborted(ssm, -1); + break; + case READ_MSG03: + initsm_read_msg_handler(ssm, read_msg03_cb); + break; + case SEND_RESP03: ; + struct libusb_bulk_transfer resp03_trf; + + fill_send_cmdresponse_urb(&resp03_trf, ++upekdev->seq, init_resp03, + sizeof(init_resp03)); + urbh = libusb_async_bulk_transfer(dev->udev, &resp03_trf, + initsm_send_msg_cb, ssm, TIMEOUT); + if (!urbh) { + g_free(resp03_trf.data); + fpi_ssm_mark_aborted(ssm, -EIO); + } + break; + case READ_MSG05: + initsm_read_msg_handler(ssm, read_msg05_cb); + break; + case SEND28_06: ; + unsigned char dummy28_06 = 0x04; + upekdev->seq = 0xf0; + initsm_send_msg28_handler(ssm, 0x06, &dummy28_06, 1); + break; + case READ28_06: + initsm_read_msg_handler(ssm, read28_06_cb); + break; + case SEND28_07: ; + unsigned char dummy28_07 = 0x04; + initsm_send_msg28_handler(ssm, 0x07, &dummy28_07, 1); + break; + case READ28_07: + initsm_read_msg_handler(ssm, read28_07_cb); + break; + case SEND28_08: + initsm_send_msg28_handler(ssm, 0x08, init28_08, sizeof(init28_08)); + break; + case READ28_08: + initsm_read_msg_handler(ssm, read28_08_cb); + break; + case SEND28_0C: + initsm_send_msg28_handler(ssm, 0x0c, init28_0c, sizeof(init28_0c)); + break; + case READ28_0C: + initsm_read_msg_handler(ssm, read28_0c_cb); + break; + case SEND28_0B: + initsm_send_msg28_handler(ssm, 0x0b, init28_0b, sizeof(init28_0b)); + break; + case READ28_0B: + initsm_read_msg_handler(ssm, read28_0b_cb); + break; + } +} + +static struct fpi_ssm *initsm_new(struct fp_dev *dev) +{ + return fpi_ssm_new(dev, initsm_run_state, INITSM_NUM_STATES); +} + +enum deinitsm_states { + SEND_RESP07 = 0, + READ_MSG01, + DEINITSM_NUM_STATES, +}; + +static void send_resp07_cb(libusb_dev_handle *devh, libusb_urb_handle *urbh, + enum libusb_urb_cb_status status, unsigned char endpoint, + int rqlength, unsigned char *data, int actual_length, void *user_data) +{ + struct fpi_ssm *ssm = user_data; + if (status != FP_URB_COMPLETED) + fpi_ssm_mark_aborted(ssm, -EIO); + else if (rqlength != actual_length) + fpi_ssm_mark_aborted(ssm, -EPROTO); + else + fpi_ssm_next_state(ssm); + libusb_urb_handle_free(urbh); +} + +static void read_msg01_cb(struct fp_dev *dev, enum read_msg_status status, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data) +{ + struct fpi_ssm *ssm = user_data; + struct upekts_dev *upekdev = dev->priv; + + if (status == READ_MSG_ERROR) { + fpi_ssm_mark_aborted(ssm, -1); + return; + } else if (status != READ_MSG_CMD) { + fp_err("expected command, got %d seq=%x", status, seq); + fpi_ssm_mark_aborted(ssm, -1); + return; + } + upekdev->seq = seq; if (seq != 1) { fp_err("expected seq=1, got %x", seq); - return -EPROTO; + fpi_ssm_mark_aborted(ssm, -1); + return; } - return 0; + fpi_ssm_next_state(ssm); +} + +static void deinitsm_state_handler(struct fpi_ssm *ssm) +{ + struct fp_dev *dev = ssm->dev; + + switch (ssm->cur_state) { + case SEND_RESP07: ; + struct libusb_bulk_transfer msg; + struct libusb_urb_handle *urbh; + unsigned char dummy = 0; + + fill_send_cmdresponse_urb(&msg, 0x07, &dummy, 1); + urbh = libusb_async_bulk_transfer(dev->udev, &msg, send_resp07_cb, ssm, + TIMEOUT); + if (!urbh) { + g_free(msg.data); + fpi_ssm_mark_aborted(ssm, -EIO); + } + break; + case READ_MSG01: ; + int r = read_msg_async(dev, read_msg01_cb, ssm); + if (r < 0) + fpi_ssm_mark_aborted(ssm, r); + break; + } +} + +static struct fpi_ssm *deinitsm_new(struct fp_dev *dev) +{ + return fpi_ssm_new(dev, deinitsm_state_handler, DEINITSM_NUM_STATES); } static int dev_init(struct fp_dev *dev, unsigned long driver_data) @@ -564,6 +840,7 @@ static int dev_init(struct fp_dev *dev, unsigned long driver_data) dev->priv = upekdev; dev->nr_enroll_stages = 3; + fpi_drvcb_init_complete(dev, 0); return 0; } @@ -571,6 +848,7 @@ static void dev_exit(struct fp_dev *dev) { libusb_release_interface(dev->udev, 0); g_free(dev->priv); + fpi_drvcb_deinit_complete(dev); } static const unsigned char enroll_init[] = { @@ -583,134 +861,296 @@ static const unsigned char scan_comp[] = { /* used for enrollment and verification */ static const unsigned char poll_data[] = { 0x30, 0x01 }; -static int enroll(struct fp_dev *dev, gboolean initial, - int stage, struct fp_print_data **_data, struct fp_img **img) +enum enroll_start_sm_states { + RUN_INITSM = 0, + ENROLL_INIT, + READ_ENROLL_MSG28, + ENROLL_START_NUM_STATES, +}; + +/* Called when the device initialization state machine completes */ +static void enroll_start_sm_cb_initsm(struct fpi_ssm *initsm) { - unsigned char *data; - size_t data_len; - int r; - int result = 0; - int passed = 0; + struct fpi_ssm *enroll_start_ssm = initsm->priv; + int error = initsm->error; - if (initial) { - r = do_init(dev); - if (r < 0) - return r; + fpi_ssm_free(initsm); + if (error) + fpi_ssm_mark_aborted(enroll_start_ssm, error); + else + fpi_ssm_next_state(enroll_start_ssm); +} - r = send_cmd28(dev, 0x02, (unsigned char *) enroll_init, - sizeof(enroll_init)); - if (r < 0) - return r; +/* called when enroll init URB has completed */ +static void enroll_start_sm_cb_init(libusb_dev_handle *devh, + libusb_urb_handle *urbh, enum libusb_urb_cb_status status, + unsigned char endpoint, int rqlength, unsigned char *data, + int actual_length, void *user_data) +{ + struct fpi_ssm *ssm = user_data; + if (status != FP_URB_COMPLETED) + fpi_ssm_mark_aborted(ssm, -EIO); + else if (rqlength != actual_length) + fpi_ssm_mark_aborted(ssm, -EPROTO); + else + fpi_ssm_next_state(ssm); + libusb_urb_handle_free(urbh); +} + +static void enroll_start_sm_cb_msg28(struct fp_dev *dev, + enum read_msg_status status, uint8_t seq, unsigned char subcmd, + unsigned char *data, size_t data_len, void *user_data) +{ + struct upekts_dev *upekdev = dev->priv; + struct fpi_ssm *ssm = user_data; + + if (status != READ_MSG_RESPONSE) { + fp_err("expected response, got %d seq=%x", status, seq); + fpi_ssm_mark_aborted(ssm, -1); + } else if (subcmd != 0) { + fp_warn("expected response to subcmd 0, got response to %02x", + subcmd); + fpi_ssm_mark_aborted(ssm, -1); + } else if (seq != upekdev->seq) { + fp_err("expected response to cmd seq=%02x, got response to %02x", + upekdev->seq, seq); + fpi_ssm_mark_aborted(ssm, -1); + } else { + fpi_ssm_next_state(ssm); + } +} + +static void enroll_start_sm_run_state(struct fpi_ssm *ssm) +{ + struct fp_dev *dev = ssm->dev; + + switch (ssm->cur_state) { + case RUN_INITSM: ; + struct fpi_ssm *initsm = initsm_new(dev); + initsm->priv = ssm; + fpi_ssm_start(initsm, enroll_start_sm_cb_initsm); + break; + case ENROLL_INIT: ; + struct libusb_bulk_transfer msg; + struct libusb_urb_handle *urbh; + fill_send_cmd28_urb(dev, &msg, 0x02, enroll_init, sizeof(enroll_init)); + urbh = libusb_async_bulk_transfer(dev->udev, &msg, + enroll_start_sm_cb_init, ssm, TIMEOUT); + if (!urbh) { + g_free(msg.data); + fpi_ssm_mark_aborted(ssm, -EIO); + } + break; + case READ_ENROLL_MSG28: ; /* FIXME: protocol misunderstanding here. device receives response * to subcmd 0 after submitting subcmd 2? */ /* actually this is probably a poll response? does the above cmd * include a 30 01 poll somewhere? */ - if (read_msg28(dev, 0x00, NULL, NULL) < 0) - return -EPROTO; + int r = read_msg_async(dev, enroll_start_sm_cb_msg28, ssm); + if (r < 0) + fpi_ssm_mark_aborted(ssm, r); + break; + } +} + +static void enroll_iterate(struct fp_dev *dev); + +static void e_handle_resp00(struct fp_dev *dev, unsigned char *data, + size_t data_len) +{ + struct upekts_dev *upekdev = dev->priv; + unsigned char status; + int result = 0; + + if (data_len != 14) { + fp_err("received 3001 poll response of %d bytes?", data_len); + fpi_drvcb_enroll_stage_completed(dev, -EPROTO, NULL, NULL); + return; } - while (!result) { - unsigned char status; + status = data[5]; + fp_dbg("poll result = %02x", status); - r = send_cmd28(dev, 0x00, (unsigned char *) poll_data, - sizeof(poll_data)); - if (r < 0) - return r; - if (read_msg28(dev, 0x00, &data, &data_len) < 0) - return -EPROTO; - - if (data_len != 14) { - fp_err("received 3001 poll response of %d bytes?", data_len); - g_free(data); - return -EPROTO; + switch (status) { + case 0x0c: + case 0x0d: + case 0x0e: + /* if we previously completed a non-last enrollment stage, we'll + * get this code to indicate successful stage completion */ + if (upekdev->enroll_passed) { + result = FP_ENROLL_PASS; + upekdev->enroll_passed = FALSE; } + /* otherwise it just means "no news" so we poll again */ + break; + case 0x1c: /* FIXME what does this one mean? */ + case 0x0b: /* FIXME what does this one mean? */ + case 0x23: /* FIXME what does this one mean? */ + result = FP_ENROLL_RETRY; + break; + case 0x0f: /* scan taking too long, remove finger and try again */ + result = FP_ENROLL_RETRY_REMOVE_FINGER; + break; + case 0x1e: /* swipe too short */ + result = FP_ENROLL_RETRY_TOO_SHORT; + break; + case 0x24: /* finger not centered */ + result = FP_ENROLL_RETRY_CENTER_FINGER; + break; + case 0x20: + /* finger scanned successfully */ + /* need to look at the next poll result to determine if enrollment is + * complete or not */ + upekdev->enroll_passed = 1; + break; + case 0x00: /* enrollment complete */ + /* we can now expect the enrollment data on the next poll, so we + * have nothing to do here */ + break; + default: + fp_err("unrecognised scan status code %02x", status); + result = -EPROTO; + break; + } - status = data[5]; - fp_dbg("poll result = %02x", status); - - /* These codes indicate that we're waiting for a finger scan, so poll - * again */ - switch (status) { - case 0x0c: - case 0x0d: - case 0x0e: - /* no news, poll again */ - if (passed) - result = FP_ENROLL_PASS; - break; - case 0x1c: /* FIXME what does this one mean? */ - case 0x0b: /* FIXME what does this one mean? */ - case 0x23: /* FIXME what does this one mean? */ - result = FP_ENROLL_RETRY; - break; - case 0x0f: /* scan taking too long, remove finger and try again */ - result = FP_ENROLL_RETRY_REMOVE_FINGER; - break; - case 0x1e: /* swipe too short */ - result = FP_ENROLL_RETRY_TOO_SHORT; - break; - case 0x24: /* finger not centered */ - result = FP_ENROLL_RETRY_CENTER_FINGER; - break; - case 0x20: - /* finger scanned successfully */ - /* don't break out immediately, need to look at the next - * value to determine if enrollment is complete or not */ - passed = 1; - break; - case 0x00: - if (passed) - result = FP_ENROLL_COMPLETE; - break; - default: - fp_err("unrecognised scan status code %02x", status); - result = -EPROTO; - break; - } - g_free(data); + if (result) { + fpi_drvcb_enroll_stage_completed(dev, result, NULL, NULL); + if (result > 0) + enroll_iterate(dev); + } else { + enroll_iterate(dev); } /* FIXME: need to extend protocol research to handle the case when * enrolment fails, e.g. you scan a different finger on each stage */ + /* FIXME: should do proper tracking of when we expect cmd0 results and + * cmd2 results and enforce it */ +} - if (result == FP_ENROLL_COMPLETE) { - struct fp_print_data *fdata; - - r = send_cmd28(dev, 0x00, (unsigned char *) poll_data, - sizeof(poll_data)); - if (r < 0) - return r; - /* FIXME: protocol misunderstanding here. device receives response - * to subcmd 0 after submitting subcmd 2? */ - if (read_msg28(dev, 0x02, &data, &data_len) < 0) - return -EPROTO; - - if (data_len < sizeof(scan_comp)) { - fp_err("fingerprint data too short (%d bytes)", data_len); - result = -EPROTO; - goto comp_out; - } - if (memcmp(data, scan_comp, sizeof(scan_comp)) != 0) { - fp_err("unrecognised data prefix %x %x %x %x %x", - data[0], data[1], data[2], data[3], data[4]); - result = -EPROTO; - goto comp_out; - } - if (!_data) { - fp_err("complete but no data storage!"); - result = FP_ENROLL_COMPLETE; - goto comp_out; - } +static void e_handle_resp02(struct fp_dev *dev, unsigned char *data, + size_t data_len) +{ + struct fp_print_data *fdata = NULL; + int result = -EPROTO; + if (data_len < sizeof(scan_comp)) { + fp_err("fingerprint data too short (%d bytes)", data_len); + } else if (memcmp(data, scan_comp, sizeof(scan_comp)) != 0) { + fp_err("unrecognised data prefix %x %x %x %x %x", + data[0], data[1], data[2], data[3], data[4]); + } else { fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp)); - memcpy(fdata->data, data + sizeof(scan_comp), data_len - sizeof(scan_comp)); - *_data = fdata; -comp_out: - do_deinit(dev); - g_free(data); + memcpy(fdata->data, data + sizeof(scan_comp), + data_len - sizeof(scan_comp)); + + result = FP_ENROLL_COMPLETE; } - return result; + fpi_drvcb_enroll_stage_completed(dev, result, fdata, NULL); +} + +static void enroll_iterate_msg_cb(struct fp_dev *dev, + enum read_msg_status msgstat, uint8_t seq, unsigned char subcmd, + unsigned char *data, size_t data_len, void *user_data) +{ + if (msgstat != READ_MSG_RESPONSE) { + fp_err("expected response, got %d seq=%x", msgstat, seq); + fpi_drvcb_enroll_stage_completed(dev, -EPROTO, NULL, NULL); + return; + } + if (subcmd == 0) { + e_handle_resp00(dev, data, data_len); + } else if (subcmd == 2) { + e_handle_resp02(dev, data, data_len); + } else { + fp_err("unexpected subcmd %d", subcmd); + fpi_drvcb_enroll_stage_completed(dev, -EPROTO, NULL, NULL); + } + +} + +static void enroll_iterate_cmd_cb(libusb_dev_handle *devh, + libusb_urb_handle *urbh, enum libusb_urb_cb_status status, + unsigned char endpoint, int rqlength, unsigned char *data, + int actual_length, void *user_data) +{ + struct fp_dev *dev = user_data; + + if (status != FP_URB_COMPLETED) { + fpi_drvcb_enroll_stage_completed(dev, -EIO, NULL, NULL); + } else if (rqlength != actual_length) { + fpi_drvcb_enroll_stage_completed(dev, -EPROTO, NULL, NULL); + } else { + int r = read_msg_async(dev, enroll_iterate_msg_cb, NULL); + if (r < 0) + fpi_drvcb_enroll_stage_completed(dev, r, NULL, NULL); + } + libusb_urb_handle_free(urbh); +} + +static void enroll_iterate(struct fp_dev *dev) +{ + struct libusb_bulk_transfer msg; + struct libusb_urb_handle *urbh; + + fill_send_cmd28_urb(dev, &msg, 0x00, poll_data, sizeof(poll_data)); + urbh = libusb_async_bulk_transfer(dev->udev, &msg, enroll_iterate_cmd_cb, + dev, TIMEOUT); + if (!urbh) { + g_free(msg.data); + fpi_drvcb_enroll_stage_completed(dev, -EIO, NULL, NULL); + } +} + +static void enroll_started(struct fpi_ssm *ssm) +{ + struct fp_dev *dev = ssm->dev; + fpi_drvcb_enroll_started(dev, ssm->error); + + if (!ssm->error) + enroll_iterate(dev); + + fpi_ssm_free(ssm); +} + +static int enroll_start(struct fp_dev *dev) +{ + struct upekts_dev *upekdev = dev->priv; + + /* do_init state machine first */ + struct fpi_ssm *ssm = fpi_ssm_new(dev, enroll_start_sm_run_state, + ENROLL_START_NUM_STATES); + + upekdev->enroll_passed = FALSE; + fpi_ssm_start(ssm, enroll_started); + return 0; +} + +static void enroll_stop_deinit_cb(struct fpi_ssm *ssm) +{ + /* don't really care about errors */ + fpi_drvcb_enroll_stopped(ssm->dev); + fpi_ssm_free(ssm); +} + +static int enroll_stop(struct fp_dev *dev) +{ + struct fpi_ssm *ssm = deinitsm_new(dev); + fpi_ssm_start(ssm, enroll_stop_deinit_cb); + return 0; +} + +static void verify_stop_deinit_cb(struct fpi_ssm *ssm) +{ + /* don't really care about errors */ + fpi_drvcb_verify_stopped(ssm->dev); + fpi_ssm_free(ssm); +} + +static void do_verify_stop(struct fp_dev *dev) +{ + struct fpi_ssm *ssm = deinitsm_new(dev); + fpi_ssm_start(ssm, verify_stop_deinit_cb); } static const unsigned char verify_hdr[] = { @@ -719,117 +1159,250 @@ static const unsigned char verify_hdr[] = { 0x00 }; -static int verify(struct fp_dev *dev, struct fp_print_data *print, - struct fp_img **img) +enum { + VERIFY_RUN_INITSM = 0, + VERIFY_INIT, + VERIFY_NUM_STATES, +}; + +/* Called when the device initialization state machine completes */ +static void verify_start_sm_cb_initsm(struct fpi_ssm *initsm) { - size_t data_len = sizeof(verify_hdr) + print->length; - unsigned char *data; - int r; - unsigned char status; - gboolean need_poll = FALSE; - gboolean done = FALSE; + struct fpi_ssm *verify_start_ssm = initsm->priv; + if (initsm->error) + fpi_ssm_mark_aborted(verify_start_ssm, initsm->error); + else + fpi_ssm_next_state(verify_start_ssm); + fpi_ssm_free(initsm); +} - r = do_init(dev); - if (r < 0) - return r; +static void verify_init_2803_cb(libusb_dev_handle *devh, libusb_urb_handle *urbh, + enum libusb_urb_cb_status status, unsigned char endpoint, + int rqlength, unsigned char *data, int actual_length, void *user_data) +{ + struct fpi_ssm *ssm = user_data; + if (status != FP_URB_COMPLETED) + fpi_ssm_mark_aborted(ssm, -EIO); + else if (rqlength != actual_length) + fpi_ssm_mark_aborted(ssm, -EPROTO); + else + fpi_ssm_next_state(ssm); + libusb_urb_handle_free(urbh); +} - data = g_malloc(data_len); - memcpy(data, verify_hdr, sizeof(verify_hdr)); - memcpy(data + sizeof(verify_hdr), print->data, print->length); +static void verify_start_sm_run_state(struct fpi_ssm *ssm) +{ + struct fp_dev *dev = ssm->dev; - r = send_cmd28(dev, 0x03, data, data_len); - if (r < 0) - return r; - g_free(data); + switch (ssm->cur_state) { + case VERIFY_RUN_INITSM: ; + struct fpi_ssm *initsm = initsm_new(dev); + initsm->priv = ssm; + fpi_ssm_start(initsm, verify_start_sm_cb_initsm); + break; + case VERIFY_INIT: ; + struct fp_print_data *print = dev->verify_data; + size_t data_len = sizeof(verify_hdr) + print->length; + unsigned char *data = g_malloc(data_len); + struct libusb_bulk_transfer msg; + struct libusb_urb_handle *urbh; - while (!done) { - if (need_poll) { - r = send_cmd28(dev, 0x00, (unsigned char *) poll_data, - sizeof(poll_data)); - if (r < 0) - return r; - } else { - need_poll = TRUE; - } - if (read_msg28(dev, 0x00, &data, &data_len) < 0) - return -EPROTO; - - if (data_len != 14) { - fp_err("received 3001 poll response of %d bytes?", data_len); - r = -EPROTO; - goto out; - } - - status = data[5]; - fp_dbg("poll result = %02x", status); - - /* These codes indicate that we're waiting for a finger scan, so poll - * again */ - switch (status) { - case 0x0c: /* no news, poll again */ - break; - case 0x20: - fp_dbg("processing scan for verification"); - break; - case 0x00: - fp_dbg("good image"); - done = TRUE; - break; - case 0x1c: /* FIXME what does this one mean? */ - case 0x0b: /* FIXME what does this one mean? */ - case 0x23: /* FIXME what does this one mean? */ - r = FP_VERIFY_RETRY; - goto out; - case 0x0f: /* scan taking too long, remove finger and try again */ - r = FP_VERIFY_RETRY_REMOVE_FINGER; - goto out; - case 0x1e: /* swipe too short */ - r = FP_VERIFY_RETRY_TOO_SHORT; - goto out; - case 0x24: /* finger not centered */ - r = FP_VERIFY_RETRY_CENTER_FINGER; - goto out; - default: - fp_err("unrecognised verify status code %02x", status); - r = -EPROTO; - goto out; - } + memcpy(data, verify_hdr, sizeof(verify_hdr)); + memcpy(data + sizeof(verify_hdr), print->data, print->length); + fill_send_cmd28_urb(dev, &msg, 0x03, data, data_len); g_free(data); + + urbh = libusb_async_bulk_transfer(dev->udev, &msg, + verify_init_2803_cb, ssm, TIMEOUT); + if (!urbh) { + g_free(msg.data); + fpi_ssm_mark_aborted(ssm, -EIO); + } + break; + } +} + +static void verify_iterate(struct fp_dev *dev); + +static void v_handle_resp00(struct fp_dev *dev, unsigned char *data, + size_t data_len) +{ + unsigned char status; + int r = 0; + + if (data_len != 14) { + fp_err("received 3001 poll response of %d bytes?", data_len); + r = -EPROTO; + goto out; } - if (status == 0x00) { - /* poll again for verify result */ - r = send_cmd28(dev, 0x00, (unsigned char *) poll_data, - sizeof(poll_data)); - if (r < 0) - return r; - if (read_msg28(dev, 0x03, &data, &data_len) < 0) - return -EPROTO; - if (data_len < 2) { - fp_err("verify result abnormally short!"); - r = -EPROTO; - goto out; - } - if (data[0] != 0x12) { - fp_err("unexpected verify header byte %02x", data[0]); - r = -EPROTO; - goto out; - } - if (data[1] == 0x00) { - r = FP_VERIFY_NO_MATCH; - } else if (data[1] == 0x01) { - r = FP_VERIFY_MATCH; - } else { - fp_err("unrecognised verify result %02x", data[1]); - r = -EPROTO; - goto out; - } + status = data[5]; + fp_dbg("poll result = %02x", status); + + /* These codes indicate that we're waiting for a finger scan, so poll + * again */ + switch (status) { + case 0x0c: /* no news, poll again */ + break; + case 0x20: + fp_dbg("processing scan for verification"); + break; + case 0x00: + fp_dbg("good image"); + break; + case 0x1c: /* FIXME what does this one mean? */ + case 0x0b: /* FIXME what does this one mean? */ + case 0x23: /* FIXME what does this one mean? */ + r = FP_VERIFY_RETRY; + break; + case 0x0f: /* scan taking too long, remove finger and try again */ + r = FP_VERIFY_RETRY_REMOVE_FINGER; + break; + case 0x1e: /* swipe too short */ + r = FP_VERIFY_RETRY_TOO_SHORT; + break; + case 0x24: /* finger not centered */ + r = FP_VERIFY_RETRY_CENTER_FINGER; + break; + default: + fp_err("unrecognised verify status code %02x", status); + r = -EPROTO; } out: - do_deinit(dev); - g_free(data); - return r; + if (r) + fpi_drvcb_report_verify_result(dev, r, NULL); + if (r >= 0) + verify_iterate(dev); +} + +static void v_handle_resp03(struct fp_dev *dev, unsigned char *data, + size_t data_len) +{ + int r; + + if (data_len < 2) { + fp_err("verify result abnormally short!"); + r = -EPROTO; + } else if (data[0] != 0x12) { + fp_err("unexpected verify header byte %02x", data[0]); + r = -EPROTO; + } else if (data[1] == 0x00) { + r = FP_VERIFY_NO_MATCH; + } else if (data[1] == 0x01) { + r = FP_VERIFY_MATCH; + } else { + fp_err("unrecognised verify result %02x", data[1]); + r = -EPROTO; + } + fpi_drvcb_report_verify_result(dev, r, NULL); +} + +static void verify_rd2800_cb(struct fp_dev *dev, enum read_msg_status msgstat, + uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len, + void *user_data) +{ + struct upekts_dev *upekdev = dev->priv; + + if (msgstat != READ_MSG_RESPONSE) { + fp_err("expected response, got %d seq=%x", msgstat, seq); + fpi_drvcb_report_verify_result(dev, -EPROTO, NULL); + return; + } else if (seq != upekdev->seq) { + fp_err("expected response to cmd seq=%02x, got response to %02x", + upekdev->seq, seq); + fpi_drvcb_report_verify_result(dev, -EPROTO, NULL); + return; + } + + if (subcmd == 0) + v_handle_resp00(dev, data, data_len); + else if (subcmd == 3) + v_handle_resp03(dev, data, data_len); + else + fpi_drvcb_report_verify_result(dev, -EPROTO, NULL); +} + +static void verify_wr2800_cb(libusb_dev_handle *devh, libusb_urb_handle *urbh, + enum libusb_urb_cb_status status, unsigned char endpoint, + int rqlength, unsigned char *data, int actual_length, void *user_data) +{ + struct fp_dev *dev = user_data; + + if (status != FP_URB_COMPLETED) { + fpi_drvcb_report_verify_result(dev, -EIO, NULL); + } else if (rqlength != actual_length) { + fpi_drvcb_report_verify_result(dev, -EIO, NULL); + } else { + int r = read_msg_async(dev, verify_rd2800_cb, NULL); + if (r < 0) + fpi_drvcb_report_verify_result(dev, r, NULL); + } + libusb_urb_handle_free(urbh); +} + +static void verify_iterate(struct fp_dev *dev) +{ + struct upekts_dev *upekdev = dev->priv; + struct libusb_bulk_transfer msg; + + if (upekdev->stop_verify) { + do_verify_stop(dev); + return; + } + + /* FIXME: this doesn't flow well, should the first cmd be moved from + * verify init to here? */ + if (upekdev->first_verify_iteration) { + int r = read_msg_async(dev, verify_rd2800_cb, NULL); + upekdev->first_verify_iteration = FALSE; + if (r < 0) + fpi_drvcb_report_verify_result(dev, r, NULL); + } else { + struct libusb_urb_handle *urbh; + fill_send_cmd28_urb(dev, &msg, 0x00, poll_data, sizeof(poll_data)); + urbh = libusb_async_bulk_transfer(dev->udev, &msg, verify_wr2800_cb, + dev, TIMEOUT); + if (!urbh) { + g_free(msg.data); + fpi_drvcb_report_verify_result(dev, -EIO, NULL); + } + } +} + +static void verify_started(struct fpi_ssm *ssm) +{ + struct fp_dev *dev = ssm->dev; + struct upekts_dev *upekdev = dev->priv; + + fpi_drvcb_verify_started(dev, ssm->error); + if (!ssm->error) { + upekdev->first_verify_iteration = TRUE; + verify_iterate(dev); + } + + fpi_ssm_free(ssm); +} + +static int verify_start(struct fp_dev *dev) +{ + struct upekts_dev *upekdev = dev->priv; + struct fpi_ssm *ssm = fpi_ssm_new(dev, verify_start_sm_run_state, + VERIFY_NUM_STATES); + upekdev->stop_verify = FALSE; + fpi_ssm_start(ssm, verify_started); + return 0; +} + +static int verify_stop(struct fp_dev *dev, gboolean iterating) +{ + struct upekts_dev *upekdev = dev->priv; + + if (!iterating) + do_verify_stop(dev); + else + upekdev->stop_verify = TRUE; + return 0; } static const struct usb_id id_table[] = { @@ -843,8 +1416,10 @@ struct fp_driver upekts_driver = { .full_name = "UPEK TouchStrip", .id_table = id_table, .init = dev_init, - .exit = dev_exit, - .enroll = enroll, - .verify = verify, + .deinit = dev_exit, + .enroll_start = enroll_start, + .enroll_stop = enroll_stop, + .verify_start = verify_start, + .verify_stop = verify_stop, };