Added support for UPEK TCS4C (USB ID 147e:1000)

https://bugs.freedesktop.org/show_bug.cgi?id=29719
This commit is contained in:
Hugo Grostabussiat 2010-08-31 16:09:22 +02:00 committed by Bastien Nocera
parent 15b8e8f376
commit a18f318446
2 changed files with 219 additions and 35 deletions

View file

@ -7,3 +7,4 @@ Copyright (C) 2007 Cyrille Bagard
Copyright (C) 2007 Vasily Khoruzhick
Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
Copyright (C) 2007 Anthony Bretaudeau <wxcover@users.sourceforge.net>
Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>

View file

@ -2,6 +2,9 @@
* UPEK TouchStrip Sensor-Only driver for libfprint
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* TCS4C (USB ID 147e:1000) support:
* Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
*
* 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, },
};