libfprint/libfprint/drivers/upeksonly.c
2019-11-20 13:53:45 +01:00

1410 lines
35 KiB
C

/*
* 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>
*
* TCRD5B (USB ID 147e:1001) support:
* Copyright (C) 2014 Vasily Khoruzhick <anarsoul@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
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "upeksonly"
#include "drivers_api.h"
#include "upeksonly.h"
#define CTRL_TIMEOUT 1000
#define NUM_BULK_TRANSFERS 24
#define MAX_ROWS 2048
#define MIN_ROWS 64
#define BLANK_THRESHOLD 250
#define FINGER_PRESENT_THRESHOLD 32
#define FINGER_REMOVED_THRESHOLD 100
#define DIFF_THRESHOLD 13
enum {
UPEKSONLY_2016,
UPEKSONLY_1000,
UPEKSONLY_1001,
};
enum sonly_kill_transfers_action {
NOT_KILLING = 0,
/* abort a SSM with an error code */
ABORT_SSM,
/* report an image session error */
IMG_SESSION_ERROR,
/* iterate a SSM to the next state */
ITERATE_SSM,
/* call a callback */
EXEC_CALLBACK,
};
enum sonly_fs {
AWAIT_FINGER,
FINGER_DETECTED,
FINGER_REMOVED,
};
struct _FpiDeviceUpeksonly {
FpImageDevice parent;
gboolean capturing;
gboolean deactivating;
guint8 read_reg_result;
int dev_model;
int img_width;
FpiSsm *loopsm;
/* Do we really need multiple concurrent transfers? */
GCancellable *img_cancellable;
GPtrArray *img_transfers;
int num_flying;
GSList *rows;
size_t num_rows;
unsigned char *rowbuf;
int rowbuf_offset;
int wraparounds;
int num_blank;
int num_nonblank;
enum sonly_fs finger_state;
int last_seqnum;
enum sonly_kill_transfers_action killing_transfers;
GError *kill_error;
union {
FpiSsm *kill_ssm;
void (*kill_cb)(FpImageDevice *dev);
};
struct fpi_line_asmbl_ctx assembling_ctx;
};
G_DECLARE_FINAL_TYPE(FpiDeviceUpeksonly, fpi_device_upeksonly, FPI,
DEVICE_UPEKSONLY, FpImageDevice);
G_DEFINE_TYPE(FpiDeviceUpeksonly, fpi_device_upeksonly, FP_TYPE_IMAGE_DEVICE);
/* Calculate squared standard deviation of sum of two lines */
static int upeksonly_get_deviation2(struct fpi_line_asmbl_ctx *ctx,
GSList *line1, GSList *line2)
{
unsigned char *buf1 = line1->data, *buf2 = line2->data;
int res = 0, mean = 0, i;
g_assert (ctx->line_width > 0);
for (i = 0; i < ctx->line_width; i+= 2)
mean += (int)buf1[i + 1] + (int)buf2[i];
mean /= (ctx->line_width / 2);
for (i = 0; i < ctx->line_width; i+= 2) {
int dev = (int)buf1[i + 1] + (int)buf2[i] - mean;
res += dev*dev;
}
return res / (ctx->line_width / 2);
}
static unsigned char upeksonly_get_pixel(struct fpi_line_asmbl_ctx *ctx,
GSList *row,
unsigned x)
{
unsigned char *buf;
unsigned offset;
/* The scans from this device are rolled right by two columns */
if (x < ctx->line_width - 2)
offset = x + 2;
else if ((x > ctx->line_width - 2) && (x < ctx->line_width))
offset = x - (ctx->line_width - 2);
else
return 0;
/* Each 2nd pixel is shifted 2 pixels down */
if ((!(x & 1)) && g_slist_next(row) && g_slist_next(g_slist_next(row)))
buf = g_slist_next(g_slist_next(row))->data;
else
buf = row->data;
return buf[offset];
}
/***** IMAGE PROCESSING *****/
static void free_img_transfers(FpiDeviceUpeksonly *sdev)
{
g_cancellable_cancel (sdev->img_cancellable);
g_clear_object (&sdev->img_cancellable);
g_clear_pointer (&sdev->img_transfers, g_ptr_array_unref);
}
static void last_transfer_killed(FpImageDevice *dev)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
switch (self->killing_transfers) {
case ABORT_SSM:
fp_dbg("abort ssm error %s", self->kill_error->message);
fpi_ssm_mark_failed(self->kill_ssm, g_steal_pointer (&self->kill_error));
return;
case ITERATE_SSM:
fp_dbg("iterate ssm");
fpi_ssm_next_state(self->kill_ssm);
return;
case IMG_SESSION_ERROR:
fp_dbg("session error %s", self->kill_error->message);
fpi_image_device_session_error(dev, g_steal_pointer (&self->kill_error));
return;
default:
return;
}
}
static void cancel_img_transfers(FpImageDevice *dev)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
g_cancellable_cancel (self->img_cancellable);
if (self->num_flying == 0)
last_transfer_killed(dev);
}
static gboolean is_capturing(FpiDeviceUpeksonly *sdev)
{
return sdev->num_rows < MAX_ROWS && (sdev->finger_state != FINGER_REMOVED);
}
static void handoff_img(FpImageDevice *dev)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
FpImage *img;
GSList *elem = self->rows;
if (!elem) {
fp_err("no rows?");
return;
}
self->rows = g_slist_reverse(self->rows);
fp_dbg("%lu rows", self->num_rows);
img = fpi_assemble_lines(&self->assembling_ctx, self->rows, self->num_rows);
g_slist_free_full(self->rows, g_free);
self->rows = NULL;
fpi_image_device_image_captured(dev, img);
fpi_image_device_report_finger_status(dev, FALSE);
self->killing_transfers = ITERATE_SSM;
self->kill_ssm = self->loopsm;
cancel_img_transfers(dev);
}
static void row_complete(FpImageDevice *dev)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
self->rowbuf_offset = -1;
if (self->num_rows > 0) {
unsigned char *lastrow = self->rows->data;
int std_sq_dev, mean_sq_diff;
std_sq_dev = fpi_std_sq_dev(self->rowbuf, self->img_width);
mean_sq_diff = fpi_mean_sq_diff_norm(lastrow, self->rowbuf,
self->img_width);
switch (self->finger_state) {
case AWAIT_FINGER:
if (self->deactivating) {
self->killing_transfers = ITERATE_SSM;
self->kill_ssm = self->loopsm;
cancel_img_transfers(dev);
}
fp_dbg("std_sq_dev: %d", std_sq_dev);
if (std_sq_dev > BLANK_THRESHOLD) {
self->num_nonblank++;
} else {
self->num_nonblank = 0;
}
if (self->num_nonblank > FINGER_PRESENT_THRESHOLD) {
self->finger_state = FINGER_DETECTED;
fpi_image_device_report_finger_status(dev,
TRUE);
} else {
return;
}
break;
case FINGER_DETECTED:
case FINGER_REMOVED:
default:
break;
}
if (std_sq_dev > BLANK_THRESHOLD) {
self->num_blank = 0;
} else {
self->num_blank++;
/* Don't consider the scan complete unless there's at least
* MIN_ROWS recorded or very long blank read occurred.
*
* Typical problem spot: one brief touch before starting the
* actual scan. Happens most commonly if scan is started
* from before the first joint resulting in a gap after the initial touch.
*/
if (self->num_blank > FINGER_REMOVED_THRESHOLD) {
self->finger_state = FINGER_REMOVED;
fp_dbg("detected finger removal. Blank rows: %d, Full rows: %lu",
self->num_blank, self->num_rows);
handoff_img(dev);
return;
}
}
fp_dbg("mean_sq_diff: %d, std_sq_dev: %d", mean_sq_diff, std_sq_dev);
fp_dbg("num_blank: %d", self->num_blank);
if (mean_sq_diff < DIFF_THRESHOLD) {
return;
}
}
switch (self->finger_state) {
case AWAIT_FINGER:
if (!self->num_rows) {
self->rows = g_slist_prepend(self->rows, self->rowbuf);
self->num_rows++;
} else {
return;
}
break;
case FINGER_DETECTED:
case FINGER_REMOVED:
self->rows = g_slist_prepend(self->rows, self->rowbuf);
self->num_rows++;
break;
}
self->rowbuf = NULL;
if (self->num_rows >= MAX_ROWS) {
fp_dbg("row limit met");
handoff_img(dev);
}
}
/* add data to row buffer */
static void add_to_rowbuf(FpImageDevice *dev, unsigned char *data, int size)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
memcpy(self->rowbuf + self->rowbuf_offset, data, size);
self->rowbuf_offset += size;
if (self->rowbuf_offset >= self->img_width)
row_complete(dev);
}
static void start_new_row(FpiDeviceUpeksonly *self, unsigned char *data,
int size)
{
if (!self->rowbuf)
self->rowbuf = g_malloc(self->img_width);
memcpy(self->rowbuf, data, size);
self->rowbuf_offset = size;
}
/* returns number of bytes left to be copied into rowbuf (capped to 62)
* or -1 if we aren't capturing anything */
static int rowbuf_remaining(FpiDeviceUpeksonly *sdev)
{
int r;
if (sdev->rowbuf_offset == -1)
return -1;
r = sdev->img_width - sdev->rowbuf_offset;
if (r > 62)
r = 62;
return r;
}
static void handle_packet(FpImageDevice *dev, unsigned char *data)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
guint16 seqnum = data[0] << 8 | data[1];
int abs_base_addr;
int for_rowbuf;
int next_row_addr;
int diff;
unsigned char dummy_data[62];
/* Init dummy data to something neutral */
memset (dummy_data, 204, 62);
data += 2; /* skip sequence number */
if (seqnum != self->last_seqnum + 1) {
if (seqnum != 0 && self->last_seqnum != 16383) {
int missing_data = seqnum - self->last_seqnum;
int i;
fp_warn("lost %d packets of data between %d and %d", missing_data,
self->last_seqnum, seqnum );
/* Minimize distortions for readers that lose a lot of packets */
for (i =1; i < missing_data; i++) {
abs_base_addr = (self->last_seqnum + 1) * 62;
/* If possible take the replacement data from last row */
if (self->num_rows > 1) {
int row_left = self->img_width - self->rowbuf_offset;
unsigned char *last_row = g_slist_nth_data (self->rows,
0);
if (row_left >= 62) {
memcpy(dummy_data,
last_row + self->rowbuf_offset,
62);
} else {
memcpy(dummy_data,
last_row + self->rowbuf_offset,
row_left);
memcpy(dummy_data + row_left, last_row , 62 - row_left);
}
}
fp_warn("adding dummy input for %d, i=%d",
self->last_seqnum + i, i);
for_rowbuf = rowbuf_remaining(self);
if (for_rowbuf != -1) {
add_to_rowbuf(dev, dummy_data, for_rowbuf);
/* row boundary */
if (for_rowbuf < 62) {
start_new_row(self,
dummy_data + for_rowbuf,
62 - for_rowbuf);
}
} else if (abs_base_addr % self->img_width == 0) {
start_new_row(self, dummy_data, 62);
} else {
/* does the data in the packet reside on a row boundary?
* if so capture it */
next_row_addr = ((abs_base_addr / self->img_width) + 1) * self->img_width;
diff = next_row_addr - abs_base_addr;
if (diff < 62)
start_new_row(self,
dummy_data + diff,
62 - diff);
}
self->last_seqnum = self->last_seqnum + 1;
}
}
}
if (seqnum <= self->last_seqnum) {
fp_dbg("detected wraparound");
self->wraparounds++;
}
self->last_seqnum = seqnum;
seqnum += self->wraparounds * 16384;
abs_base_addr = seqnum * 62;
/* are we already capturing a row? if so append the data to the
* row buffer */
for_rowbuf = rowbuf_remaining(self);
if (for_rowbuf != -1) {
add_to_rowbuf(dev, data, for_rowbuf);
/*row boundary*/
if (for_rowbuf < 62) {
start_new_row(self, data + for_rowbuf,
62 - for_rowbuf);
}
return;
}
/* does the packet START on a boundary? if so we want it in full */
if (abs_base_addr % self->img_width == 0) {
start_new_row(self, data, 62);
return;
}
/* does the data in the packet reside on a row boundary?
* if so capture it */
next_row_addr = ((abs_base_addr / self->img_width) + 1) * self->img_width;
diff = next_row_addr - abs_base_addr;
if (diff < 62)
start_new_row(self, data + diff, 62 - diff);
}
static void img_data_cb(FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
FpImageDevice *dev = FP_IMAGE_DEVICE (device);
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
int i;
self->num_flying--;
if (self->killing_transfers) {
if (self->num_flying == 0)
last_transfer_killed(dev);
/* don't care about error or success if we're terminating */
g_clear_error (&error);
return;
}
if (error) {
fp_warn("bad status %s, terminating session", error->message);
self->killing_transfers = IMG_SESSION_ERROR;
/* This cannot really happen, but just in case. */
if (!self->kill_error)
self->kill_error = error;
else
g_error_free (error);
cancel_img_transfers(dev);
return;
}
/* there are 64 packets in the transfer buffer
* each packet is 64 bytes in length
* the first 2 bytes are a sequence number
* then there are 62 bytes for image data
*/
for (i = 0; i < 4096; i += 64) {
if (!is_capturing(self))
return;
handle_packet(dev, transfer->buffer + i);
}
if (is_capturing(self)) {
fpi_usb_transfer_submit (transfer,
0,
self->img_cancellable,
img_data_cb,
user_data);
self->num_flying++;
}
}
/***** STATE MACHINE HELPERS *****/
struct write_regs_data {
FpDevice *dev;
FpiSsm *ssm;
FpiUsbTransfer *transfer;
const struct sonly_regwrite *regs;
size_t num_regs;
size_t regs_written;
};
static void write_regs_finished(struct write_regs_data *wrdata, GError *error)
{
if (!error)
fpi_ssm_next_state(wrdata->ssm);
else
fpi_ssm_mark_failed(wrdata->ssm, error);
}
static void write_regs_iterate(struct write_regs_data *wrdata);
static void write_regs_cb(FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
struct write_regs_data *wrdata = user_data;
if (error) {
write_regs_finished(wrdata, error);
return;
}
wrdata->regs_written++;
write_regs_iterate(wrdata);
}
static void write_regs_iterate(struct write_regs_data *wrdata)
{
FpiUsbTransfer *transfer;
const struct sonly_regwrite *regwrite;
if (wrdata->regs_written >= wrdata->num_regs) {
write_regs_finished(wrdata, NULL);
return;
}
regwrite = &wrdata->regs[wrdata->regs_written];
fp_dbg("set %02x=%02x", regwrite->reg, regwrite->value);
transfer = fpi_usb_transfer_new(wrdata->dev);
fpi_usb_transfer_fill_control(transfer,
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
0x0c,
0,
regwrite->reg,
1);
transfer->short_is_error = TRUE;
transfer->ssm = wrdata->ssm;
fpi_usb_transfer_submit(transfer, CTRL_TIMEOUT, NULL, write_regs_cb, NULL);
fpi_usb_transfer_unref(transfer);
transfer->buffer[0] = regwrite->value;
}
static void
sm_write_regs(FpiSsm *ssm,
FpDevice *dev,
const struct sonly_regwrite *regs,
size_t num_regs)
{
struct write_regs_data *wrdata = g_malloc(sizeof(*wrdata));
wrdata->ssm = ssm;
wrdata->regs = regs;
wrdata->num_regs = num_regs;
wrdata->regs_written = 0;
wrdata->dev = dev;
write_regs_iterate(wrdata);
}
static void sm_write_reg_cb(FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
if (error)
fpi_ssm_mark_failed(transfer->ssm, error);
else
fpi_ssm_next_state(transfer->ssm);
}
static void
sm_write_reg(FpiSsm *ssm,
FpImageDevice *dev,
guint8 reg,
guint8 value)
{
FpiUsbTransfer *transfer = fpi_usb_transfer_new(FP_DEVICE (dev));
fp_dbg("set %02x=%02x", reg, value);
fpi_usb_transfer_fill_control(transfer,
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
0x0c,
0,
reg,
1);
transfer->short_is_error = TRUE;
transfer->ssm = ssm;
fpi_usb_transfer_submit(transfer, CTRL_TIMEOUT, NULL, sm_write_reg_cb, NULL);
fpi_usb_transfer_unref(transfer);
transfer->buffer[0] = value;
}
static void sm_read_reg_cb(FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
FpImageDevice *dev = FP_IMAGE_DEVICE(device);
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
if (error) {
fpi_ssm_mark_failed(transfer->ssm, error);
} else {
self->read_reg_result = transfer->buffer[0];
fp_dbg("read reg result = %02x", self->read_reg_result);
fpi_ssm_next_state(transfer->ssm);
}
g_free(transfer->buffer);
}
static void
sm_read_reg(FpiSsm *ssm,
FpImageDevice *dev,
guint8 reg)
{
FpiUsbTransfer *transfer = fpi_usb_transfer_new(FP_DEVICE(dev));
fp_dbg("read reg %02x", reg);
fpi_usb_transfer_fill_control(transfer,
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
0x0c,
0,
reg,
8);
transfer->ssm = ssm;
transfer->short_is_error = TRUE;
fpi_usb_transfer_submit(transfer,
CTRL_TIMEOUT,
NULL,
sm_read_reg_cb,
NULL);
fpi_usb_transfer_unref(transfer);
}
static void sm_await_intr_cb(FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
FpImageDevice *dev = FP_IMAGE_DEVICE(device);
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
if (error) {
g_free(transfer->buffer);
fpi_ssm_mark_failed(transfer->ssm, error);
return;
}
fp_dbg("interrupt received: %02x %02x %02x %02x",
transfer->buffer[0], transfer->buffer[1],
transfer->buffer[2], transfer->buffer[3]);
g_free(transfer->buffer);
self->finger_state = FINGER_DETECTED;
fpi_image_device_report_finger_status(dev, TRUE);
fpi_ssm_next_state(transfer->ssm);
}
static void
sm_await_intr(FpiSsm *ssm,
FpImageDevice *dev)
{
FpiUsbTransfer *transfer = fpi_usb_transfer_new(FP_DEVICE(dev));
G_DEBUG_HERE();
fpi_usb_transfer_fill_interrupt (transfer, 0x83, 4);
transfer->short_is_error = TRUE;
transfer->ssm = ssm;
/* NOTE: This was changed to be cancellable with the version 2 port! */
fpi_usb_transfer_submit (transfer,
0,
fpi_device_get_cancellable (FP_DEVICE (dev)),
sm_await_intr_cb,
NULL);
fpi_usb_transfer_unref (transfer);
}
/***** AWAIT FINGER *****/
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,
};
enum awfsm_1000_states {
AWFSM_1000_WRITEV_1,
AWFSM_1000_WRITEV_2,
AWFSM_1000_NUM_STATES,
};
static void awfsm_2016_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
FpImageDevice *dev = user_data;
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case AWFSM_2016_WRITEV_1:
sm_write_regs(ssm, _dev, awfsm_2016_writev_1, G_N_ELEMENTS(awfsm_2016_writev_1));
break;
case AWFSM_2016_READ_01:
sm_read_reg(ssm, dev, 0x01);
break;
case AWFSM_2016_WRITE_01:
if (self->read_reg_result != 0xc6)
sm_write_reg(ssm, dev, 0x01, 0x46);
else
sm_write_reg(ssm, dev, 0x01, 0xc6);
break;
case AWFSM_2016_WRITEV_2:
sm_write_regs(ssm, _dev, awfsm_2016_writev_2, G_N_ELEMENTS(awfsm_2016_writev_2));
break;
case AWFSM_2016_READ_13:
sm_read_reg(ssm, dev, 0x13);
break;
case AWFSM_2016_WRITE_13:
if (self->read_reg_result != 0x45)
sm_write_reg(ssm, dev, 0x13, 0x05);
else
sm_write_reg(ssm, dev, 0x13, 0x45);
break;
case AWFSM_2016_WRITEV_3:
sm_write_regs(ssm, _dev, awfsm_2016_writev_3, G_N_ELEMENTS(awfsm_2016_writev_3));
break;
case AWFSM_2016_READ_07:
sm_read_reg(ssm, dev, 0x07);
break;
case AWFSM_2016_WRITE_07:
if (self->read_reg_result != 0x10 && self->read_reg_result != 0x90)
fp_warn("odd reg7 value %x", self->read_reg_result);
sm_write_reg(ssm, dev, 0x07, self->read_reg_result);
break;
case AWFSM_2016_WRITEV_4:
sm_write_regs(ssm, _dev, awfsm_2016_writev_4, G_N_ELEMENTS(awfsm_2016_writev_4));
break;
}
}
static void awfsm_1000_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
switch (fpi_ssm_get_cur_state(ssm)) {
case AWFSM_1000_WRITEV_1:
sm_write_regs(ssm, _dev, awfsm_1000_writev_1, G_N_ELEMENTS(awfsm_1000_writev_1));
break;
case AWFSM_1000_WRITEV_2:
sm_write_regs(ssm, _dev, awfsm_1000_writev_2, G_N_ELEMENTS(awfsm_1000_writev_2));
break;
}
}
/***** 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,
};
enum capsm_1000_states {
CAPSM_1000_INIT,
CAPSM_1000_FIRE_BULK,
CAPSM_1000_WRITEV,
CAPSM_1000_NUM_STATES,
};
enum capsm_1001_states {
CAPSM_1001_INIT,
CAPSM_1001_FIRE_BULK,
CAPSM_1001_WRITEV_1,
CAPSM_1001_WRITEV_2,
CAPSM_1001_WRITEV_3,
CAPSM_1001_WRITEV_4,
CAPSM_1001_WRITEV_5,
CAPSM_1001_NUM_STATES,
};
static void
capsm_fire_bulk(FpiSsm *ssm,
FpDevice *dev)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
int i;
g_assert (self->capturing == FALSE);
g_clear_object (&self->img_cancellable);
self->img_cancellable = g_cancellable_new ();
for (i = 0; i < self->img_transfers->len; i++) {
fpi_usb_transfer_submit(g_ptr_array_index (self->img_transfers, i),
0,
self->img_cancellable,
img_data_cb,
NULL);
self->num_flying++;
}
self->capturing = TRUE;
fpi_ssm_next_state(ssm);
}
static void capsm_2016_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
FpImageDevice *dev = user_data;
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPSM_2016_INIT:
self->rowbuf_offset = -1;
self->num_rows = 0;
self->wraparounds = -1;
self->num_blank = 0;
self->num_nonblank = 0;
self->finger_state = FINGER_DETECTED;
self->last_seqnum = 16383;
self->killing_transfers = 0;
fpi_ssm_next_state(ssm);
break;
case CAPSM_2016_WRITE_15:
sm_write_reg(ssm, dev, 0x15, 0x20);
break;
case CAPSM_2016_WRITE_30:
sm_write_reg(ssm, dev, 0x30, 0xe0);
break;
case CAPSM_2016_FIRE_BULK: ;
capsm_fire_bulk (ssm, _dev);
break;
case CAPSM_2016_WRITEV:
sm_write_regs(ssm, _dev, capsm_2016_writev, G_N_ELEMENTS(capsm_2016_writev));
break;
}
}
static void capsm_1000_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPSM_1000_INIT:
self->rowbuf_offset = -1;
self->num_rows = 0;
self->wraparounds = -1;
self->num_blank = 0;
self->num_nonblank = 0;
self->finger_state = FINGER_DETECTED;
self->last_seqnum = 16383;
self->killing_transfers = 0;
fpi_ssm_next_state(ssm);
break;
case CAPSM_1000_FIRE_BULK: ;
capsm_fire_bulk (ssm, _dev);
break;
case CAPSM_1000_WRITEV:
sm_write_regs(ssm, _dev, capsm_1000_writev, G_N_ELEMENTS(capsm_1000_writev));
break;
}
}
static void capsm_1001_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPSM_1001_INIT:
self->rowbuf_offset = -1;
self->num_rows = 0;
self->wraparounds = -1;
self->num_blank = 0;
self->num_nonblank = 0;
self->finger_state = AWAIT_FINGER;
self->last_seqnum = 16383;
self->killing_transfers = 0;
fpi_ssm_next_state(ssm);
break;
case CAPSM_1001_FIRE_BULK: ;
capsm_fire_bulk (ssm, _dev);
break;
case CAPSM_1001_WRITEV_1:
sm_write_regs(ssm, _dev, capsm_1001_writev_1, G_N_ELEMENTS(capsm_1001_writev_1));
break;
case CAPSM_1001_WRITEV_2:
sm_write_regs(ssm, _dev, capsm_1001_writev_2, G_N_ELEMENTS(capsm_1001_writev_2));
break;
case CAPSM_1001_WRITEV_3:
sm_write_regs(ssm, _dev, capsm_1001_writev_3, G_N_ELEMENTS(capsm_1001_writev_3));
break;
case CAPSM_1001_WRITEV_4:
sm_write_regs(ssm, _dev, capsm_1001_writev_4, G_N_ELEMENTS(capsm_1001_writev_4));
break;
case CAPSM_1001_WRITEV_5:
sm_write_regs(ssm, _dev, capsm_1001_writev_5, G_N_ELEMENTS(capsm_1001_writev_5));
break;
}
}
/***** DEINITIALIZATION *****/
enum deinitsm_2016_states {
DEINITSM_2016_WRITEV,
DEINITSM_2016_NUM_STATES,
};
enum deinitsm_1000_states {
DEINITSM_1000_WRITEV,
DEINITSM_1000_NUM_STATES,
};
enum deinitsm_1001_states {
DEINITSM_1001_WRITEV,
DEINITSM_1001_NUM_STATES,
};
static void deinitsm_2016_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
switch (fpi_ssm_get_cur_state(ssm)) {
case DEINITSM_2016_WRITEV:
sm_write_regs(ssm, _dev, deinitsm_2016_writev, G_N_ELEMENTS(deinitsm_2016_writev));
break;
}
}
static void deinitsm_1000_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
switch (fpi_ssm_get_cur_state(ssm)) {
case DEINITSM_1000_WRITEV:
sm_write_regs(ssm, _dev, deinitsm_1000_writev, G_N_ELEMENTS(deinitsm_1000_writev));
break;
}
}
static void deinitsm_1001_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
switch (fpi_ssm_get_cur_state(ssm)) {
case DEINITSM_1001_WRITEV:
sm_write_regs(ssm, _dev, deinitsm_1001_writev, G_N_ELEMENTS(deinitsm_1001_writev));
break;
}
}
/***** INITIALIZATION *****/
enum initsm_2016_states {
INITSM_2016_WRITEV_1,
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_NUM_STATES,
};
enum initsm_1001_states {
INITSM_1001_WRITEV_1,
INITSM_1001_WRITEV_2,
INITSM_1001_WRITEV_3,
INITSM_1001_WRITEV_4,
INITSM_1001_WRITEV_5,
INITSM_1001_NUM_STATES,
};
static void initsm_2016_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
FpImageDevice *dev = user_data;
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case INITSM_2016_WRITEV_1:
sm_write_regs(ssm, _dev, initsm_2016_writev_1, G_N_ELEMENTS(initsm_2016_writev_1));
break;
case INITSM_2016_READ_09:
sm_read_reg(ssm, dev, 0x09);
break;
case INITSM_2016_WRITE_09:
sm_write_reg(ssm, dev, 0x09, self->read_reg_result & ~0x08);
break;
case INITSM_2016_READ_13:
sm_read_reg(ssm, dev, 0x13);
break;
case INITSM_2016_WRITE_13:
sm_write_reg(ssm, dev, 0x13, self->read_reg_result & ~0x10);
break;
case INITSM_2016_WRITE_04:
sm_write_reg(ssm, dev, 0x04, 0x00);
break;
case INITSM_2016_WRITE_05:
sm_write_reg(ssm, dev, 0x05, 0x00);
break;
}
}
static void initsm_1000_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
switch (fpi_ssm_get_cur_state(ssm)) {
case INITSM_1000_WRITEV_1:
sm_write_regs(ssm, _dev, initsm_1000_writev_1, G_N_ELEMENTS(initsm_1000_writev_1));
break;
}
}
static void initsm_1001_run_state(FpiSsm *ssm, FpDevice *_dev,
void *user_data)
{
switch (fpi_ssm_get_cur_state(ssm)) {
case INITSM_1001_WRITEV_1:
sm_write_regs(ssm, _dev, initsm_1001_writev_1, G_N_ELEMENTS(initsm_1001_writev_1));
break;
case INITSM_1001_WRITEV_2:
sm_write_regs(ssm, _dev, initsm_1001_writev_2, G_N_ELEMENTS(initsm_1001_writev_2));
break;
case INITSM_1001_WRITEV_3:
sm_write_regs(ssm, _dev, initsm_1001_writev_3, G_N_ELEMENTS(initsm_1001_writev_3));
break;
case INITSM_1001_WRITEV_4:
sm_write_regs(ssm, _dev, initsm_1001_writev_4, G_N_ELEMENTS(initsm_1001_writev_4));
break;
case INITSM_1001_WRITEV_5:
sm_write_regs(ssm, _dev, initsm_1001_writev_5, G_N_ELEMENTS(initsm_1001_writev_5));
break;
}
}
/***** CAPTURE LOOP *****/
enum loopsm_states {
LOOPSM_RUN_AWFSM,
LOOPSM_AWAIT_FINGER,
LOOPSM_RUN_CAPSM,
LOOPSM_CAPTURE,
LOOPSM_RUN_DEINITSM,
LOOPSM_FINAL,
LOOPSM_NUM_STATES,
};
static void loopsm_run_state(FpiSsm *ssm, FpDevice *_dev, void *user_data)
{
FpImageDevice *dev = user_data;
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case LOOPSM_RUN_AWFSM: ;
switch (self->dev_model) {
case UPEKSONLY_1001:
if (self->deactivating) {
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_next_state(ssm);
}
break;
default:
if (self->deactivating) {
fpi_ssm_mark_completed(ssm);
} else {
FpiSsm *awfsm = NULL;
switch (self->dev_model) {
case UPEKSONLY_2016:
awfsm = fpi_ssm_new(FP_DEVICE(dev),
awfsm_2016_run_state,
AWFSM_2016_NUM_STATES,
dev);
break;
case UPEKSONLY_1000:
awfsm = fpi_ssm_new(FP_DEVICE(dev),
awfsm_1000_run_state,
AWFSM_1000_NUM_STATES,
dev);
break;
}
fpi_ssm_start_subsm(ssm, awfsm);
}
break;
}
break;
case LOOPSM_AWAIT_FINGER:
switch (self->dev_model) {
case UPEKSONLY_1001:
fpi_ssm_next_state(ssm);
break;
default:
sm_await_intr(ssm, dev);
break;
}
break;
case LOOPSM_RUN_CAPSM: ;
FpiSsm *capsm = NULL;
switch (self->dev_model) {
case UPEKSONLY_2016:
capsm = fpi_ssm_new(FP_DEVICE(dev),
capsm_2016_run_state,
CAPSM_2016_NUM_STATES, dev);
break;
case UPEKSONLY_1000:
capsm = fpi_ssm_new(FP_DEVICE(dev),
capsm_1000_run_state,
CAPSM_1000_NUM_STATES, dev);
break;
case UPEKSONLY_1001:
capsm = fpi_ssm_new(FP_DEVICE(dev),
capsm_1001_run_state,
CAPSM_1001_NUM_STATES, dev);
break;
}
fpi_ssm_start_subsm(ssm, capsm);
break;
case LOOPSM_CAPTURE:
break;
case LOOPSM_RUN_DEINITSM: ;
FpiSsm *deinitsm = NULL;
switch (self->dev_model) {
case UPEKSONLY_2016:
deinitsm = fpi_ssm_new(FP_DEVICE(dev),
deinitsm_2016_run_state,
DEINITSM_2016_NUM_STATES, dev);
break;
case UPEKSONLY_1000:
deinitsm = fpi_ssm_new(FP_DEVICE(dev),
deinitsm_1000_run_state,
DEINITSM_1000_NUM_STATES, dev);
break;
case UPEKSONLY_1001:
deinitsm = fpi_ssm_new(FP_DEVICE(dev),
deinitsm_1001_run_state,
DEINITSM_1001_NUM_STATES, dev);
break;
}
self->capturing = FALSE;
fpi_ssm_start_subsm(ssm, deinitsm);
break;
case LOOPSM_FINAL:
fpi_ssm_jump_to_state(ssm, LOOPSM_RUN_AWFSM);
break;
}
}
/***** DRIVER STUFF *****/
static void deactivate_done(FpImageDevice *dev, GError *error)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
G_DEBUG_HERE();
free_img_transfers(self);
g_free(self->rowbuf);
self->rowbuf = NULL;
g_slist_free_full (self->rows, g_free);
self->rows = NULL;
fpi_image_device_deactivate_complete(dev, error);
}
static void dev_deactivate(FpImageDevice *dev)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
if (!self->capturing) {
deactivate_done(dev, NULL);
return;
}
self->deactivating = TRUE;
self->killing_transfers = ITERATE_SSM;
self->kill_ssm = self->loopsm;
cancel_img_transfers(dev);
}
static void loopsm_complete(FpiSsm *ssm, FpDevice *_dev, void *user_data, GError *error)
{
FpImageDevice *dev = user_data;
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(_dev);
fpi_ssm_free(ssm);
if (self->deactivating) {
deactivate_done(dev, error);
return;
}
if (error) {
fpi_image_device_session_error(dev, error);
return;
}
}
static void initsm_complete(FpiSsm *ssm, FpDevice *_dev, void *user_data,
GError *error)
{
FpImageDevice *dev = user_data;
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(_dev);
fpi_ssm_free(ssm);
fpi_image_device_activate_complete(dev, error);
if (error)
return;
self->loopsm = fpi_ssm_new(FP_DEVICE(dev), loopsm_run_state,
LOOPSM_NUM_STATES, dev);
fpi_ssm_start(self->loopsm, loopsm_complete);
}
static void dev_activate(FpImageDevice *dev)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY(dev);
FpiSsm *ssm = NULL;
int i;
self->deactivating = FALSE;
self->capturing = FALSE;
self->img_transfers = g_ptr_array_new_full(NUM_BULK_TRANSFERS, (GDestroyNotify) fpi_usb_transfer_unref);
self->num_flying = 0;
for (i = 0; i < self->img_transfers->len; i++) {
FpiUsbTransfer *transfer;
transfer = fpi_usb_transfer_new (FP_DEVICE (dev));
fpi_usb_transfer_fill_bulk (transfer, 0x81, 4096);
g_ptr_array_add (self->img_transfers, transfer);
}
switch (self->dev_model) {
case UPEKSONLY_2016:
ssm = fpi_ssm_new(FP_DEVICE(dev), initsm_2016_run_state,
INITSM_2016_NUM_STATES, dev);
break;
case UPEKSONLY_1000:
ssm = fpi_ssm_new(FP_DEVICE(dev), initsm_1000_run_state,
INITSM_1000_NUM_STATES, dev);
break;
case UPEKSONLY_1001:
ssm = fpi_ssm_new(FP_DEVICE(dev), initsm_1001_run_state,
INITSM_1001_NUM_STATES, dev);
break;
}
fpi_ssm_start(ssm, initsm_complete);
}
static void dev_init(FpImageDevice *dev);
static void dev_deinit(FpImageDevice *dev)
{
GError *error = NULL;
g_usb_device_release_interface(fpi_device_get_usb_device(FP_DEVICE(dev)),
0, 0, &error);
fpi_image_device_close_complete(dev, error);
}
static gint dev_discover(GUsbDevice *usb_device)
{
guint16 pid = g_usb_device_get_pid (usb_device);
guint16 bcd = g_usb_device_get_release (usb_device);
if (pid == 0x2016) {
if (bcd == 1) /* Revision 1 is what we're interested in */
return 1;
}
if (pid == 0x1000) {
if (bcd == 0x0033) /* Looking for revision 0.33 */
return 1;
}
if (pid == 0x1001)
return 1;
return 0;
}
static const FpIdEntry id_table [ ] = {
{ .vid = 0x147e, .pid = 0x2016, .driver_data = UPEKSONLY_2016 },
{ .vid = 0x147e, .pid = 0x1000, .driver_data = UPEKSONLY_1000 },
{ .vid = 0x147e, .pid = 0x1001, .driver_data = UPEKSONLY_1001 },
{ .vid = 0, .pid = 0, .driver_data = 0 },
};
static void fpi_device_upeksonly_init(FpiDeviceUpeksonly *self) {
}
static void fpi_device_upeksonly_class_init(FpiDeviceUpeksonlyClass *klass) {
FpDeviceClass *dev_class = FP_DEVICE_CLASS(klass);
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS(klass);
dev_class->id = "upeksonly";
dev_class->full_name = "UPEK TouchStrip Sensor-Only";
dev_class->type = FP_DEVICE_TYPE_USB;
dev_class->id_table = id_table;
dev_class->scan_type = FP_SCAN_TYPE_SWIPE;
dev_class->usb_discover = dev_discover;
img_class->img_open = dev_init;
img_class->img_close = dev_deinit;
img_class->activate = dev_activate;
img_class->deactivate = dev_deactivate;
img_class->img_width = -1;
img_class->img_height = -1;
}
static void dev_init(FpImageDevice *dev)
{
GError *error = NULL;
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (dev);
if (!g_usb_device_set_configuration (fpi_device_get_usb_device(FP_DEVICE(dev)), 1, &error)) {
fp_err("could not set configuration 1");
fpi_image_device_open_complete(dev, error);
}
if (!g_usb_device_claim_interface(fpi_device_get_usb_device(FP_DEVICE(dev)), 0, 0, &error)) {
fpi_image_device_open_complete(dev, error);
return;
}
self->assembling_ctx.max_height = 1024;
self->assembling_ctx.resolution = 8;
self->assembling_ctx.median_filter_size = 25;
self->assembling_ctx.max_search_offset = 30;
self->assembling_ctx.get_deviation = upeksonly_get_deviation2;
self->assembling_ctx.get_pixel = upeksonly_get_pixel;
self = FPI_DEVICE_UPEKSONLY(dev);
self->dev_model = (int)fpi_device_get_driver_data (FP_DEVICE (dev));
switch (self->dev_model) {
case UPEKSONLY_1000:
self->img_width = IMG_WIDTH_1000;
self->assembling_ctx.line_width = IMG_WIDTH_1000;
break;
case UPEKSONLY_1001:
self->img_width = IMG_WIDTH_1001;
self->assembling_ctx.line_width = IMG_WIDTH_1001;
/* The sensor resolution is too low for the normal threshold. */
fpi_image_device_set_bz3_threshold (dev, 25);
break;
case UPEKSONLY_2016:
self->img_width = IMG_WIDTH_2016;
self->assembling_ctx.line_width = IMG_WIDTH_2016;
break;
default:
g_assert_not_reached ();
}
fpi_image_device_open_complete(dev, NULL);
}