ea6d5ba6d6
ID is just a some magic number to make fingerprint from one scanner model incompatible with another scanner model. Get rid of "magic", declare enum and use it. https://bugs.freedesktop.org/show_bug.cgi?id=56956
1288 lines
31 KiB
C
1288 lines
31 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>
|
|
*
|
|
* 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 <errno.h>
|
|
#include <string.h>
|
|
|
|
#include <glib.h>
|
|
#include <libusb.h>
|
|
|
|
#include <fp_internal.h>
|
|
|
|
#include "driver_ids.h"
|
|
|
|
#define CTRL_TIMEOUT 1000
|
|
#define IMG_WIDTH 288
|
|
#define NUM_BULK_TRANSFERS 24
|
|
#define MAX_ROWS 700
|
|
#define MIN_ROWS 64
|
|
|
|
enum {
|
|
UPEKSONLY_2016,
|
|
UPEKSONLY_1000,
|
|
};
|
|
|
|
struct img_transfer_data {
|
|
int idx;
|
|
struct fp_img_dev *dev;
|
|
gboolean flying;
|
|
gboolean cancelling;
|
|
};
|
|
|
|
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,
|
|
};
|
|
|
|
struct sonly_dev {
|
|
gboolean capturing;
|
|
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;
|
|
int num_flying;
|
|
|
|
GSList *rows;
|
|
size_t num_rows;
|
|
unsigned char *rowbuf;
|
|
int rowbuf_offset;
|
|
|
|
int wraparounds;
|
|
int num_blank;
|
|
int finger_removed;
|
|
int last_seqnum;
|
|
|
|
enum sonly_kill_transfers_action killing_transfers;
|
|
int kill_status_code;
|
|
union {
|
|
struct fpi_ssm *kill_ssm;
|
|
void (*kill_cb)(struct fp_img_dev *dev);
|
|
};
|
|
};
|
|
|
|
struct sonly_regwrite {
|
|
uint8_t reg;
|
|
uint8_t value;
|
|
};
|
|
|
|
/***** IMAGE PROCESSING *****/
|
|
|
|
static void free_img_transfers(struct sonly_dev *sdev)
|
|
{
|
|
int i;
|
|
for (i = 0; i < NUM_BULK_TRANSFERS; i++) {
|
|
struct libusb_transfer *transfer = sdev->img_transfer[i];
|
|
if (!transfer)
|
|
continue;
|
|
|
|
g_free(transfer->buffer);
|
|
libusb_free_transfer(transfer);
|
|
}
|
|
g_free(sdev->img_transfer_data);
|
|
}
|
|
|
|
static void last_transfer_killed(struct fp_img_dev *dev)
|
|
{
|
|
struct sonly_dev *sdev = dev->priv;
|
|
switch (sdev->killing_transfers) {
|
|
case ABORT_SSM:
|
|
fp_dbg("abort ssm error %d", sdev->kill_status_code);
|
|
fpi_ssm_mark_aborted(sdev->kill_ssm, sdev->kill_status_code);
|
|
return;
|
|
case ITERATE_SSM:
|
|
fp_dbg("iterate ssm");
|
|
fpi_ssm_next_state(sdev->kill_ssm);
|
|
return;
|
|
case IMG_SESSION_ERROR:
|
|
fp_dbg("session error %d", sdev->kill_status_code);
|
|
fpi_imgdev_session_error(dev, sdev->kill_status_code);
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void cancel_img_transfers(struct fp_img_dev *dev)
|
|
{
|
|
struct sonly_dev *sdev = dev->priv;
|
|
int i;
|
|
|
|
if (sdev->num_flying == 0) {
|
|
last_transfer_killed(dev);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < NUM_BULK_TRANSFERS; i++) {
|
|
struct img_transfer_data *idata = &sdev->img_transfer_data[i];
|
|
if (!idata->flying || idata->cancelling)
|
|
continue;
|
|
fp_dbg("cancelling transfer %d", i);
|
|
int r = libusb_cancel_transfer(sdev->img_transfer[i]);
|
|
if (r < 0)
|
|
fp_dbg("cancel failed error %d", r);
|
|
idata->cancelling = TRUE;
|
|
}
|
|
}
|
|
|
|
static gboolean is_capturing(struct sonly_dev *sdev)
|
|
{
|
|
return sdev->num_rows < MAX_ROWS && !sdev->finger_removed;
|
|
}
|
|
|
|
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);
|
|
GSList *elem = sdev->rows;
|
|
size_t offset = 0;
|
|
|
|
if (!elem) {
|
|
fp_err("no rows?");
|
|
return;
|
|
}
|
|
|
|
fp_dbg("%d rows", sdev->num_rows);
|
|
img->height = sdev->num_rows;
|
|
|
|
/* The scans from this device are rolled right by two colums
|
|
* It feels a lot smarter to correct here than mess with it at
|
|
* read time*/
|
|
do {
|
|
memcpy(img->data + offset, elem->data + 2, IMG_WIDTH - 2);
|
|
memcpy(img->data + offset + IMG_WIDTH - 2, elem->data, 2);
|
|
g_free(elem->data);
|
|
offset += IMG_WIDTH;
|
|
} while ((elem = g_slist_next(elem)) != NULL);
|
|
|
|
g_slist_free(sdev->rows);
|
|
sdev->rows = NULL;
|
|
|
|
fpi_imgdev_image_captured(dev, img);
|
|
fpi_imgdev_report_finger_status(dev, FALSE);
|
|
|
|
sdev->killing_transfers = ITERATE_SSM;
|
|
sdev->kill_ssm = sdev->loopsm;
|
|
cancel_img_transfers(dev);
|
|
}
|
|
|
|
static void compute_rows(unsigned char *a, unsigned char *b, int *diff,
|
|
int *total)
|
|
{
|
|
int i;
|
|
int _total = 0;
|
|
int _diff = 0;
|
|
|
|
for (i = 0; i < IMG_WIDTH; i++) {
|
|
if (a[i] > b[i])
|
|
_diff += a[i] - b[i];
|
|
else
|
|
_diff += b[i] - a[i];
|
|
_total += b[i];
|
|
}
|
|
*diff = _diff;
|
|
*total = _total;
|
|
}
|
|
|
|
static void row_complete(struct fp_img_dev *dev)
|
|
{
|
|
struct sonly_dev *sdev = dev->priv;
|
|
sdev->rowbuf_offset = -1;
|
|
|
|
if (sdev->num_rows > 0) {
|
|
unsigned char *lastrow = sdev->rows->data;
|
|
int diff;
|
|
int total;
|
|
|
|
compute_rows(lastrow, sdev->rowbuf, &diff, &total);
|
|
|
|
if (total < 52000) {
|
|
sdev->num_blank = 0;
|
|
} else {
|
|
sdev->num_blank++;
|
|
/* Don't consider the scan complete unless theres 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 inital touch.
|
|
*/
|
|
if ((sdev->num_blank > 500)
|
|
&& ((sdev->num_rows > MIN_ROWS) || (sdev->num_blank > 5000))) {
|
|
sdev->finger_removed = 1;
|
|
fp_dbg("detected finger removal. Blank rows: %d, Full rows: %d", sdev->num_blank, sdev->num_rows);
|
|
handoff_img(dev);
|
|
return;
|
|
}
|
|
}
|
|
if (diff < 3000)
|
|
return;
|
|
}
|
|
|
|
sdev->rows = g_slist_prepend(sdev->rows, sdev->rowbuf);
|
|
sdev->num_rows++;
|
|
sdev->rowbuf = NULL;
|
|
|
|
if (sdev->num_rows >= MAX_ROWS) {
|
|
fp_dbg("row limit met");
|
|
handoff_img(dev);
|
|
}
|
|
}
|
|
|
|
/* add data to row buffer */
|
|
static void add_to_rowbuf(struct fp_img_dev *dev, unsigned char *data, int size)
|
|
{
|
|
struct sonly_dev *sdev = dev->priv;
|
|
|
|
memcpy(sdev->rowbuf + sdev->rowbuf_offset, data, size);
|
|
sdev->rowbuf_offset += size;
|
|
if (sdev->rowbuf_offset >= IMG_WIDTH)
|
|
row_complete(dev);
|
|
|
|
}
|
|
|
|
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, data, size);
|
|
sdev->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(struct sonly_dev *sdev)
|
|
{
|
|
int r;
|
|
|
|
if (sdev->rowbuf_offset == -1)
|
|
return -1;
|
|
|
|
r = IMG_WIDTH - sdev->rowbuf_offset;
|
|
if (r > 62)
|
|
r = 62;
|
|
return r;
|
|
}
|
|
|
|
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 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 != sdev->last_seqnum + 1) {
|
|
if (seqnum != 0 && sdev->last_seqnum != 16383) {
|
|
int missing_data = seqnum - sdev->last_seqnum;
|
|
int i;
|
|
fp_warn("lost %d packets of data between %d and %d", missing_data, sdev->last_seqnum, seqnum );
|
|
|
|
/* Minimize distortions for readers that lose a lot of packets */
|
|
for (i =1; i < missing_data; i++) {
|
|
abs_base_addr = (sdev->last_seqnum + 1) * 62;
|
|
|
|
/* If possible take the replacement data from last row */
|
|
if (sdev->num_rows > 1) {
|
|
int row_left = IMG_WIDTH - sdev->rowbuf_offset;
|
|
unsigned char *last_row = g_slist_nth_data (sdev->rows, 0);
|
|
|
|
if (row_left >= 62) {
|
|
memcpy(dummy_data, last_row + sdev->rowbuf_offset, 62);
|
|
} else {
|
|
memcpy(dummy_data, last_row + sdev->rowbuf_offset, row_left);
|
|
memcpy(dummy_data + row_left, last_row , 62 - row_left);
|
|
}
|
|
}
|
|
|
|
fp_warn("adding dummy input for %d, i=%d", sdev->last_seqnum + i, i);
|
|
for_rowbuf = rowbuf_remaining(sdev);
|
|
if (for_rowbuf != -1) {
|
|
add_to_rowbuf(dev, dummy_data, for_rowbuf);
|
|
/* row boundary */
|
|
if (for_rowbuf < 62) {
|
|
start_new_row(sdev, dummy_data + for_rowbuf, 62 - for_rowbuf);
|
|
}
|
|
} else if (abs_base_addr % IMG_WIDTH == 0) {
|
|
start_new_row(sdev, 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 / IMG_WIDTH) + 1) * IMG_WIDTH;
|
|
diff = next_row_addr - abs_base_addr;
|
|
if (diff < 62)
|
|
start_new_row(sdev, dummy_data + diff, 62 - diff);
|
|
}
|
|
sdev->last_seqnum = sdev->last_seqnum + 1;
|
|
}
|
|
}
|
|
}
|
|
if (seqnum <= sdev->last_seqnum) {
|
|
fp_dbg("detected wraparound");
|
|
sdev->wraparounds++;
|
|
}
|
|
|
|
sdev->last_seqnum = seqnum;
|
|
seqnum += sdev->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(sdev);
|
|
if (for_rowbuf != -1) {
|
|
add_to_rowbuf(dev, data, for_rowbuf);
|
|
/*row boundary*/
|
|
if (for_rowbuf < 62) {
|
|
start_new_row(sdev, 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 % IMG_WIDTH == 0) {
|
|
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)
|
|
{
|
|
struct img_transfer_data *idata = transfer->user_data;
|
|
struct fp_img_dev *dev = idata->dev;
|
|
struct sonly_dev *sdev = dev->priv;
|
|
int i;
|
|
|
|
idata->flying = FALSE;
|
|
idata->cancelling = FALSE;
|
|
sdev->num_flying--;
|
|
|
|
if (sdev->killing_transfers) {
|
|
if (sdev->num_flying == 0)
|
|
last_transfer_killed(dev);
|
|
|
|
/* don't care about error or success if we're terminating */
|
|
return;
|
|
}
|
|
|
|
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
|
fp_warn("bad status %d, terminating session", transfer->status);
|
|
sdev->killing_transfers = IMG_SESSION_ERROR;
|
|
sdev->kill_status_code = transfer->status;
|
|
cancel_img_transfers(dev);
|
|
}
|
|
|
|
/* 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(sdev))
|
|
return;
|
|
handle_packet(dev, transfer->buffer + i);
|
|
}
|
|
|
|
if (is_capturing(sdev)) {
|
|
int r = libusb_submit_transfer(transfer);
|
|
if (r < 0) {
|
|
fp_warn("failed resubmit, error %d", r);
|
|
sdev->killing_transfers = IMG_SESSION_ERROR;
|
|
sdev->kill_status_code = r;
|
|
cancel_img_transfers(dev);
|
|
return;
|
|
}
|
|
sdev->num_flying++;
|
|
idata->flying = TRUE;
|
|
}
|
|
}
|
|
|
|
/***** STATE MACHINE HELPERS *****/
|
|
|
|
struct write_regs_data {
|
|
struct fpi_ssm *ssm;
|
|
struct libusb_transfer *transfer;
|
|
const struct sonly_regwrite *regs;
|
|
size_t num_regs;
|
|
size_t regs_written;
|
|
};
|
|
|
|
static void write_regs_finished(struct write_regs_data *wrdata, int result)
|
|
{
|
|
g_free(wrdata->transfer->buffer);
|
|
libusb_free_transfer(wrdata->transfer);
|
|
if (result == 0)
|
|
fpi_ssm_next_state(wrdata->ssm);
|
|
else
|
|
fpi_ssm_mark_aborted(wrdata->ssm, result);
|
|
g_free(wrdata);
|
|
}
|
|
|
|
|
|
static void write_regs_iterate(struct write_regs_data *wrdata)
|
|
{
|
|
struct libusb_control_setup *setup;
|
|
const struct sonly_regwrite *regwrite;
|
|
int r;
|
|
|
|
if (wrdata->regs_written >= wrdata->num_regs) {
|
|
write_regs_finished(wrdata, 0);
|
|
return;
|
|
}
|
|
|
|
regwrite = &wrdata->regs[wrdata->regs_written];
|
|
|
|
fp_dbg("set %02x=%02x", regwrite->reg, regwrite->value);
|
|
setup = libusb_control_transfer_get_setup(wrdata->transfer);
|
|
setup->wIndex = regwrite->reg;
|
|
wrdata->transfer->buffer[LIBUSB_CONTROL_SETUP_SIZE] = regwrite->value;
|
|
|
|
r = libusb_submit_transfer(wrdata->transfer);
|
|
if (r < 0)
|
|
write_regs_finished(wrdata, r);
|
|
}
|
|
|
|
static void write_regs_cb(struct libusb_transfer *transfer)
|
|
{
|
|
struct write_regs_data *wrdata = transfer->user_data;
|
|
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
|
write_regs_finished(wrdata, transfer->status);
|
|
return;
|
|
}
|
|
|
|
wrdata->regs_written++;
|
|
write_regs_iterate(wrdata);
|
|
}
|
|
|
|
static void sm_write_regs(struct fpi_ssm *ssm,
|
|
const struct sonly_regwrite *regs, size_t num_regs)
|
|
{
|
|
struct write_regs_data *wrdata = g_malloc(sizeof(*wrdata));
|
|
unsigned char *data;
|
|
|
|
wrdata->transfer = libusb_alloc_transfer(0);
|
|
if (!wrdata->transfer) {
|
|
g_free(wrdata);
|
|
fpi_ssm_mark_aborted(ssm, -ENOMEM);
|
|
return;
|
|
}
|
|
|
|
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE + 1);
|
|
libusb_fill_control_setup(data, 0x40, 0x0c, 0, 0, 1);
|
|
libusb_fill_control_transfer(wrdata->transfer, ssm->dev->udev, data,
|
|
write_regs_cb, wrdata, CTRL_TIMEOUT);
|
|
wrdata->transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
|
|
|
|
wrdata->ssm = ssm;
|
|
wrdata->regs = regs;
|
|
wrdata->num_regs = num_regs;
|
|
wrdata->regs_written = 0;
|
|
write_regs_iterate(wrdata);
|
|
}
|
|
|
|
static void sm_write_reg_cb(struct libusb_transfer *transfer)
|
|
{
|
|
struct fpi_ssm *ssm = transfer->user_data;
|
|
g_free(transfer->buffer);
|
|
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
|
|
fpi_ssm_mark_aborted(ssm, -EIO);
|
|
else
|
|
fpi_ssm_next_state(ssm);
|
|
|
|
}
|
|
|
|
static void sm_write_reg(struct fpi_ssm *ssm, uint8_t reg, uint8_t value)
|
|
{
|
|
struct fp_img_dev *dev = ssm->priv;
|
|
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
|
|
unsigned char *data;
|
|
int r;
|
|
|
|
if (!transfer) {
|
|
fpi_ssm_mark_aborted(ssm, -ENOMEM);
|
|
return;
|
|
}
|
|
|
|
fp_dbg("set %02x=%02x", reg, value);
|
|
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE + 1);
|
|
libusb_fill_control_setup(data, 0x40, 0x0c, 0, reg, 1);
|
|
libusb_fill_control_transfer(transfer, dev->udev, data, sm_write_reg_cb,
|
|
ssm, CTRL_TIMEOUT);
|
|
|
|
data[LIBUSB_CONTROL_SETUP_SIZE] = value;
|
|
transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK |
|
|
LIBUSB_TRANSFER_FREE_TRANSFER;
|
|
|
|
r = libusb_submit_transfer(transfer);
|
|
if (r < 0) {
|
|
g_free(data);
|
|
libusb_free_transfer(transfer);
|
|
fpi_ssm_mark_aborted(ssm, r);
|
|
}
|
|
}
|
|
|
|
static void sm_read_reg_cb(struct libusb_transfer *transfer)
|
|
{
|
|
struct fpi_ssm *ssm = transfer->user_data;
|
|
struct fp_img_dev *dev = ssm->priv;
|
|
struct sonly_dev *sdev = dev->priv;
|
|
|
|
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
|
fpi_ssm_mark_aborted(ssm, -EIO);
|
|
} else {
|
|
sdev->read_reg_result = libusb_control_transfer_get_data(transfer)[0];
|
|
fp_dbg("read reg result = %02x", sdev->read_reg_result);
|
|
fpi_ssm_next_state(ssm);
|
|
}
|
|
|
|
g_free(transfer->buffer);
|
|
}
|
|
|
|
static void sm_read_reg(struct fpi_ssm *ssm, uint8_t reg)
|
|
{
|
|
struct fp_img_dev *dev = ssm->priv;
|
|
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
|
|
unsigned char *data;
|
|
int r;
|
|
|
|
if (!transfer) {
|
|
fpi_ssm_mark_aborted(ssm, -ENOMEM);
|
|
return;
|
|
}
|
|
|
|
fp_dbg("read reg %02x", reg);
|
|
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE + 8);
|
|
libusb_fill_control_setup(data, 0xc0, 0x0c, 0, reg, 8);
|
|
libusb_fill_control_transfer(transfer, dev->udev, data, sm_read_reg_cb,
|
|
ssm, CTRL_TIMEOUT);
|
|
transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK |
|
|
LIBUSB_TRANSFER_FREE_TRANSFER;
|
|
|
|
r = libusb_submit_transfer(transfer);
|
|
if (r < 0) {
|
|
g_free(data);
|
|
libusb_free_transfer(transfer);
|
|
fpi_ssm_mark_aborted(ssm, r);
|
|
}
|
|
}
|
|
|
|
static void sm_await_intr_cb(struct libusb_transfer *transfer)
|
|
{
|
|
struct fpi_ssm *ssm = transfer->user_data;
|
|
struct fp_img_dev *dev = ssm->priv;
|
|
|
|
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
|
g_free(transfer->buffer);
|
|
fpi_ssm_mark_aborted(ssm, transfer->status);
|
|
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);
|
|
|
|
fpi_imgdev_report_finger_status(dev, TRUE);
|
|
fpi_ssm_next_state(ssm);
|
|
}
|
|
|
|
static void sm_await_intr(struct fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = ssm->priv;
|
|
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
|
|
unsigned char *data;
|
|
int r;
|
|
|
|
if (!transfer) {
|
|
fpi_ssm_mark_aborted(ssm, -ENOMEM);
|
|
return;
|
|
}
|
|
|
|
fp_dbg("");
|
|
data = g_malloc(4);
|
|
libusb_fill_interrupt_transfer(transfer, dev->udev, 0x83, data, 4,
|
|
sm_await_intr_cb, ssm, 0);
|
|
transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK |
|
|
LIBUSB_TRANSFER_FREE_TRANSFER;
|
|
|
|
r = libusb_submit_transfer(transfer);
|
|
if (r < 0) {
|
|
libusb_free_transfer(transfer);
|
|
g_free(data);
|
|
fpi_ssm_mark_aborted(ssm, r);
|
|
}
|
|
}
|
|
|
|
/***** AWAIT FINGER *****/
|
|
|
|
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_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_2016_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_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_2016_writev_4[] = {
|
|
{ 0x08, 0x00 }, { 0x10, 0x00 }, { 0x12, 0x01 }, { 0x11, 0xbf },
|
|
{ 0x12, 0x01 }, { 0x07, 0x10 }, { 0x07, 0x10 }, { 0x04, 0x00 },\
|
|
{ 0x05, 0x00 }, { 0x0b, 0x00 },
|
|
|
|
/* enter finger detection mode */
|
|
{ 0x15, 0x20 }, { 0x30, 0xe1 }, { 0x15, 0x24 }, { 0x15, 0x04 },
|
|
{ 0x15, 0x84 },
|
|
};
|
|
|
|
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(struct fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = ssm->priv;
|
|
struct sonly_dev *sdev = dev->priv;
|
|
|
|
switch (ssm->cur_state) {
|
|
case AWFSM_2016_WRITEV_1:
|
|
sm_write_regs(ssm, awfsm_2016_writev_1, G_N_ELEMENTS(awfsm_2016_writev_1));
|
|
break;
|
|
case AWFSM_2016_READ_01:
|
|
sm_read_reg(ssm, 0x01);
|
|
break;
|
|
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_2016_WRITEV_2:
|
|
sm_write_regs(ssm, awfsm_2016_writev_2, G_N_ELEMENTS(awfsm_2016_writev_2));
|
|
break;
|
|
case AWFSM_2016_READ_13:
|
|
sm_read_reg(ssm, 0x13);
|
|
break;
|
|
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_2016_WRITEV_3:
|
|
sm_write_regs(ssm, awfsm_2016_writev_3, G_N_ELEMENTS(awfsm_2016_writev_3));
|
|
break;
|
|
case AWFSM_2016_READ_07:
|
|
sm_read_reg(ssm, 0x07);
|
|
break;
|
|
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_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_2016_writev[] = {
|
|
/* enter capture mode */
|
|
{ 0x09, 0x28 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, { 0x04, 0x00 },
|
|
{ 0x05, 0x00 },
|
|
};
|
|
|
|
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,
|
|
};
|
|
|
|
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_2016_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_2016_WRITE_15:
|
|
sm_write_reg(ssm, 0x15, 0x20);
|
|
break;
|
|
case CAPSM_2016_WRITE_30:
|
|
sm_write_reg(ssm, 0x30, 0xe0);
|
|
break;
|
|
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_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_2016_writev[] = {
|
|
/* reset + enter low power mode */
|
|
{ 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_2016_states {
|
|
DEINITSM_2016_WRITEV,
|
|
DEINITSM_2016_NUM_STATES,
|
|
};
|
|
|
|
enum deinitsm_1000_states {
|
|
DEINITSM_1000_WRITEV,
|
|
DEINITSM_1000_NUM_STATES,
|
|
};
|
|
|
|
static void deinitsm_2016_run_state(struct fpi_ssm *ssm)
|
|
{
|
|
switch (ssm->cur_state) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
/***** INITIALIZATION *****/
|
|
|
|
static const struct sonly_regwrite initsm_2016_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 },
|
|
|
|
{ 0x44, 0x01 }, { 0x43, 0x06 }, { 0x43, 0x05 }, { 0x43, 0x04 },
|
|
{ 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_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,
|
|
};
|
|
|
|
static void initsm_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 INITSM_2016_WRITEV_1:
|
|
sm_write_regs(ssm, initsm_2016_writev_1, G_N_ELEMENTS(initsm_2016_writev_1));
|
|
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;
|
|
}
|
|
}
|
|
|
|
static void initsm_1000_run_state(struct fpi_ssm *ssm)
|
|
{
|
|
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 {
|
|
LOOPSM_RUN_AWFSM,
|
|
LOOPSM_AWAIT_FINGER,
|
|
LOOPSM_RUN_CAPSM,
|
|
LOOPSM_CAPTURE,
|
|
LOOPSM_RUN_DEINITSM,
|
|
LOOPSM_FINAL,
|
|
LOOPSM_NUM_STATES,
|
|
};
|
|
|
|
static void loopsm_run_state(struct fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = ssm->priv;
|
|
struct sonly_dev *sdev = dev->priv;
|
|
|
|
switch (ssm->cur_state) {
|
|
case LOOPSM_RUN_AWFSM: ;
|
|
if (sdev->deactivating) {
|
|
fpi_ssm_mark_completed(ssm);
|
|
} else {
|
|
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);
|
|
}
|
|
break;
|
|
case LOOPSM_AWAIT_FINGER:
|
|
sm_await_intr(ssm);
|
|
break;
|
|
case LOOPSM_RUN_CAPSM: ;
|
|
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;
|
|
case LOOPSM_CAPTURE:
|
|
/* bulk URBs already flying, so just wait for image completion
|
|
* to push us into next state */
|
|
break;
|
|
case LOOPSM_RUN_DEINITSM: ;
|
|
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);
|
|
break;
|
|
case LOOPSM_FINAL:
|
|
fpi_ssm_jump_to_state(ssm, LOOPSM_RUN_AWFSM);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
/***** DRIVER STUFF *****/
|
|
|
|
static void deactivate_done(struct fp_img_dev *dev)
|
|
{
|
|
struct sonly_dev *sdev = dev->priv;
|
|
|
|
fp_dbg("");
|
|
free_img_transfers(sdev);
|
|
g_free(sdev->rowbuf);
|
|
sdev->rowbuf = NULL;
|
|
|
|
if (sdev->rows) {
|
|
g_slist_foreach(sdev->rows, (GFunc) g_free, NULL);
|
|
sdev->rows = NULL;
|
|
}
|
|
|
|
fpi_imgdev_deactivate_complete(dev);
|
|
}
|
|
|
|
static void dev_deactivate(struct fp_img_dev *dev)
|
|
{
|
|
struct sonly_dev *sdev = dev->priv;
|
|
|
|
if (!sdev->capturing) {
|
|
deactivate_done(dev);
|
|
return;
|
|
}
|
|
|
|
sdev->deactivating = TRUE;
|
|
sdev->killing_transfers = ITERATE_SSM;
|
|
sdev->kill_ssm = sdev->loopsm;
|
|
cancel_img_transfers(dev);
|
|
}
|
|
|
|
static void loopsm_complete(struct fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = ssm->priv;
|
|
struct sonly_dev *sdev = dev->priv;
|
|
int r = ssm->error;
|
|
|
|
fpi_ssm_free(ssm);
|
|
|
|
if (sdev->deactivating) {
|
|
deactivate_done(dev);
|
|
return;
|
|
}
|
|
|
|
if (r) {
|
|
fpi_imgdev_session_error(dev, r);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void initsm_complete(struct fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = ssm->priv;
|
|
struct sonly_dev *sdev = dev->priv;
|
|
int r = ssm->error;
|
|
|
|
fpi_ssm_free(ssm);
|
|
fpi_imgdev_activate_complete(dev, r);
|
|
if (r != 0)
|
|
return;
|
|
|
|
sdev->loopsm = fpi_ssm_new(dev->dev, loopsm_run_state, LOOPSM_NUM_STATES);
|
|
sdev->loopsm->priv = dev;
|
|
fpi_ssm_start(sdev->loopsm, loopsm_complete);
|
|
}
|
|
|
|
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
|
|
{
|
|
struct sonly_dev *sdev = dev->priv;
|
|
struct fpi_ssm *ssm = NULL;
|
|
int i;
|
|
|
|
sdev->deactivating = FALSE;
|
|
sdev->capturing = FALSE;
|
|
|
|
memset(sdev->img_transfer, 0,
|
|
NUM_BULK_TRANSFERS * sizeof(struct libusb_transfer *));
|
|
sdev->img_transfer_data =
|
|
g_malloc0(sizeof(struct img_transfer_data) * NUM_BULK_TRANSFERS);
|
|
sdev->num_flying = 0;
|
|
for (i = 0; i < NUM_BULK_TRANSFERS; i++) {
|
|
unsigned char *data;
|
|
sdev->img_transfer[i] = libusb_alloc_transfer(0);
|
|
if (!sdev->img_transfer[i]) {
|
|
free_img_transfers(sdev);
|
|
return -ENOMEM;
|
|
}
|
|
sdev->img_transfer_data[i].idx = i;
|
|
sdev->img_transfer_data[i].dev = dev;
|
|
data = g_malloc(4096);
|
|
libusb_fill_bulk_transfer(sdev->img_transfer[i], dev->udev, 0x81, data,
|
|
4096, img_data_cb, &sdev->img_transfer_data[i], 0);
|
|
}
|
|
|
|
switch (sdev->dev_model) {
|
|
case UPEKSONLY_2016:
|
|
ssm = fpi_ssm_new(dev->dev, initsm_2016_run_state, INITSM_2016_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;
|
|
}
|
|
|
|
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
|
{
|
|
int r;
|
|
|
|
r = libusb_set_configuration(dev->udev, 1);
|
|
if (r < 0) {
|
|
fp_err("could not set configuration 1");
|
|
return r;
|
|
}
|
|
|
|
r = libusb_claim_interface(dev->udev, 0);
|
|
if (r < 0) {
|
|
fp_err("could not claim interface 0");
|
|
return r;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void dev_deinit(struct fp_img_dev *dev)
|
|
{
|
|
g_free(dev->priv);
|
|
libusb_release_interface(dev->udev, 0);
|
|
fpi_imgdev_close_complete(dev);
|
|
}
|
|
|
|
static int dev_discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
|
|
{
|
|
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, .driver_data = UPEKSONLY_2016 },
|
|
{ .vendor = 0x147e, .product = 0x1000, .driver_data = UPEKSONLY_1000 },
|
|
{ 0, 0, 0, },
|
|
};
|
|
|
|
struct fp_img_driver upeksonly_driver = {
|
|
.driver = {
|
|
.id = UPEKSONLY_ID,
|
|
.name = FP_COMPONENT,
|
|
.full_name = "UPEK TouchStrip Sensor-Only",
|
|
.id_table = id_table,
|
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
|
.discover = dev_discover,
|
|
},
|
|
.flags = 0,
|
|
.img_width = IMG_WIDTH,
|
|
.img_height = -1,
|
|
|
|
.open = dev_init,
|
|
.close = dev_deinit,
|
|
.activate = dev_activate,
|
|
.deactivate = dev_deactivate,
|
|
};
|
|
|