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

Reply via email to