d683b271d4
Unfortunately, the implementation was not thread safe and was not sticking to the thread local main context. In addition to this, it is not entirely clear to me how this API should behave. The current approach is to simply cancel the transition with the state machine halting in its current state. Instead, it could also make sense for cancellation to cause the state machine to return a G_IO_ERROR_CANCELLED. As such, simply remove the feature for now. If anyone actually has a good use-case then we can add it again.
902 lines
26 KiB
C
902 lines
26 KiB
C
/*
|
|
* Validity Sensors, Inc. VFS5011 Fingerprint Reader driver for libfprint
|
|
* Copyright (C) 2013 Arseniy Lartsev <arseniy@chalmers.se>
|
|
* AceLan Kao <acelan.kao@canonical.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 "vfs5011"
|
|
|
|
#include "drivers_api.h"
|
|
#include "vfs5011_proto.h"
|
|
|
|
/* =================== sync/async USB transfer sequence ==================== */
|
|
|
|
enum {
|
|
ACTION_SEND,
|
|
ACTION_RECEIVE,
|
|
};
|
|
|
|
struct usb_action
|
|
{
|
|
int type;
|
|
const char *name;
|
|
int endpoint;
|
|
int size;
|
|
unsigned char *data;
|
|
int correct_reply_size;
|
|
};
|
|
|
|
#define SEND(ENDPOINT, COMMAND) \
|
|
{ \
|
|
.type = ACTION_SEND, \
|
|
.endpoint = ENDPOINT, \
|
|
.name = #COMMAND, \
|
|
.size = sizeof (COMMAND), \
|
|
.data = COMMAND \
|
|
},
|
|
|
|
#define RECV(ENDPOINT, SIZE) \
|
|
{ \
|
|
.type = ACTION_RECEIVE, \
|
|
.endpoint = ENDPOINT, \
|
|
.size = SIZE, \
|
|
.data = NULL \
|
|
},
|
|
|
|
#define RECV_CHECK(ENDPOINT, SIZE, EXPECTED) \
|
|
{ \
|
|
.type = ACTION_RECEIVE, \
|
|
.endpoint = ENDPOINT, \
|
|
.size = SIZE, \
|
|
.data = EXPECTED, \
|
|
.correct_reply_size = sizeof (EXPECTED) \
|
|
},
|
|
|
|
struct usbexchange_data
|
|
{
|
|
int stepcount;
|
|
FpImageDevice *device;
|
|
struct usb_action *actions;
|
|
void *receive_buf;
|
|
int timeout;
|
|
};
|
|
|
|
static void start_scan (FpImageDevice *dev);
|
|
|
|
static void
|
|
async_send_cb (FpiUsbTransfer *transfer, FpDevice *device,
|
|
gpointer user_data, GError *error)
|
|
{
|
|
struct usbexchange_data *data = fpi_ssm_get_data (transfer->ssm);
|
|
struct usb_action *action;
|
|
|
|
g_assert (!(fpi_ssm_get_cur_state (transfer->ssm) >= data->stepcount));
|
|
|
|
action = &data->actions[fpi_ssm_get_cur_state (transfer->ssm)];
|
|
g_assert (!(action->type != ACTION_SEND));
|
|
|
|
if (error)
|
|
{
|
|
/* Transfer not completed, return IO error */
|
|
fpi_ssm_mark_failed (transfer->ssm, error);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
fpi_ssm_next_state (transfer->ssm);
|
|
}
|
|
|
|
static void
|
|
async_recv_cb (FpiUsbTransfer *transfer, FpDevice *device,
|
|
gpointer user_data, GError *error)
|
|
{
|
|
struct usbexchange_data *data = fpi_ssm_get_data (transfer->ssm);
|
|
struct usb_action *action;
|
|
|
|
if (error)
|
|
{
|
|
/* Transfer not completed, return IO error */
|
|
fpi_ssm_mark_failed (transfer->ssm, error);
|
|
return;
|
|
}
|
|
|
|
g_assert (!(fpi_ssm_get_cur_state (transfer->ssm) >= data->stepcount));
|
|
|
|
action = &data->actions[fpi_ssm_get_cur_state (transfer->ssm)];
|
|
g_assert (!(action->type != ACTION_RECEIVE));
|
|
|
|
if (action->data != NULL)
|
|
{
|
|
if (transfer->actual_length != action->correct_reply_size)
|
|
{
|
|
fp_err ("Got %d bytes instead of %d",
|
|
(gint) transfer->actual_length,
|
|
action->correct_reply_size);
|
|
fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
|
|
return;
|
|
}
|
|
if (memcmp (transfer->buffer, action->data,
|
|
action->correct_reply_size) != 0)
|
|
{
|
|
fp_dbg ("Wrong reply:");
|
|
fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fp_dbg ("Got %d bytes out of %d",
|
|
(gint) transfer->actual_length,
|
|
(gint) transfer->length);
|
|
}
|
|
|
|
fpi_ssm_next_state (transfer->ssm);
|
|
}
|
|
|
|
static void
|
|
usbexchange_loop (FpiSsm *ssm, FpDevice *_dev)
|
|
{
|
|
struct usbexchange_data *data = fpi_ssm_get_data (ssm);
|
|
struct usb_action *action = &data->actions[fpi_ssm_get_cur_state (ssm)];
|
|
FpiUsbTransfer *transfer;
|
|
|
|
g_assert (fpi_ssm_get_cur_state (ssm) < data->stepcount);
|
|
|
|
switch (action->type)
|
|
{
|
|
case ACTION_SEND:
|
|
fp_dbg ("Sending %s", action->name);
|
|
transfer = fpi_usb_transfer_new (_dev);
|
|
fpi_usb_transfer_fill_bulk_full (transfer, action->endpoint,
|
|
action->data, action->size,
|
|
NULL);
|
|
transfer->ssm = ssm;
|
|
transfer->short_is_error = TRUE;
|
|
fpi_usb_transfer_submit (transfer, data->timeout, NULL,
|
|
async_send_cb, NULL);
|
|
break;
|
|
|
|
case ACTION_RECEIVE:
|
|
fp_dbg ("Receiving %d bytes", action->size);
|
|
transfer = fpi_usb_transfer_new (_dev);
|
|
fpi_usb_transfer_fill_bulk_full (transfer, action->endpoint,
|
|
data->receive_buf,
|
|
action->size, NULL);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, data->timeout, NULL,
|
|
async_recv_cb, NULL);
|
|
break;
|
|
|
|
default:
|
|
fp_err ("Bug detected: invalid action %d", action->type);
|
|
fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
usb_exchange_async (FpiSsm *ssm,
|
|
struct usbexchange_data *data,
|
|
const char *exchange_name)
|
|
{
|
|
FpiSsm *subsm = fpi_ssm_new_full (FP_DEVICE (data->device),
|
|
usbexchange_loop,
|
|
data->stepcount,
|
|
data->stepcount,
|
|
exchange_name);
|
|
|
|
fpi_ssm_set_data (subsm, data, NULL);
|
|
fpi_ssm_start_subsm (ssm, subsm);
|
|
}
|
|
|
|
/* ====================== utils ======================= */
|
|
|
|
/* Calculade squared standand deviation of sum of two lines */
|
|
static int
|
|
vfs5011_get_deviation2 (struct fpi_line_asmbl_ctx *ctx, GSList *row1, GSList *row2)
|
|
{
|
|
unsigned char *buf1, *buf2;
|
|
int res = 0, mean = 0, i;
|
|
const int size = 64;
|
|
|
|
buf1 = (unsigned char *) row1->data + 56;
|
|
buf2 = (unsigned char *) row2->data + 168;
|
|
|
|
for (i = 0; i < size; i++)
|
|
mean += (int) buf1[i] + (int) buf2[i];
|
|
|
|
mean /= size;
|
|
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
int dev = (int) buf1[i] + (int) buf2[i] - mean;
|
|
res += dev * dev;
|
|
}
|
|
|
|
return res / size;
|
|
}
|
|
|
|
static unsigned char
|
|
vfs5011_get_pixel (struct fpi_line_asmbl_ctx *ctx,
|
|
GSList *row,
|
|
unsigned x)
|
|
{
|
|
unsigned char *data = (unsigned char *) row->data + 8;
|
|
|
|
return data[x];
|
|
}
|
|
|
|
/* ====================== main stuff ======================= */
|
|
|
|
enum {
|
|
CAPTURE_LINES = 256,
|
|
MAXLINES = 2000,
|
|
MAX_CAPTURE_LINES = 100000,
|
|
};
|
|
|
|
static struct fpi_line_asmbl_ctx assembling_ctx = {
|
|
.line_width = VFS5011_IMAGE_WIDTH,
|
|
.max_height = MAXLINES,
|
|
.resolution = 10,
|
|
.median_filter_size = 25,
|
|
.max_search_offset = 30,
|
|
.get_deviation = vfs5011_get_deviation2,
|
|
.get_pixel = vfs5011_get_pixel,
|
|
};
|
|
|
|
struct _FpDeviceVfs5011
|
|
{
|
|
FpImageDevice parent;
|
|
|
|
unsigned char *total_buffer;
|
|
unsigned char *capture_buffer;
|
|
unsigned char *row_buffer;
|
|
unsigned char *lastline;
|
|
GSList *rows;
|
|
int lines_captured, lines_recorded, empty_lines;
|
|
int max_lines_captured, max_lines_recorded;
|
|
int lines_total, lines_total_allocated;
|
|
gboolean loop_running;
|
|
gboolean deactivating;
|
|
struct usbexchange_data init_sequence;
|
|
};
|
|
|
|
G_DECLARE_FINAL_TYPE (FpDeviceVfs5011, fpi_device_vfs5011, FPI, DEVICE_VFS5011,
|
|
FpImageDevice);
|
|
G_DEFINE_TYPE (FpDeviceVfs5011, fpi_device_vfs5011, FP_TYPE_IMAGE_DEVICE);
|
|
|
|
enum {
|
|
DEV_ACTIVATE_REQUEST_FPRINT,
|
|
DEV_ACTIVATE_INIT_COMPLETE,
|
|
DEV_ACTIVATE_READ_DATA,
|
|
DEV_ACTIVATE_DATA_COMPLETE,
|
|
DEV_ACTIVATE_PREPARE_NEXT_CAPTURE,
|
|
DEV_ACTIVATE_NUM_STATES
|
|
};
|
|
|
|
enum {
|
|
DEV_OPEN_START,
|
|
DEV_OPEN_NUM_STATES
|
|
};
|
|
|
|
static void
|
|
capture_init (FpDeviceVfs5011 *self, int max_captured,
|
|
int max_recorded)
|
|
{
|
|
fp_dbg ("capture_init");
|
|
self->lastline = NULL;
|
|
self->lines_captured = 0;
|
|
self->lines_recorded = 0;
|
|
self->empty_lines = 0;
|
|
self->lines_total = 0;
|
|
self->lines_total_allocated = 0;
|
|
self->total_buffer = NULL;
|
|
self->max_lines_captured = max_captured;
|
|
self->max_lines_recorded = max_recorded;
|
|
}
|
|
|
|
static int
|
|
process_chunk (FpDeviceVfs5011 *self, int transferred)
|
|
{
|
|
enum {
|
|
DEVIATION_THRESHOLD = 15 * 15,
|
|
DIFFERENCE_THRESHOLD = 600,
|
|
STOP_CHECK_LINES = 50
|
|
};
|
|
|
|
fp_dbg ("process_chunk: got %d bytes", transferred);
|
|
int lines_captured = transferred / VFS5011_LINE_SIZE;
|
|
int i;
|
|
|
|
for (i = 0; i < lines_captured; i++)
|
|
{
|
|
unsigned char *linebuf = self->capture_buffer
|
|
+ i * VFS5011_LINE_SIZE;
|
|
|
|
if (fpi_std_sq_dev (linebuf + 8, VFS5011_IMAGE_WIDTH)
|
|
< DEVIATION_THRESHOLD)
|
|
{
|
|
if (self->lines_captured == 0)
|
|
continue;
|
|
else
|
|
self->empty_lines++;
|
|
}
|
|
else
|
|
{
|
|
self->empty_lines = 0;
|
|
}
|
|
if (self->empty_lines >= STOP_CHECK_LINES)
|
|
{
|
|
fp_dbg ("process_chunk: got %d empty lines, finishing",
|
|
self->empty_lines);
|
|
return 1;
|
|
}
|
|
|
|
self->lines_captured++;
|
|
if (self->lines_captured > self->max_lines_captured)
|
|
{
|
|
fp_dbg ("process_chunk: captured %d lines, finishing",
|
|
self->lines_captured);
|
|
return 1;
|
|
}
|
|
|
|
if ((self->lastline == NULL) ||
|
|
(fpi_mean_sq_diff_norm (self->lastline + 8,
|
|
linebuf + 8,
|
|
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD))
|
|
{
|
|
self->lastline = g_malloc (VFS5011_LINE_SIZE);
|
|
self->rows = g_slist_prepend (self->rows,
|
|
self->lastline);
|
|
memmove (self->lastline, linebuf, VFS5011_LINE_SIZE);
|
|
self->lines_recorded++;
|
|
if (self->lines_recorded >= self->max_lines_recorded)
|
|
{
|
|
fp_dbg ("process_chunk: recorded %d lines, finishing",
|
|
self->lines_recorded);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
submit_image (FpiSsm *ssm,
|
|
FpDeviceVfs5011 *self,
|
|
FpImageDevice *dev)
|
|
{
|
|
FpImage *img;
|
|
|
|
if (self->lines_recorded < VFS5011_IMAGE_WIDTH)
|
|
{
|
|
fpi_image_device_retry_scan (dev, FP_DEVICE_RETRY_TOO_SHORT);
|
|
return;
|
|
}
|
|
|
|
g_assert (self->rows != NULL);
|
|
|
|
self->rows = g_slist_reverse (self->rows);
|
|
|
|
img = fpi_assemble_lines (&assembling_ctx, self->rows,
|
|
self->lines_recorded);
|
|
|
|
g_slist_free_full (self->rows, g_free);
|
|
self->rows = NULL;
|
|
|
|
fp_dbg ("Image captured, committing");
|
|
|
|
fpi_image_device_image_captured (dev, img);
|
|
}
|
|
|
|
static void
|
|
chunk_capture_callback (FpiUsbTransfer *transfer, FpDevice *device,
|
|
gpointer user_data, GError *error)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (device);
|
|
FpDeviceVfs5011 *self;
|
|
|
|
self = FPI_DEVICE_VFS5011 (dev);
|
|
|
|
if (!error ||
|
|
g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT))
|
|
{
|
|
if (error)
|
|
g_error_free (error);
|
|
|
|
if (transfer->actual_length > 0)
|
|
fpi_image_device_report_finger_status (dev, TRUE);
|
|
|
|
if (process_chunk (self, transfer->actual_length))
|
|
fpi_ssm_jump_to_state (transfer->ssm,
|
|
DEV_ACTIVATE_DATA_COMPLETE);
|
|
else
|
|
fpi_ssm_jump_to_state (transfer->ssm,
|
|
DEV_ACTIVATE_READ_DATA);
|
|
}
|
|
else
|
|
{
|
|
if (!self->deactivating)
|
|
{
|
|
fp_err ("Failed to capture data");
|
|
fpi_ssm_mark_failed (transfer->ssm, error);
|
|
}
|
|
else
|
|
{
|
|
g_error_free (error);
|
|
fpi_ssm_mark_completed (transfer->ssm);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
capture_chunk_async (FpDeviceVfs5011 *self,
|
|
GUsbDevice *handle, int nline,
|
|
int timeout, FpiSsm *ssm)
|
|
{
|
|
FpiUsbTransfer *transfer;
|
|
|
|
fp_dbg ("capture_chunk_async: capture %d lines, already have %d",
|
|
nline, self->lines_recorded);
|
|
enum {
|
|
DEVIATION_THRESHOLD = 15 * 15,
|
|
DIFFERENCE_THRESHOLD = 600,
|
|
STOP_CHECK_LINES = 50
|
|
};
|
|
|
|
transfer = fpi_usb_transfer_new (FP_DEVICE (self));
|
|
fpi_usb_transfer_fill_bulk_full (transfer,
|
|
VFS5011_IN_ENDPOINT_DATA,
|
|
self->capture_buffer,
|
|
nline * VFS5011_LINE_SIZE, NULL);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, timeout, fpi_device_get_cancellable (FP_DEVICE (self)),
|
|
chunk_capture_callback, NULL);
|
|
}
|
|
|
|
/*
|
|
* Device initialization. Windows driver only does it when the device is
|
|
* plugged in, but it doesn't harm to do this every time before scanning the
|
|
* image.
|
|
*/
|
|
struct usb_action vfs5011_initialization[] = {
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_01)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_19)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64) /* B5C457F9 */
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_00)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64) /* 0000FFFFFFFF */
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_01)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64) /* 0000FFFFFFFFFF */
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_02)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_01)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_03)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_04)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 256)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 64)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_05)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_01)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_06)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 17216)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 32)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_07)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 45056)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_08)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 16896)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_09)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 4928)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_10)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 5632)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_11)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 5632)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_12)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 3328)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 64)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_13)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_03)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_14)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 4800)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_02)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_27)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_15)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_16)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 2368)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 4800)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_17)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_init_18)
|
|
/* 0000 */
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
/*
|
|
* Windows driver does this and it works
|
|
* But in this driver this call never returns...
|
|
* RECV(VFS5011_IN_ENDPOINT_CTRL2, 8) //00D3054000
|
|
*/
|
|
};
|
|
|
|
/* Initiate recording the image */
|
|
struct usb_action vfs5011_initiate_capture[] = {
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_04)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 64)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 84032)
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_prepare_00)
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_prepare_01)
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_prepare_02)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 2368)
|
|
RECV (VFS5011_IN_ENDPOINT_CTRL, 64)
|
|
RECV (VFS5011_IN_ENDPOINT_DATA, 4800)
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_prepare_03)
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
|
|
/*
|
|
* Windows driver does this and it works
|
|
* But in this driver this call never returns...
|
|
* RECV(VFS5011_IN_ENDPOINT_CTRL2, 8);
|
|
*/
|
|
|
|
SEND (VFS5011_OUT_ENDPOINT, vfs5011_prepare_04)
|
|
RECV_CHECK (VFS5011_IN_ENDPOINT_CTRL, 2368, VFS5011_NORMAL_CONTROL_REPLY)
|
|
|
|
/*
|
|
* Windows driver does this and it works
|
|
* But in this driver this call never returns...
|
|
* RECV(VFS5011_IN_ENDPOINT_CTRL2, 8);
|
|
*/
|
|
};
|
|
|
|
/* ====================== lifprint interface ======================= */
|
|
|
|
static void
|
|
activate_loop (FpiSsm *ssm, FpDevice *_dev)
|
|
{
|
|
enum {READ_TIMEOUT = 0};
|
|
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
FpDeviceVfs5011 *self;
|
|
|
|
self = FPI_DEVICE_VFS5011 (_dev);
|
|
|
|
fp_dbg ("main_loop: state %d", fpi_ssm_get_cur_state (ssm));
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fp_dbg ("deactivating, marking completed");
|
|
fpi_ssm_mark_completed (ssm);
|
|
return;
|
|
}
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case DEV_ACTIVATE_REQUEST_FPRINT:
|
|
self->init_sequence.stepcount =
|
|
G_N_ELEMENTS (vfs5011_initiate_capture);
|
|
self->init_sequence.actions = vfs5011_initiate_capture;
|
|
self->init_sequence.device = dev;
|
|
if (self->init_sequence.receive_buf == NULL)
|
|
self->init_sequence.receive_buf =
|
|
g_malloc0 (VFS5011_RECEIVE_BUF_SIZE);
|
|
self->init_sequence.timeout = 1000;
|
|
usb_exchange_async (ssm, &self->init_sequence, "ACTIVATE REQUEST");
|
|
break;
|
|
|
|
case DEV_ACTIVATE_INIT_COMPLETE:
|
|
if (self->init_sequence.receive_buf != NULL)
|
|
g_free (self->init_sequence.receive_buf);
|
|
self->init_sequence.receive_buf = NULL;
|
|
capture_init (self, MAX_CAPTURE_LINES, MAXLINES);
|
|
fpi_image_device_activate_complete (dev, NULL);
|
|
fpi_ssm_next_state (ssm);
|
|
break;
|
|
|
|
case DEV_ACTIVATE_READ_DATA:
|
|
capture_chunk_async (self,
|
|
fpi_device_get_usb_device (FP_DEVICE (dev)),
|
|
CAPTURE_LINES,
|
|
READ_TIMEOUT, ssm);
|
|
break;
|
|
|
|
case DEV_ACTIVATE_DATA_COMPLETE:
|
|
fpi_ssm_next_state_delayed (ssm, 1);
|
|
break;
|
|
|
|
case DEV_ACTIVATE_PREPARE_NEXT_CAPTURE:
|
|
self->init_sequence.stepcount =
|
|
G_N_ELEMENTS (vfs5011_initiate_capture);
|
|
self->init_sequence.actions = vfs5011_initiate_capture;
|
|
self->init_sequence.device = dev;
|
|
if (self->init_sequence.receive_buf == NULL)
|
|
self->init_sequence.receive_buf =
|
|
g_malloc0 (VFS5011_RECEIVE_BUF_SIZE);
|
|
self->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT;
|
|
usb_exchange_async (ssm, &self->init_sequence, "PREPARE CAPTURE");
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
static void
|
|
activate_loop_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
FpDeviceVfs5011 *self;
|
|
|
|
self = FPI_DEVICE_VFS5011 (_dev);
|
|
|
|
fp_dbg ("finishing");
|
|
if (self->init_sequence.receive_buf != NULL)
|
|
g_free (self->init_sequence.receive_buf);
|
|
self->init_sequence.receive_buf = NULL;
|
|
if (!self->deactivating && !error)
|
|
{
|
|
submit_image (ssm, self, dev);
|
|
fpi_image_device_report_finger_status (dev, FALSE);
|
|
}
|
|
|
|
self->loop_running = FALSE;
|
|
|
|
if (self->deactivating)
|
|
fpi_image_device_deactivate_complete (dev, error);
|
|
else if (error)
|
|
fpi_image_device_session_error (dev, error);
|
|
else
|
|
start_scan (dev);
|
|
}
|
|
|
|
|
|
static void
|
|
open_loop (FpiSsm *ssm, FpDevice *_dev)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
FpDeviceVfs5011 *self;
|
|
|
|
self = FPI_DEVICE_VFS5011 (_dev);
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case DEV_OPEN_START:
|
|
self->init_sequence.stepcount =
|
|
G_N_ELEMENTS (vfs5011_initialization);
|
|
self->init_sequence.actions = vfs5011_initialization;
|
|
self->init_sequence.device = dev;
|
|
self->init_sequence.receive_buf =
|
|
g_malloc0 (VFS5011_RECEIVE_BUF_SIZE);
|
|
self->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT;
|
|
usb_exchange_async (ssm, &self->init_sequence, "DEVICE OPEN");
|
|
break;
|
|
}
|
|
;
|
|
}
|
|
|
|
static void
|
|
open_loop_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
|
|
{
|
|
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
|
|
FpDeviceVfs5011 *self;
|
|
|
|
self = FPI_DEVICE_VFS5011 (_dev);
|
|
g_free (self->init_sequence.receive_buf);
|
|
self->init_sequence.receive_buf = NULL;
|
|
|
|
fpi_image_device_open_complete (dev, error);
|
|
}
|
|
|
|
static void
|
|
dev_open (FpImageDevice *dev)
|
|
{
|
|
FpiSsm *ssm;
|
|
GError *error = NULL;
|
|
FpDeviceVfs5011 *self;
|
|
|
|
self = FPI_DEVICE_VFS5011 (dev);
|
|
self->capture_buffer = g_new0 (unsigned char, CAPTURE_LINES * VFS5011_LINE_SIZE);
|
|
|
|
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;
|
|
}
|
|
|
|
ssm = fpi_ssm_new (FP_DEVICE (dev), open_loop, DEV_OPEN_NUM_STATES);
|
|
fpi_ssm_start (ssm, open_loop_complete);
|
|
}
|
|
|
|
static void
|
|
dev_close (FpImageDevice *dev)
|
|
{
|
|
GError *error = NULL;
|
|
FpDeviceVfs5011 *self = FPI_DEVICE_VFS5011 (dev);
|
|
|
|
g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (dev)),
|
|
0, 0, &error);
|
|
|
|
g_free (self->capture_buffer);
|
|
g_slist_free_full (g_steal_pointer (&self->rows), g_free);
|
|
|
|
fpi_image_device_close_complete (dev, error);
|
|
}
|
|
|
|
static void
|
|
start_scan (FpImageDevice *dev)
|
|
{
|
|
FpDeviceVfs5011 *self;
|
|
FpiSsm *ssm;
|
|
|
|
self = FPI_DEVICE_VFS5011 (dev);
|
|
self->loop_running = TRUE;
|
|
fp_dbg ("creating ssm");
|
|
ssm = fpi_ssm_new (FP_DEVICE (dev), activate_loop, DEV_ACTIVATE_NUM_STATES);
|
|
fp_dbg ("starting ssm");
|
|
fpi_ssm_start (ssm, activate_loop_complete);
|
|
fp_dbg ("ssm done, getting out");
|
|
}
|
|
|
|
static void
|
|
dev_activate (FpImageDevice *dev)
|
|
{
|
|
FpDeviceVfs5011 *self;
|
|
|
|
self = FPI_DEVICE_VFS5011 (dev);
|
|
fp_dbg ("device initialized");
|
|
self->deactivating = FALSE;
|
|
|
|
start_scan (dev);
|
|
}
|
|
|
|
static void
|
|
dev_deactivate (FpImageDevice *dev)
|
|
{
|
|
FpDeviceVfs5011 *self;
|
|
|
|
self = FPI_DEVICE_VFS5011 (dev);
|
|
if (self->loop_running)
|
|
self->deactivating = TRUE;
|
|
else
|
|
fpi_image_device_deactivate_complete (dev, NULL);
|
|
}
|
|
|
|
static const FpIdEntry id_table[] = {
|
|
{ /* Validity device from some Toshiba laptops */ .vid = 0x138a, .pid = 0x0010, },
|
|
{ /* vfs5011 */ .vid = 0x138a, .pid = 0x0011, },
|
|
{ /* Validity device from Lenovo Preferred Pro USB Fingerprint Keyboard KUF1256 */ .vid = 0x138a, .pid = 0x0015, },
|
|
{ /* Validity device from Lenovo T440 laptops */ .vid = 0x138a, .pid = 0x0017, },
|
|
{ /* one more Validity device */ .vid = 0x138a, .pid = 0x0018, },
|
|
{ .vid = 0, .pid = 0, .driver_data = 0 },
|
|
};
|
|
|
|
static void
|
|
fpi_device_vfs5011_init (FpDeviceVfs5011 *self)
|
|
{
|
|
}
|
|
static void
|
|
fpi_device_vfs5011_class_init (FpDeviceVfs5011Class *klass)
|
|
{
|
|
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
|
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
|
|
|
|
dev_class->id = "vfs5011";
|
|
dev_class->full_name = "Validity VFS5011";
|
|
dev_class->type = FP_DEVICE_TYPE_USB;
|
|
dev_class->id_table = id_table;
|
|
dev_class->scan_type = FP_SCAN_TYPE_SWIPE;
|
|
|
|
img_class->img_open = dev_open;
|
|
img_class->img_close = dev_close;
|
|
img_class->activate = dev_activate;
|
|
img_class->deactivate = dev_deactivate;
|
|
|
|
img_class->bz3_threshold = 20;
|
|
|
|
img_class->img_width = VFS5011_IMAGE_WIDTH;
|
|
img_class->img_height = -1;
|
|
}
|