From a18f318446df01d27f3a8dc677c2a644fb086a60 Mon Sep 17 00:00:00 2001 From: Hugo Grostabussiat Date: Tue, 31 Aug 2010 16:09:22 +0200 Subject: [PATCH] Added support for UPEK TCS4C (USB ID 147e:1000) https://bugs.freedesktop.org/show_bug.cgi?id=29719 --- AUTHORS | 1 + libfprint/drivers/upeksonly.c | 253 +++++++++++++++++++++++++++++----- 2 files changed, 219 insertions(+), 35 deletions(-) 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 Copyright (C) 2007 Anthony Bretaudeau +Copyright (C) 2010 Hugo Grostabussiat diff --git a/libfprint/drivers/upeksonly.c b/libfprint/drivers/upeksonly.c index 9b9adbb..e2f5a45 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 * + * TCS4C (USB ID 147e:1000) support: + * Copyright (C) 2010 Hugo Grostabussiat + * * 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 @@ -33,6 +36,11 @@ #define MAX_ROWS 700 #define MIN_ROWS 64 +enum { + UPEKSONLY_2016, + UPEKSONLY_1000, +}; + struct img_transfer_data { int idx; struct fp_img_dev *dev; @@ -61,6 +69,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; @@ -658,11 +668,31 @@ static const struct sonly_regwrite awfsm_writev_1[] = { { 0x00, 0x67 }, { 0x00, 0x67 }, }; +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 }, + + /* Initialize 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_writev_2[] = { { 0x01, 0xc6 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e }, { 0x0f, 0x0d }, { 0x0b, 0x00 }, }; +static const struct sonly_regwrite awfsm_1000_writev_2[] = { + /* Enable finger detection */ + { 0x30, 0xe1 }, { 0x15, 0x06 }, { 0x15, 0x86 }, +}; + static const struct sonly_regwrite awfsm_writev_3[] = { { 0x13, 0x45 }, { 0x30, 0xe0 }, { 0x12, 0x01 }, { 0x20, 0x01 }, { 0x09, 0x20 }, { 0x0a, 0x00 }, { 0x30, 0xe0 }, { 0x20, 0x01 }, @@ -692,6 +722,12 @@ enum awfsm_states { AWFSM_NUM_STATES, }; +enum awfsm_1000_states { + AWFSM_1000_WRITEV_1, + AWFSM_1000_WRITEV_2, + AWFSM_1000_NUM_STATES, +}; + static void awfsm_run_state(struct fpi_ssm *ssm) { struct fp_img_dev *dev = ssm->priv; @@ -739,6 +775,18 @@ static void awfsm_run_state(struct fpi_ssm *ssm) } } +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[] = { @@ -747,6 +795,10 @@ static const struct sonly_regwrite capsm_writev[] = { { 0x05, 0x00 }, }; +static const struct sonly_regwrite capsm_1000_writev[] = { + { 0x08, 0x80 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, /* Enter capture mode */ +}; + enum capsm_states { CAPSM_INIT, CAPSM_WRITE_15, @@ -756,6 +808,43 @@ enum capsm_states { CAPSM_NUM_STATES, }; +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_run_state(struct fpi_ssm *ssm) { struct fp_img_dev *dev = ssm->priv; @@ -779,30 +868,7 @@ static void capsm_run_state(struct fpi_ssm *ssm) 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; - fpi_ssm_next_state(ssm); + capsm_fire_bulk (ssm); break; case CAPSM_WRITEV: sm_write_regs(ssm, capsm_writev, G_N_ELEMENTS(capsm_writev)); @@ -810,6 +876,31 @@ static void capsm_run_state(struct fpi_ssm *ssm) } } +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_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[] = { @@ -817,11 +908,23 @@ static const struct sonly_regwrite deinitsm_writev[] = { { 0x0b, 0x00 }, { 0x09, 0x20 }, { 0x13, 0x45 }, { 0x13, 0x45 }, }; +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_states { DEINITSM_WRITEV, DEINITSM_NUM_STATES, }; +enum deinitsm_1000_states { + DEINITSM_1000_WRITEV, + DEINITSM_1000_NUM_STATES, +}; + static void deinitsm_run_state(struct fpi_ssm *ssm) { switch (ssm->cur_state) { @@ -831,6 +934,15 @@ static void deinitsm_run_state(struct fpi_ssm *ssm) } } +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; + } +} + /***** INITIALIZATION *****/ static const struct sonly_regwrite initsm_writev_1[] = { @@ -846,6 +958,19 @@ static const struct sonly_regwrite initsm_writev_1[] = { { 0x44, 0x00 }, { 0x0b, 0x00 }, }; +static const struct sonly_regwrite initsm_1000_writev_1[] = { + { 0x49, 0x00 }, /* Encryption disabled */ + + /* Setting encryption key. Doesn'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 }, + + { 0x04, 0x00 }, { 0x05, 0x00 }, + + { 0x0b, 0x00 }, { 0x08, 0x00 }, /* Initialize capture control registers */ +}; + enum initsm_states { INITSM_WRITEV_1, INITSM_READ_09, @@ -857,6 +982,11 @@ enum initsm_states { INITSM_NUM_STATES, }; +enum initsm_1000_states { + INITSM_1000_WRITEV_1, + INITSM_1000_NUM_STATES, +}; + static void initsm_run_state(struct fpi_ssm *ssm) { struct fp_img_dev *dev = ssm->priv; @@ -887,6 +1017,18 @@ static void initsm_run_state(struct fpi_ssm *ssm) } } +static void initsm_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 INITSM_1000_WRITEV_1: + sm_write_regs(ssm, initsm_1000_writev_1, G_N_ELEMENTS(initsm_1000_writev_1)); + break; + } +} + /***** CAPTURE LOOP *****/ enum loopsm_states { @@ -909,8 +1051,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_run_state, + AWFSM_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); } @@ -919,8 +1070,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_run_state, + CAPSM_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; @@ -929,8 +1089,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_run_state, + DEINITSM_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); @@ -1039,7 +1208,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_NUM_STATES); + break; + case UPEKSONLY_1000: + ssm = fpi_ssm_new(dev->dev, initsm_1000_run_state, INITSM_1000_NUM_STATES); + break; + } ssm->priv = dev; fpi_ssm_start(ssm, initsm_complete); return 0; @@ -1062,6 +1238,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; } @@ -1075,15 +1252,21 @@ static void dev_deinit(struct fp_img_dev *dev) static int dev_discover(struct libusb_device_descriptor *dsc, uint32_t *devtype) { - /* Revision 1 is what we're interested in */ - if (dsc->bcdDevice == 1) - return 1; + if (dsc->idProduct == 0x2016) { + if (dsc->bcdDevice == 1) /* Revision 1 is what we're interested in */ + return 1; + } + if (dsc->idProduct == 0x1000) { + if (dsc->bcdDevice == 0x0033) /* Looking for revision 0.33 */ + return 1; + } return 0; } 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, }, };