Hi, I have a Compal (JHL90) laptop featuring an integrated UPEK fingerprint sensor (probably model TCS4C), which have USB ID 147e:1000. This model wasn't supported by libfprint-0.1.0-pre2, that's why I decided to write a driver for it. To do so, I analyzed traffic on the USB bus between the device and a Linux version of BSAPI, a proprietary library provided by UPEK. I didn't disassemble any binary. As this device shares some similarities with the other device already supported by upeksonly driver, I chose to modify that driver to allow it to support both devices. By the way, I corrected a minor bug that was causing some garbage to appear in scanned image. So here comes the result. Apply the attached patch against libfprint-0.1.0-pre2. As I could only test it with my own device, I have no idea whether it still works for the other device already supported. Some testing would be welcome. In addition to the patch itself, I attached a text document describing technical aspects of the device. It may be useful to help support other similar UPEK devices. Cheers.
Hugo Grostabussiat
diff --git a/AUTHORS b/AUTHORS index cc82954..1cd2e51 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,3 +7,4 @@ Copyright (C) 2007 Cyrille Bagard Copyright (C) 2007 Vasily Khoruzhick Copyright (C) 2007 Jan-Michael Brummer <[email protected]> Copyright (C) 2007 Anthony Bretaudeau <[email protected]> +Copyright (C) 2010 Hugo Grostabussiat <[email protected]> diff --git a/libfprint/drivers/upeksonly.c b/libfprint/drivers/upeksonly.c index f41a6be..a458f25 100644 --- a/libfprint/drivers/upeksonly.c +++ b/libfprint/drivers/upeksonly.c @@ -2,6 +2,9 @@ * UPEK TouchStrip Sensor-Only driver for libfprint * Copyright (C) 2008 Daniel Drake <[email protected]> * + * TCS4C (USB ID 147e:1000) support: + * Copyright (C) 2010 Hugo Grostabussiat <[email protected]> + * * 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 @@ -32,6 +35,11 @@ #define NUM_BULK_TRANSFERS 24 #define MAX_ROWS 700 +enum { + UPEKSONLY_2016, + UPEKSONLY_1000, +}; + struct img_transfer_data { int idx; struct fp_img_dev *dev; @@ -60,6 +68,8 @@ struct sonly_dev { gboolean deactivating; uint8_t read_reg_result; + int dev_model; + struct fpi_ssm *loopsm; struct libusb_transfer *img_transfer[NUM_BULK_TRANSFERS]; struct img_transfer_data *img_transfer_data; @@ -155,8 +165,8 @@ static gboolean is_capturing(struct sonly_dev *sdev) static void handoff_img(struct fp_img_dev *dev) { struct sonly_dev *sdev = dev->priv; - size_t size = IMG_WIDTH * sdev->num_rows; - struct fp_img *img = fpi_img_new(size); + size_t size; + struct fp_img *img; GSList *elem = sdev->rows; size_t offset = 0; @@ -165,6 +175,31 @@ static void handoff_img(struct fp_img_dev *dev) return; } + /* FIXME: If image size is less than 8x8, nbis complains and bad things + * happen (segfault). Image size should be checke before being processed + * by nbis. Now, to avoid a crash, we just repeat the last line we captured + * comply with nbis requirements. + * 16 rows are more than enough so that's fine. */ + const int min_rows = 16; + if (sdev->num_rows < min_rows) { + int i; + unsigned char* srcbuf = g_slist_nth_data (elem, sdev->num_rows - 1); + + for (i = sdev->num_rows; i < min_rows; i ++) { + unsigned char* dstbuf = g_malloc (IMG_WIDTH); + if (!dstbuf) { + fp_err ("out of memory"); + return; + } + memcpy (dstbuf, srcbuf, IMG_WIDTH); + elem = g_slist_prepend(elem, dstbuf); + } + sdev->num_rows = min_rows; + } + + size = IMG_WIDTH * sdev->num_rows; + img = fpi_img_new(size); + fp_dbg("%d rows", sdev->num_rows); img->height = sdev->num_rows; @@ -254,8 +289,7 @@ static void start_new_row(struct sonly_dev *sdev, unsigned char *data, int size) { if (!sdev->rowbuf) sdev->rowbuf = g_malloc(IMG_WIDTH); - memcpy(sdev->rowbuf + IMG_WIDTH - 2, data, 2); - memcpy(sdev->rowbuf, data + 2, size - 2); + memcpy (sdev->rowbuf, data, size); sdev->rowbuf_offset = size; } @@ -277,11 +311,9 @@ static int rowbuf_remaining(struct sonly_dev *sdev) static void handle_packet(struct fp_img_dev *dev, unsigned char *data) { struct sonly_dev *sdev = dev->priv; - uint16_t seqnum = data[0] << 8 | data[1]; + int seqnum = data[0] << 8 | data[1]; int abs_base_addr; int for_rowbuf; - int next_row_addr; - int diff; data += 2; /* skip sequence number */ if (seqnum != sdev->last_seqnum + 1) { @@ -302,7 +334,10 @@ static void handle_packet(struct fp_img_dev *dev, unsigned char *data) for_rowbuf = rowbuf_remaining(sdev); if (for_rowbuf != -1) { add_to_rowbuf(dev, data, for_rowbuf); - /* FIXME: we drop a row here */ + /* If we still have data, copy it to a new row. */ + if (rowbuf_remaining(sdev) == -1 && for_rowbuf < 62) { + start_new_row(sdev, data + for_rowbuf, 62 - for_rowbuf); + } return; } @@ -311,13 +346,6 @@ static void handle_packet(struct fp_img_dev *dev, unsigned char *data) start_new_row(sdev, data, 62); return; } - - /* does the data in the packet reside on a row boundary? - * if so capture it */ - next_row_addr = ((abs_base_addr / IMG_WIDTH) + 1) * IMG_WIDTH; - diff = next_row_addr - abs_base_addr; - if (diff < 62) - start_new_row(sdev, data + diff, 62 - diff); } static void img_data_cb(struct libusb_transfer *transfer) @@ -591,22 +619,42 @@ static void sm_await_intr(struct fpi_ssm *ssm) /***** AWAIT FINGER *****/ -static const struct sonly_regwrite awfsm_writev_1[] = { +static const struct sonly_regwrite awfsm_2016_writev_1[] = { { 0x0a, 0x00 }, { 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x3b }, { 0x00, 0x67 }, { 0x00, 0x67 }, }; -static const struct sonly_regwrite awfsm_writev_2[] = { +static const struct sonly_regwrite awfsm_1000_writev_1[] = { + /* Initialize sensor settings */ + { 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x37 }, { 0x00, 0x5f }, + { 0x01, 0x6e }, { 0x01, 0xee }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, + { 0x0e, 0x0e }, { 0x0f, 0x0d }, + + { 0x13, 0x05 }, { 0x13, 0x45 }, + + /* Initlalize finger detection registers (not enabling yet) */ + { 0x30, 0xe0 }, { 0x15, 0x26 }, + + { 0x12, 0x01 }, { 0x20, 0x01 }, { 0x07, 0x10 }, + { 0x10, 0x00 }, { 0x11, 0xbf }, +}; + +static const struct sonly_regwrite awfsm_2016_writev_2[] = { { 0x01, 0xc6 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e }, { 0x0f, 0x0d }, { 0x0b, 0x00 }, }; -static const struct sonly_regwrite awfsm_writev_3[] = { +static const struct sonly_regwrite awfsm_1000_writev_2[] = { + /* Enable finger detection */ + { 0x30, 0xe1 }, { 0x15, 0x06 }, { 0x15, 0x86 }, +}; + +static const struct sonly_regwrite awfsm_2016_writev_3[] = { { 0x13, 0x45 }, { 0x30, 0xe0 }, { 0x12, 0x01 }, { 0x20, 0x01 }, { 0x09, 0x20 }, { 0x0a, 0x00 }, { 0x30, 0xe0 }, { 0x20, 0x01 }, }; -static const struct sonly_regwrite awfsm_writev_4[] = { +static const struct sonly_regwrite awfsm_2016_writev_4[] = { { 0x08, 0x00 }, { 0x10, 0x00 }, { 0x12, 0x01 }, { 0x11, 0xbf }, { 0x12, 0x01 }, { 0x07, 0x10 }, { 0x07, 0x10 }, { 0x04, 0x00 },\ { 0x05, 0x00 }, { 0x0b, 0x00 }, @@ -616,91 +664,150 @@ static const struct sonly_regwrite awfsm_writev_4[] = { { 0x15, 0x84 }, }; -enum awfsm_states { - AWFSM_WRITEV_1, - AWFSM_READ_01, - AWFSM_WRITE_01, - AWFSM_WRITEV_2, - AWFSM_READ_13, - AWFSM_WRITE_13, - AWFSM_WRITEV_3, - AWFSM_READ_07, - AWFSM_WRITE_07, - AWFSM_WRITEV_4, - AWFSM_NUM_STATES, +enum awfsm_2016_states { + AWFSM_2016_WRITEV_1, + AWFSM_2016_READ_01, + AWFSM_2016_WRITE_01, + AWFSM_2016_WRITEV_2, + AWFSM_2016_READ_13, + AWFSM_2016_WRITE_13, + AWFSM_2016_WRITEV_3, + AWFSM_2016_READ_07, + AWFSM_2016_WRITE_07, + AWFSM_2016_WRITEV_4, + AWFSM_2016_NUM_STATES, }; -static void awfsm_run_state(struct fpi_ssm *ssm) +enum awfsm_1000_states { + AWFSM_1000_WRITEV_1, + AWFSM_1000_WRITEV_2, + AWFSM_1000_NUM_STATES, +}; + +static void awfsm_2016_run_state(struct fpi_ssm *ssm) { struct fp_img_dev *dev = ssm->priv; struct sonly_dev *sdev = dev->priv; switch (ssm->cur_state) { - case AWFSM_WRITEV_1: - sm_write_regs(ssm, awfsm_writev_1, G_N_ELEMENTS(awfsm_writev_1)); + case AWFSM_2016_WRITEV_1: + sm_write_regs(ssm, awfsm_2016_writev_1, G_N_ELEMENTS(awfsm_2016_writev_1)); break; - case AWFSM_READ_01: + case AWFSM_2016_READ_01: sm_read_reg(ssm, 0x01); break; - case AWFSM_WRITE_01: + case AWFSM_2016_WRITE_01: if (sdev->read_reg_result != 0xc6) sm_write_reg(ssm, 0x01, 0x46); else sm_write_reg(ssm, 0x01, 0xc6); break; - case AWFSM_WRITEV_2: - sm_write_regs(ssm, awfsm_writev_2, G_N_ELEMENTS(awfsm_writev_2)); + case AWFSM_2016_WRITEV_2: + sm_write_regs(ssm, awfsm_2016_writev_2, G_N_ELEMENTS(awfsm_2016_writev_2)); break; - case AWFSM_READ_13: + case AWFSM_2016_READ_13: sm_read_reg(ssm, 0x13); break; - case AWFSM_WRITE_13: + case AWFSM_2016_WRITE_13: if (sdev->read_reg_result != 0x45) sm_write_reg(ssm, 0x13, 0x05); else sm_write_reg(ssm, 0x13, 0x45); break; - case AWFSM_WRITEV_3: - sm_write_regs(ssm, awfsm_writev_3, G_N_ELEMENTS(awfsm_writev_3)); + case AWFSM_2016_WRITEV_3: + sm_write_regs(ssm, awfsm_2016_writev_3, G_N_ELEMENTS(awfsm_2016_writev_3)); break; - case AWFSM_READ_07: + case AWFSM_2016_READ_07: sm_read_reg(ssm, 0x07); break; - case AWFSM_WRITE_07: + case AWFSM_2016_WRITE_07: if (sdev->read_reg_result != 0x10 && sdev->read_reg_result != 0x90) fp_warn("odd reg7 value %x", sdev->read_reg_result); sm_write_reg(ssm, 0x07, sdev->read_reg_result); break; - case AWFSM_WRITEV_4: - sm_write_regs(ssm, awfsm_writev_4, G_N_ELEMENTS(awfsm_writev_4)); + case AWFSM_2016_WRITEV_4: + sm_write_regs(ssm, awfsm_2016_writev_4, G_N_ELEMENTS(awfsm_2016_writev_4)); + break; + } +} + +static void awfsm_1000_run_state(struct fpi_ssm *ssm) +{ + switch (ssm->cur_state) { + case AWFSM_1000_WRITEV_1: + sm_write_regs(ssm, awfsm_1000_writev_1, G_N_ELEMENTS(awfsm_1000_writev_1)); + break; + case AWFSM_1000_WRITEV_2: + sm_write_regs(ssm, awfsm_1000_writev_2, G_N_ELEMENTS(awfsm_1000_writev_2)); break; } } /***** CAPTURE MODE *****/ -static const struct sonly_regwrite capsm_writev[] = { +static const struct sonly_regwrite capsm_2016_writev[] = { /* enter capture mode */ { 0x09, 0x28 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, { 0x04, 0x00 }, { 0x05, 0x00 }, }; -enum capsm_states { - CAPSM_INIT, - CAPSM_WRITE_15, - CAPSM_WRITE_30, - CAPSM_FIRE_BULK, - CAPSM_WRITEV, - CAPSM_NUM_STATES, +static const struct sonly_regwrite capsm_1000_writev[] = { + { 0x08, 0x80 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, /* Enter capture mode */ +}; + +enum capsm_2016_states { + CAPSM_2016_INIT, + CAPSM_2016_WRITE_15, + CAPSM_2016_WRITE_30, + CAPSM_2016_FIRE_BULK, + CAPSM_2016_WRITEV, + CAPSM_2016_NUM_STATES, }; -static void capsm_run_state(struct fpi_ssm *ssm) +enum capsm_1000_states { + CAPSM_1000_INIT, + CAPSM_1000_FIRE_BULK, + CAPSM_1000_WRITEV, + CAPSM_1000_NUM_STATES, +}; + +static void capsm_fire_bulk(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = ssm->priv; + struct sonly_dev *sdev = dev->priv; + int i; + for (i = 0; i < NUM_BULK_TRANSFERS; i++) { + int r = libusb_submit_transfer(sdev->img_transfer[i]); + if (r < 0) { + if (i == 0) { + /* first one failed: easy peasy */ + fpi_ssm_mark_aborted(ssm, r); + return; + } + + /* cancel all flying transfers, and request that the SSM + * gets aborted when the last transfer has dropped out of + * the sky */ + sdev->killing_transfers = ABORT_SSM; + sdev->kill_ssm = ssm; + sdev->kill_status_code = r; + cancel_img_transfers(dev); + return; + } + sdev->img_transfer_data[i].flying = TRUE; + sdev->num_flying++; + } + sdev->capturing = TRUE; + fpi_ssm_next_state(ssm); +} + +static void capsm_2016_run_state(struct fpi_ssm *ssm) { struct fp_img_dev *dev = ssm->priv; struct sonly_dev *sdev = dev->priv; switch (ssm->cur_state) { - case CAPSM_INIT: + case CAPSM_2016_INIT: sdev->rowbuf_offset = -1; sdev->num_rows = 0; sdev->wraparounds = -1; @@ -710,61 +817,84 @@ static void capsm_run_state(struct fpi_ssm *ssm) sdev->killing_transfers = 0; fpi_ssm_next_state(ssm); break; - case CAPSM_WRITE_15: + case CAPSM_2016_WRITE_15: sm_write_reg(ssm, 0x15, 0x20); break; - case CAPSM_WRITE_30: + case CAPSM_2016_WRITE_30: sm_write_reg(ssm, 0x30, 0xe0); break; - case CAPSM_FIRE_BULK: ; - int i; - for (i = 0; i < NUM_BULK_TRANSFERS; i++) { - int r = libusb_submit_transfer(sdev->img_transfer[i]); - if (r < 0) { - if (i == 0) { - /* first one failed: easy peasy */ - fpi_ssm_mark_aborted(ssm, r); - return; - } - - /* cancel all flying transfers, and request that the SSM - * gets aborted when the last transfer has dropped out of - * the sky */ - sdev->killing_transfers = ABORT_SSM; - sdev->kill_ssm = ssm; - sdev->kill_status_code = r; - cancel_img_transfers(dev); - return; - } - sdev->img_transfer_data[i].flying = TRUE; - sdev->num_flying++; - } - sdev->capturing = TRUE; + case CAPSM_2016_FIRE_BULK: ; + capsm_fire_bulk (ssm); + break; + case CAPSM_2016_WRITEV: + sm_write_regs(ssm, capsm_2016_writev, G_N_ELEMENTS(capsm_2016_writev)); + break; + } +} + +static void capsm_1000_run_state(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = ssm->priv; + struct sonly_dev *sdev = dev->priv; + + switch (ssm->cur_state) { + case CAPSM_1000_INIT: + sdev->rowbuf_offset = -1; + sdev->num_rows = 0; + sdev->wraparounds = -1; + sdev->num_blank = 0; + sdev->finger_removed = 0; + sdev->last_seqnum = 16383; + sdev->killing_transfers = 0; fpi_ssm_next_state(ssm); break; - case CAPSM_WRITEV: - sm_write_regs(ssm, capsm_writev, G_N_ELEMENTS(capsm_writev)); + case CAPSM_1000_FIRE_BULK: ; + capsm_fire_bulk (ssm); + break; + case CAPSM_1000_WRITEV: + sm_write_regs(ssm, capsm_1000_writev, G_N_ELEMENTS(capsm_1000_writev)); break; } } /***** DEINITIALIZATION *****/ -static const struct sonly_regwrite deinitsm_writev[] = { +static const struct sonly_regwrite deinitsm_2016_writev[] = { /* reset + enter low power mode */ { 0x0b, 0x00 }, { 0x09, 0x20 }, { 0x13, 0x45 }, { 0x13, 0x45 }, }; -enum deinitsm_states { - DEINITSM_WRITEV, - DEINITSM_NUM_STATES, +static const struct sonly_regwrite deinitsm_1000_writev[] = { + { 0x15, 0x26 }, { 0x30, 0xe0 }, /* Disable finger detection */ + + { 0x0b, 0x00 }, { 0x13, 0x45 }, { 0x08, 0x00 }, /* Disable capture mode */ +}; + + +enum deinitsm_2016_states { + DEINITSM_2016_WRITEV, + DEINITSM_2016_NUM_STATES, +}; + +enum deinitsm_1000_states { + DEINITSM_1000_WRITEV, + DEINITSM_1000_NUM_STATES, }; -static void deinitsm_run_state(struct fpi_ssm *ssm) +static void deinitsm_2016_run_state(struct fpi_ssm *ssm) { switch (ssm->cur_state) { - case DEINITSM_WRITEV: - sm_write_regs(ssm, deinitsm_writev, G_N_ELEMENTS(deinitsm_writev)); + case DEINITSM_2016_WRITEV: + sm_write_regs(ssm, deinitsm_2016_writev, G_N_ELEMENTS(deinitsm_2016_writev)); + break; + } +} + +static void deinitsm_1000_run_state(struct fpi_ssm *ssm) +{ + switch (ssm->cur_state) { + case DEINITSM_1000_WRITEV: + sm_write_regs(ssm, deinitsm_1000_writev, G_N_ELEMENTS(deinitsm_1000_writev)); break; } } @@ -772,27 +902,41 @@ static void deinitsm_run_state(struct fpi_ssm *ssm) /***** INITIALIZATION *****/ static const struct sonly_regwrite initsm_writev_1[] = { - { 0x49, 0x00 }, - - /* BSAPI writes different values to register 0x3e each time. I initially - * thought this was some kind of clever authentication, but just blasting - * these sniffed values each time seems to work. */ - { 0x3e, 0x83 }, { 0x3e, 0x4f }, { 0x3e, 0x0f }, { 0x3e, 0xbf }, - { 0x3e, 0x45 }, { 0x3e, 0x35 }, { 0x3e, 0x1c }, { 0x3e, 0xae }, + { 0x49, 0x00 }, /* Encryption disabled */ + /* Setting encryption key. Don't need to be random since we don't use any + * encryption. */ + { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, + { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, +}; + +static const struct sonly_regwrite initsm_2016_writev_2[] = { { 0x44, 0x01 }, { 0x43, 0x06 }, { 0x43, 0x05 }, { 0x43, 0x04 }, { 0x44, 0x00 }, { 0x0b, 0x00 }, }; -enum initsm_states { - INITSM_WRITEV_1, - INITSM_READ_09, - INITSM_WRITE_09, - INITSM_READ_13, - INITSM_WRITE_13, - INITSM_WRITE_04, - INITSM_WRITE_05, - INITSM_NUM_STATES, +static const struct sonly_regwrite initsm_1000_writev_2[] = { + { 0x04, 0x00 }, { 0x05, 0x00 }, + + { 0x0b, 0x00 }, { 0x08, 0x00 }, /* Initialize capture control registers */ +}; + +enum initsm_2016_states { + INITSM_2016_WRITEV_1, + INITSM_2016_WRITEV_2, + INITSM_2016_READ_09, + INITSM_2016_WRITE_09, + INITSM_2016_READ_13, + INITSM_2016_WRITE_13, + INITSM_2016_WRITE_04, + INITSM_2016_WRITE_05, + INITSM_2016_NUM_STATES, +}; + +enum initsm_1000_states { + INITSM_1000_WRITEV_1, + INITSM_1000_WRITEV_2, + INITSM_1000_NUM_STATES, }; static void initsm_run_state(struct fpi_ssm *ssm) @@ -800,27 +944,45 @@ static void initsm_run_state(struct fpi_ssm *ssm) struct fp_img_dev *dev = ssm->priv; struct sonly_dev *sdev = dev->priv; - switch (ssm->cur_state) { - case INITSM_WRITEV_1: - sm_write_regs(ssm, initsm_writev_1, G_N_ELEMENTS(initsm_writev_1)); - break; - case INITSM_READ_09: - sm_read_reg(ssm, 0x09); - break; - case INITSM_WRITE_09: - sm_write_reg(ssm, 0x09, sdev->read_reg_result & ~0x08); - break; - case INITSM_READ_13: - sm_read_reg(ssm, 0x13); - break; - case INITSM_WRITE_13: - sm_write_reg(ssm, 0x13, sdev->read_reg_result & ~0x10); - break; - case INITSM_WRITE_04: - sm_write_reg(ssm, 0x04, 0x00); + switch (sdev->dev_model) { + case UPEKSONLY_2016: + switch (ssm->cur_state) { + case INITSM_2016_WRITEV_1: + sm_write_regs(ssm, initsm_writev_1, G_N_ELEMENTS(initsm_writev_1)); + break; + case INITSM_2016_WRITEV_2: + sm_write_regs(ssm, initsm_2016_writev_2, G_N_ELEMENTS(initsm_2016_writev_2)); + break; + case INITSM_2016_READ_09: + sm_read_reg(ssm, 0x09); + break; + case INITSM_2016_WRITE_09: + sm_write_reg(ssm, 0x09, sdev->read_reg_result & ~0x08); + break; + case INITSM_2016_READ_13: + sm_read_reg(ssm, 0x13); + break; + case INITSM_2016_WRITE_13: + sm_write_reg(ssm, 0x13, sdev->read_reg_result & ~0x10); + break; + case INITSM_2016_WRITE_04: + sm_write_reg(ssm, 0x04, 0x00); + break; + case INITSM_2016_WRITE_05: + sm_write_reg(ssm, 0x05, 0x00); + break; + } break; - case INITSM_WRITE_05: - sm_write_reg(ssm, 0x05, 0x00); + + case UPEKSONLY_1000: + switch (ssm->cur_state) { + case INITSM_1000_WRITEV_1: + sm_write_regs(ssm, initsm_writev_1, G_N_ELEMENTS(initsm_writev_1)); + break; + case INITSM_1000_WRITEV_2: + sm_write_regs(ssm, initsm_1000_writev_2, G_N_ELEMENTS(initsm_1000_writev_2)); + break; + } break; } } @@ -847,8 +1009,17 @@ static void loopsm_run_state(struct fpi_ssm *ssm) if (sdev->deactivating) { fpi_ssm_mark_completed(ssm); } else { - struct fpi_ssm *awfsm = fpi_ssm_new(dev->dev, awfsm_run_state, - AWFSM_NUM_STATES); + struct fpi_ssm *awfsm = NULL; + switch (sdev->dev_model) { + case UPEKSONLY_2016: + awfsm = fpi_ssm_new(dev->dev, awfsm_2016_run_state, + AWFSM_2016_NUM_STATES); + break; + case UPEKSONLY_1000: + awfsm = fpi_ssm_new(dev->dev, awfsm_1000_run_state, + AWFSM_1000_NUM_STATES); + break; + } awfsm->priv = dev; fpi_ssm_start_subsm(ssm, awfsm); } @@ -857,8 +1028,17 @@ static void loopsm_run_state(struct fpi_ssm *ssm) sm_await_intr(ssm); break; case LOOPSM_RUN_CAPSM: ; - struct fpi_ssm *capsm = fpi_ssm_new(dev->dev, capsm_run_state, - CAPSM_NUM_STATES); + struct fpi_ssm *capsm = NULL; + switch (sdev->dev_model) { + case UPEKSONLY_2016: + capsm = fpi_ssm_new(dev->dev, capsm_2016_run_state, + CAPSM_2016_NUM_STATES); + break; + case UPEKSONLY_1000: + capsm = fpi_ssm_new(dev->dev, capsm_1000_run_state, + CAPSM_1000_NUM_STATES); + break; + } capsm->priv = dev; fpi_ssm_start_subsm(ssm, capsm); break; @@ -867,8 +1047,17 @@ static void loopsm_run_state(struct fpi_ssm *ssm) * to push us into next state */ break; case LOOPSM_RUN_DEINITSM: ; - struct fpi_ssm *deinitsm = fpi_ssm_new(dev->dev, deinitsm_run_state, - DEINITSM_NUM_STATES); + struct fpi_ssm *deinitsm = NULL; + switch (sdev->dev_model) { + case UPEKSONLY_2016: + deinitsm = fpi_ssm_new(dev->dev, deinitsm_2016_run_state, + DEINITSM_2016_NUM_STATES); + break; + case UPEKSONLY_1000: + deinitsm = fpi_ssm_new(dev->dev, deinitsm_1000_run_state, + DEINITSM_1000_NUM_STATES); + break; + } sdev->capturing = FALSE; deinitsm->priv = dev; fpi_ssm_start_subsm(ssm, deinitsm); @@ -977,7 +1166,14 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state) 4096, img_data_cb, &sdev->img_transfer_data[i], 0); } - ssm = fpi_ssm_new(dev->dev, initsm_run_state, INITSM_NUM_STATES); + switch (sdev->dev_model) { + case UPEKSONLY_2016: + ssm = fpi_ssm_new(dev->dev, initsm_run_state, INITSM_2016_NUM_STATES); + break; + case UPEKSONLY_1000: + ssm = fpi_ssm_new(dev->dev, initsm_run_state, INITSM_1000_NUM_STATES); + break; + } ssm->priv = dev; fpi_ssm_start(ssm, initsm_complete); return 0; @@ -1000,6 +1196,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) } dev->priv = g_malloc0(sizeof(struct sonly_dev)); + ((struct sonly_dev*)dev->priv)->dev_model = (int)driver_data; fpi_imgdev_open_complete(dev, 0); return 0; } @@ -1012,7 +1209,8 @@ static void dev_deinit(struct fp_img_dev *dev) } static const struct usb_id id_table[] = { - { .vendor = 0x147e, .product = 0x2016 }, + { .vendor = 0x147e, .product = 0x2016, .driver_data = UPEKSONLY_2016 }, + { .vendor = 0x147e, .product = 0x1000, .driver_data = UPEKSONLY_1000 }, { 0, 0, 0, }, };
This file contains technical information about UPEK Touchstrip Sensor model with USB ID 147e:1000 (TCS4C according to BASPI). Information might also apply (maybe partially) to other devices from the UPEK Touchstrip Sensor only family. I gathered this information by sniffing USB traffic between the device and programs using the proprietary BSAPI SDK for Linux version 3.6 build 133 available from UPEK website. Contact: Hugo Grostabussiat <[email protected]> *** List of registers *** Hardware default values are the one you get after a hard-reset. When I put a question mark in the description field, it means I'm not sure. HW def. Address value Description ------------------------------------------------------------------------------- 0x00 0x33 Sensor sensivety. Value between 0x0 and 0x7f (BSAPI value: 0x5f) 0x01 0x33 ? (Has an effect on resulting image data) bit 7 If not set, image is totally black. 0x02 0x05 Sensor calibration ? 0x03 0x33 Sensor calibration ? 0x04 0x00 ? 0x05 0x00 ? 0x06 0x23 ? (Not used by BSAPI) 0x07 0x90 Image correction (sharpness) control ? (BSAPI value: 0x10) bit 3 Warning: Setting this bit prevents finger detection from working 0x08 0x00 Capture control register ? bit 7 Bit set in capture mode 0x09 0x27 ? 0x0a 0x00 ? 0x0b 0x00 Another capture control register ? bit 7 Bit set in capture mode 0x0c 0x0f Sample rate control register ? (BASPI value: 0x13) bit 7 If set, sensor responsiveness is drastically lower 0x0d 0x07 Sensor calibration ? (BSAPI value: 0xd) 0x0e 0x08 Calibration ? (BSAPI value: 0xe) 0x0f 0x02 Calibration ? (BSAPI value: 0xd) 0x10 0x00 Number of bytes to pad each line of data with. (I'm not sure about it. Further investigations needed) (BSAPI value: 0x0) 0x11 0xbf Row width. (BSAPI value: 0xbf -> 288 pixels) 0x12 0x01 Sensor resolution/image width control register ? 0x13 0x00 Another capture control register ? bit 4 Bit set in capture mode 0x14 0x06 ? 0x15 0x20 Finger detection control register bit 7 Engage/disengage finger detection. Make sure to reset this flag before setting it anew. bit 5 ? 0x16 0x13 ? 0x17 0x33 ? (Seems to always be 0x33) 0x18 0x00 ? (Not used by BSAPI) 0x19 0x00 ? (Not used by BSAPI) 0x1a 0x00 ? (Not used by BSAPI) 0x1b 0x00 ? (Not used by BSAPI) 0x1c 0x00 ? (Not used by BSAPI) 0x1d 0x00 ? (Not used by BSAPI) 0x1e 0x00 ? (Not used by BSAPI) 0x1f 0x00 ? (Not used by BSAPI) 0x20 0x00 Maybe some sort of synchronization ? bit 0 Enable synchronization ? If this bit is not set, results are just horrible. 0x21 0x00 ? (Not used by BSAPI) 0x22 0x00 ? (Not used by BSAPI) 0x23 0x00 ? (Not used by BSAPI) 0x24 0x00 ? (Not used by BSAPI) 0x25 0x00 ? (Not used by BSAPI) 0x26 0x00 ? (Not used by BSAPI) 0x27 0x02 ? (Not used by BSAPI) 0x28 0x00 ? (Not used by BSAPI) 0x29 0x00 ? (Not used by BSAPI) 0x2a 0x00 ? (Not used by BSAPI) 0x2b 0x00 ? (Not used by BSAPI) 0x2c 0x00 ? (Not used by BSAPI) 0x2d 0x00 ? (Not used by BSAPI) 0x2e 0x00 ? (Not used by BSAPI) 0x2f 0x00 ? (Not used by BSAPI) 0x30 0x00 ? bit 0 Bit set in finger detection mode 0x31 0x00 ? (Not used by BSAPI) 0x32 0x00 ? (Not used by BSAPI) 0x33 0x00 ? (Not used by BSAPI) 0x34 0x00 ? (Not used by BSAPI) 0x35 0x00 ? (Not used by BSAPI) 0x36 0x00 ? (Not used by BSAPI) 0x37 0x00 ? (Not used by BSAPI) 0x38 0x00 ? (Not used by BSAPI) 0x39 0x00 ? (Not used by BSAPI) 0x3a 0x00 ? (Not used by BSAPI) 0x3b 0x00 ? (Not used by BSAPI) 0x3c 0x00 ? (Not used by BSAPI) 0x3d 0x00 ? (Not used by BSAPI) 0x3e 0x00 Encryption key should be written into this register. The key is 8 bytes long and should be sent when encryption and signing are disabled. 0x3f 0x00 ? (Not used by BSAPI) 0x40 0x00 Another parameter ? Adds a strange behaviour to 0x3 command, but only under special conditions. 0x41 0x00 Command parameter 1 0x42 0x00 Command parameter 2 0x43 0x00 Command code (0x3 = Read from ROM) 0x44 0x00 Command control register bit 7 ? bit 0 Enable/disable command mode. When enabled, writing a command code into 0x43 will run that command. 0x45 0x00 Command completion status register. bit 6 BUSY flag ? bit 3 ERROR flag ? bit 1 ? 0x46 0x00 Power management ? bit 0 Hard-reset device 0x47 0x00 Device-generated key control register (you must set the key in register 0x3e prior using this one) bit 3 Put the first byte of the key in 0x58 bit 2 Put the next byte of the key in 0x58 bit 1 Flush one byte of the key and insert one from host key ? (Used by BSAPI only on some error condition, e.g. communication with device interrupted while setting the encryption key) 0x48 0x00 ? (Not used by BSAPI) 0x49 0x00 SSM (Sensor Security Mode) control register bit 2 Enable image data encryption (SSM_ENCRYPT) bit 1 Enabled for SSM_SIGN_ALL, SSM_SIGN_PARTIAL_V1 and SSM_SIGN_PARTIAL_V2 (data signing) bit 0 Reset device key ? (Used by BSAPI on the same kind of error condition as above) 0x4a 0x00 ? (Not used by BSAPI) 0x4b 0x00 ? (Not used by BSAPI) 0x4c 0x00 ? (Not used by BSAPI) 0x4d 0x00 ? (Not used by BSAPI) 0x4e 0x00 ? (Not used by BSAPI) 0x4f 0x00 ? (Not used by BSAPI) 0x50 0x00 ? (Not used by BSAPI) 0x51 0x00 ? (Not used by BSAPI) 0x52 0x00 ? (Not used by BSAPI) 0x53 0x00 ? (Not used by BSAPI) 0x54 0x00 ? (Not used by BSAPI) 0x55 0x00 ? (Not used by BSAPI) 0x56 0x00 ? (Not used by BSAPI) 0x57 0x00 ? (Not used by BSAPI) 0x58 0xff Read from this register to get one byte of the device-generated key. ------------------------------------------------------------------------------- *** List of commands *** 0x3 : Read data from device (ROM, firmware ?). Parameters: 0x41 = Offset low byte 0x42 = Offset high byte Effect: Send ROM data from the specified offset. You can fetch data with a bulk transfer. Wait for bit 6 of register 0x45 to be cleared before firing the transfer. Notes : Wait for this command to complete (bit 6 of register 0x45 reset) before doing anything else. This command has a strange behavior when register 0x40 has a value lower than 0x1f. To get the expected behavior, set register 0x40 value to 0xff. You cannot get more than 512 bytes of data at a time. If register 0x40 value is lower than 0x1f then you may not get more than 64 bytes at once (well, this might not be always true, further investigation is needed). 0x4 : Unknown, used during initialization Parameters : None Effect : Unknown 0x5 : Unknown, used during initialization Parameters : None Effect : Unknown Notes : Wait for this command to complete (bit 6 of register 0x45 reset) before doing anything else. 0x6 : Unknown, used during initialization Parameters : None Effect : Unknown *** Sensor Security Mode (SSM) *** UPEK Touchstrip Sensor provides several security features in hardware: * The ability to encrypt image data transferred on the USB bus. * The ability to sign image data transferred on the USB bus. Encryption: Encrypting image data is a solution to avoid spoofing (if a malicious person uses a custom device which imitates the fingerprint sensor and sends an image of your fingerprint collected from an object you touched). This is how it works: the fingerprint sensor driver sends a 64 bit encryption key to the device. The device will then generate another 64 bit key and encrypt each row of data obtained from the sensor. Once the driver reads the data, it uses the key it got from the device an decrypt image data. This security model is only viable provided that nobody knows the encryption algorithm used by the device. One could probably find out how it works by disassembling the Windows driver or the Linux BSAPI library, or maybe doing cryptanalysis. Signature: BSAPI is supposed to sign image data sent over the USB bus, but the only change I noticed is that BSAPI writes 0x2 instead of 0x4 in register 0x49. I did not see any trace of a signature. Maybe it's unimplemented in the BSAPI version I was using, or that signature data is hidden inside the image data itself using a steganography technique. But I doubt about the latter because I modified libusb to corrupt image data and BASPI didn't even complain. Again, disassembling the BSAPI would make things clearer about that so called protection. Device-generated key: Right after we write encryption key into register 0x3e, the device generates another 64 bit key. This device key is used by BSAPI to decrypt image data. To decrypt image data, you must know both device and host key. The validity of device key can be verified with the host key (well, I hooked some functions to write bogus values into 0x3e, after what BSAPI went crazy trying to get device key and reset and resubmitted encryption key into 0x3e). I don't know whether it's used for data signing. To get that new key, set bit 3 of register 0x47, read the first byte of the key from register 0x58. To get the next byte, set bit 2 of 0x47 and then read it from 0x58. Repeat this operation until you get the 8 bytes of the key.
_______________________________________________ fprint mailing list [email protected] http://lists.reactivated.net/mailman/listinfo/fprint
