4f6d908390
There is no need to use size_t for num_rows as it is capped to NUM_ROWS which is defined to 2048.
1555 lines
41 KiB
C
1555 lines
41 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,
|
|
|
|
/* report an image session error */
|
|
IMG_SESSION_ERROR,
|
|
|
|
/* iterate a SSM to the next state */
|
|
ITERATE_SSM,
|
|
};
|
|
|
|
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;
|
|
unsigned 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;
|
|
FpiSsm *kill_ssm;
|
|
|
|
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 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;
|
|
|
|
case NOT_KILLING:
|
|
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 ("%u 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: %u",
|
|
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;
|
|
}
|
|
|
|
/* NOTE: The old code assume 4096 bytes are received each time
|
|
* but there is no reason we need to enforce that. However, we
|
|
* always need full lines. */
|
|
if (transfer->actual_length % 64 != 0)
|
|
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Data packets need to be multiple of 64 bytes, got %zi bytes",
|
|
transfer->actual_length);
|
|
|
|
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 + 64 <= transfer->actual_length; i += 64)
|
|
{
|
|
if (!is_capturing (self))
|
|
return;
|
|
handle_packet (dev, transfer->buffer + i);
|
|
}
|
|
|
|
if (is_capturing (self))
|
|
{
|
|
fpi_usb_transfer_submit (fpi_usb_transfer_ref (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);
|
|
|
|
g_free (wrdata);
|
|
}
|
|
|
|
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;
|
|
transfer->buffer[0] = regwrite->value;
|
|
|
|
fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, write_regs_cb, wrdata);
|
|
}
|
|
|
|
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 (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;
|
|
transfer->buffer[0] = value;
|
|
|
|
fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
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]);
|
|
|
|
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);
|
|
}
|
|
|
|
/***** 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)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
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)
|
|
{
|
|
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 (fpi_usb_transfer_ref (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)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
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);
|
|
break;
|
|
|
|
case UPEKSONLY_1000:
|
|
awfsm = fpi_ssm_new (FP_DEVICE (dev),
|
|
awfsm_1000_run_state,
|
|
AWFSM_1000_NUM_STATES);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
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);
|
|
break;
|
|
|
|
case UPEKSONLY_1000:
|
|
capsm = fpi_ssm_new (FP_DEVICE (dev),
|
|
capsm_1000_run_state,
|
|
CAPSM_1000_NUM_STATES);
|
|
break;
|
|
|
|
case UPEKSONLY_1001:
|
|
capsm = fpi_ssm_new (FP_DEVICE (dev),
|
|
capsm_1001_run_state,
|
|
CAPSM_1001_NUM_STATES);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
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);
|
|
break;
|
|
|
|
case UPEKSONLY_1000:
|
|
deinitsm = fpi_ssm_new (FP_DEVICE (dev),
|
|
deinitsm_1000_run_state,
|
|
DEINITSM_1000_NUM_STATES);
|
|
break;
|
|
|
|
case UPEKSONLY_1001:
|
|
deinitsm = fpi_ssm_new (FP_DEVICE (dev),
|
|
deinitsm_1001_run_state,
|
|
DEINITSM_1001_NUM_STATES);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
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, GError *error)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (_dev);
|
|
|
|
|
|
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, GError *error)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (_dev);
|
|
|
|
fpi_image_device_activate_complete (dev, error);
|
|
if (error)
|
|
return;
|
|
|
|
self->loopsm = fpi_ssm_new (FP_DEVICE (dev), loopsm_run_state,
|
|
LOOPSM_NUM_STATES);
|
|
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->num_flying = 0;
|
|
self->img_transfers = g_ptr_array_new_with_free_func ((GFreeFunc) fpi_usb_transfer_unref);
|
|
|
|
/* This might seem odd, but we do need multiple in-flight URBs so that
|
|
* we never stop polling the device for more data.
|
|
*/
|
|
for (i = 0; i < NUM_BULK_TRANSFERS; 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);
|
|
break;
|
|
|
|
case UPEKSONLY_1000:
|
|
ssm = fpi_ssm_new (FP_DEVICE (dev), initsm_1000_run_state,
|
|
INITSM_1000_NUM_STATES);
|
|
break;
|
|
|
|
case UPEKSONLY_1001:
|
|
ssm = fpi_ssm_new (FP_DEVICE (dev), initsm_1001_run_state,
|
|
INITSM_1001_NUM_STATES);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
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);
|
|
}
|