libfprint/libfprint/drivers/vfs5011.c
Benjamin Berg 4cec28416e lib: Remove state from fp_img_driver activate handler
The state was always AWAIT_FINGER and it was never used by any driver
(except for error checking). So remove it, in particular as a correct
state change will be done after activation anyway.

The only driver with code that actually did anything based on this was
the URU4000 driver. However, all it did was an explicit state change
execution. This is not necessary, as the state_change handler is called
anyway (i.e. we now only write the AWAIT_FINGER register once rather
than twice).

Manual changes plus:

@ init @
identifier driver_name;
identifier activate_func;
@@
struct fp_img_driver driver_name = {
    ...,
    .activate = activate_func,
    ...,
};
@ remove_arg @
identifier dev;
identifier state;
identifier init.activate_func;
@@
activate_func (
	struct fp_img_dev *dev
-	, enum fp_imgdev_state state
	)
{
	<...
-	if (state != IMGDEV_STATE_AWAIT_FINGER_ON) { ... }
	...>
}
2019-06-18 18:19:38 +02:00

908 lines
24 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
*/
#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;
struct fp_img_dev *device;
struct usb_action *actions;
void *receive_buf;
int timeout;
};
static void start_scan(struct fp_img_dev *dev);
static void async_send_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct usbexchange_data *data = fpi_ssm_get_user_data(ssm);
struct usb_action *action;
if (fpi_ssm_get_cur_state(ssm) >= data->stepcount) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
goto out;
}
action = &data->actions[fpi_ssm_get_cur_state(ssm)];
if (action->type != ACTION_SEND) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
goto out;
}
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
/* Transfer not completed, return IO error */
fp_err("transfer not completed, status = %d", transfer->status);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
if (transfer->length != transfer->actual_length) {
/* Data sended mismatch with expected, return protocol error */
fp_err("length mismatch, got %d, expected %d",
transfer->actual_length, transfer->length);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
/* success */
fpi_ssm_next_state(ssm);
out:
libusb_free_transfer(transfer);
}
static void async_recv_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct usbexchange_data *data = fpi_ssm_get_user_data(ssm);
struct usb_action *action;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
/* Transfer not completed, return IO error */
fp_err("transfer not completed, status = %d", transfer->status);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
if (fpi_ssm_get_cur_state(ssm) >= data->stepcount) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
goto out;
}
action = &data->actions[fpi_ssm_get_cur_state(ssm)];
if (action->type != ACTION_RECEIVE) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
goto out;
}
if (action->data != NULL) {
if (transfer->actual_length != action->correct_reply_size) {
fp_err("Got %d bytes instead of %d",
transfer->actual_length,
action->correct_reply_size);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
if (memcmp(transfer->buffer, action->data,
action->correct_reply_size) != 0) {
fp_dbg("Wrong reply:");
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
} else
fp_dbg("Got %d bytes out of %d", transfer->actual_length,
transfer->length);
fpi_ssm_next_state(ssm);
out:
libusb_free_transfer(transfer);
}
static void usbexchange_loop(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct usbexchange_data *data = user_data;
if (fpi_ssm_get_cur_state(ssm) >= data->stepcount) {
fp_err("Bug detected: state %d out of range, only %d steps",
fpi_ssm_get_cur_state(ssm), data->stepcount);
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
return;
}
struct usb_action *action = &data->actions[fpi_ssm_get_cur_state(ssm)];
struct libusb_transfer *transfer;
int ret = -EINVAL;
switch (action->type) {
case ACTION_SEND:
fp_dbg("Sending %s", action->name);
transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(data->device)),
action->endpoint, action->data,
action->size, async_send_cb, ssm,
data->timeout);
ret = libusb_submit_transfer(transfer);
break;
case ACTION_RECEIVE:
fp_dbg("Receiving %d bytes", action->size);
transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(data->device)),
action->endpoint, data->receive_buf,
action->size, async_recv_cb, ssm,
data->timeout);
ret = libusb_submit_transfer(transfer);
break;
default:
fp_err("Bug detected: invalid action %d", action->type);
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
return;
}
if (ret != 0) {
fp_err("USB transfer error: %s", strerror(ret));
fpi_imgdev_session_error(data->device, ret);
fpi_ssm_mark_failed(ssm, ret);
}
}
static void usb_exchange_async(fpi_ssm *ssm,
struct usbexchange_data *data)
{
fpi_ssm *subsm = fpi_ssm_new(FP_DEV(data->device),
usbexchange_loop,
data->stepcount,
data);
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 = row1->data + 56;
buf2 = 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 = 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 vfs5011_data {
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;
struct libusb_transfer *flying_transfer;
};
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(struct vfs5011_data *data, int max_captured,
int max_recorded)
{
fp_dbg("capture_init");
data->lastline = NULL;
data->lines_captured = 0;
data->lines_recorded = 0;
data->empty_lines = 0;
data->lines_total = 0;
data->lines_total_allocated = 0;
data->total_buffer = NULL;
data->max_lines_captured = max_captured;
data->max_lines_recorded = max_recorded;
}
static int process_chunk(struct vfs5011_data *data, 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 = data->capture_buffer
+ i * VFS5011_LINE_SIZE;
if (fpi_std_sq_dev(linebuf + 8, VFS5011_IMAGE_WIDTH)
< DEVIATION_THRESHOLD) {
if (data->lines_captured == 0)
continue;
else
data->empty_lines++;
} else
data->empty_lines = 0;
if (data->empty_lines >= STOP_CHECK_LINES) {
fp_dbg("process_chunk: got %d empty lines, finishing",
data->empty_lines);
return 1;
}
data->lines_captured++;
if (data->lines_captured > data->max_lines_captured) {
fp_dbg("process_chunk: captured %d lines, finishing",
data->lines_captured);
return 1;
}
if ((data->lastline == NULL)
|| (fpi_mean_sq_diff_norm(
data->lastline + 8,
linebuf + 8,
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) {
data->lastline = g_malloc(VFS5011_LINE_SIZE);
data->rows = g_slist_prepend(data->rows, data->lastline);
g_memmove(data->lastline, linebuf, VFS5011_LINE_SIZE);
data->lines_recorded++;
if (data->lines_recorded >= data->max_lines_recorded) {
fp_dbg("process_chunk: recorded %d lines, finishing",
data->lines_recorded);
return 1;
}
}
}
return 0;
}
static void
submit_image(fpi_ssm *ssm,
struct vfs5011_data *data,
struct fp_img_dev *dev)
{
struct fp_img *img;
if (data->lines_recorded == 0) {
/* == FP_ENROLL_RETRY_TOO_SHORT */
fpi_imgdev_session_error(dev, FP_VERIFY_RETRY_TOO_SHORT);
return;
}
g_assert (data->rows != NULL);
data->rows = g_slist_reverse(data->rows);
img = fpi_assemble_lines(&assembling_ctx, data->rows, data->lines_recorded);
g_slist_free_full(data->rows, g_free);
data->rows = NULL;
fp_dbg("Image captured, commiting");
fpi_imgdev_image_captured(dev, img);
}
static void chunk_capture_callback(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = (fpi_ssm *)transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(FP_DEV(dev));
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) ||
(transfer->status == LIBUSB_TRANSFER_TIMED_OUT)) {
if (transfer->actual_length > 0)
fpi_imgdev_report_finger_status(dev, TRUE);
if (process_chunk(data, transfer->actual_length))
fpi_ssm_jump_to_state(ssm, DEV_ACTIVATE_DATA_COMPLETE);
else
fpi_ssm_jump_to_state(ssm, DEV_ACTIVATE_READ_DATA);
} else {
if (!data->deactivating) {
fp_err("Failed to capture data");
fpi_ssm_mark_failed(ssm, -1);
} else {
fpi_ssm_mark_completed(ssm);
}
}
libusb_free_transfer(transfer);
data->flying_transfer = NULL;
}
static int capture_chunk_async(struct vfs5011_data *data,
libusb_device_handle *handle, int nline,
int timeout, fpi_ssm *ssm)
{
fp_dbg("capture_chunk_async: capture %d lines, already have %d",
nline, data->lines_recorded);
enum {
DEVIATION_THRESHOLD = 15*15,
DIFFERENCE_THRESHOLD = 600,
STOP_CHECK_LINES = 50
};
data->flying_transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(data->flying_transfer, handle, VFS5011_IN_ENDPOINT_DATA,
data->capture_buffer,
nline * VFS5011_LINE_SIZE,
chunk_capture_callback, ssm, timeout);
return libusb_submit_transfer(data->flying_transfer);
}
/*
* 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(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
enum {READ_TIMEOUT = 0};
struct fp_img_dev *dev = user_data;
struct vfs5011_data *data;
int r;
fpi_timeout *timeout;
data = FP_INSTANCE_DATA(_dev);
fp_dbg("main_loop: state %d", fpi_ssm_get_cur_state(ssm));
if (data->deactivating) {
fp_dbg("deactivating, marking completed");
fpi_ssm_mark_completed(ssm);
return;
}
switch (fpi_ssm_get_cur_state(ssm)) {
case DEV_ACTIVATE_REQUEST_FPRINT:
data->init_sequence.stepcount =
G_N_ELEMENTS(vfs5011_initiate_capture);
data->init_sequence.actions = vfs5011_initiate_capture;
data->init_sequence.device = dev;
if (data->init_sequence.receive_buf == NULL)
data->init_sequence.receive_buf =
g_malloc0(VFS5011_RECEIVE_BUF_SIZE);
data->init_sequence.timeout = 1000;
usb_exchange_async(ssm, &data->init_sequence);
break;
case DEV_ACTIVATE_INIT_COMPLETE:
if (data->init_sequence.receive_buf != NULL)
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
capture_init(data, MAX_CAPTURE_LINES, MAXLINES);
fpi_imgdev_activate_complete(dev, 0);
fpi_ssm_next_state(ssm);
break;
case DEV_ACTIVATE_READ_DATA:
r = capture_chunk_async(data, fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_LINES,
READ_TIMEOUT, ssm);
if (r != 0) {
fp_err("Failed to capture data");
fpi_imgdev_session_error(dev, r);
fpi_ssm_mark_failed(ssm, r);
}
break;
case DEV_ACTIVATE_DATA_COMPLETE:
timeout = fpi_timeout_add(1, fpi_ssm_next_state_timeout_cb, _dev, ssm);
if (timeout == NULL) {
/* Failed to add timeout */
fp_err("failed to add timeout");
fpi_imgdev_session_error(dev, -1);
fpi_ssm_mark_failed(ssm, -1);
}
break;
case DEV_ACTIVATE_PREPARE_NEXT_CAPTURE:
data->init_sequence.stepcount =
G_N_ELEMENTS(vfs5011_initiate_capture);
data->init_sequence.actions = vfs5011_initiate_capture;
data->init_sequence.device = dev;
if (data->init_sequence.receive_buf == NULL)
data->init_sequence.receive_buf =
g_malloc0(VFS5011_RECEIVE_BUF_SIZE);
data->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT;
usb_exchange_async(ssm, &data->init_sequence);
break;
}
}
static void activate_loop_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct vfs5011_data *data;
int r = fpi_ssm_get_error(ssm);
data = FP_INSTANCE_DATA(_dev);
fp_dbg("finishing");
if (data->init_sequence.receive_buf != NULL)
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
if (!data->deactivating && !r) {
submit_image(ssm, data, dev);
fpi_imgdev_report_finger_status(dev, FALSE);
}
fpi_ssm_free(ssm);
data->loop_running = FALSE;
if (data->deactivating) {
fpi_imgdev_deactivate_complete(dev);
} else if (r) {
fpi_imgdev_session_error(dev, r);
} else {
start_scan(dev);
}
}
static void open_loop(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case DEV_OPEN_START:
data->init_sequence.stepcount =
G_N_ELEMENTS(vfs5011_initialization);
data->init_sequence.actions = vfs5011_initialization;
data->init_sequence.device = dev;
data->init_sequence.receive_buf =
g_malloc0(VFS5011_RECEIVE_BUF_SIZE);
data->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT;
usb_exchange_async(ssm, &data->init_sequence);
break;
};
}
static void open_loop_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(_dev);
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
fpi_imgdev_open_complete(dev, 0);
fpi_ssm_free(ssm);
}
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
struct vfs5011_data *data;
int r;
data = (struct vfs5011_data *)g_malloc0(sizeof(*data));
data->capture_buffer =
(unsigned char *)g_malloc0(CAPTURE_LINES * VFS5011_LINE_SIZE);
fp_dev_set_instance_data(FP_DEV(dev), data);
r = libusb_reset_device(fpi_dev_get_usb_dev(FP_DEV(dev)));
if (r != 0) {
fp_err("Failed to reset the device");
return r;
}
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r != 0) {
fp_err("Failed to claim interface: %s", libusb_error_name(r));
return r;
}
fpi_ssm *ssm;
ssm = fpi_ssm_new(FP_DEV(dev), open_loop, DEV_OPEN_NUM_STATES, dev);
fpi_ssm_start(ssm, open_loop_complete);
return 0;
}
static void dev_close(struct fp_img_dev *dev)
{
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(FP_DEV(dev));
if (data != NULL) {
g_free(data->capture_buffer);
g_slist_free_full(data->rows, g_free);
g_free(data);
}
fpi_imgdev_close_complete(dev);
}
static void start_scan(struct fp_img_dev *dev)
{
struct vfs5011_data *data;
fpi_ssm *ssm;
data = FP_INSTANCE_DATA(FP_DEV(dev));
data->loop_running = TRUE;
fp_dbg("creating ssm");
ssm = fpi_ssm_new(FP_DEV(dev), activate_loop, DEV_ACTIVATE_NUM_STATES, dev);
fp_dbg("starting ssm");
fpi_ssm_start(ssm, activate_loop_complete);
fp_dbg("ssm done, getting out");
}
static int dev_activate(struct fp_img_dev *dev)
{
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(FP_DEV(dev));
fp_dbg("device initialized");
data->deactivating = FALSE;
start_scan(dev);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
int r;
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(FP_DEV(dev));
if (data->loop_running) {
data->deactivating = TRUE;
if (data->flying_transfer) {
r = libusb_cancel_transfer(data->flying_transfer);
if (r < 0)
fp_dbg("cancel failed error %d", r);
}
} else
fpi_imgdev_deactivate_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x138a, .product = 0x0010 /* Validity device from some Toshiba laptops */ },
{ .vendor = 0x138a, .product = 0x0011 /* vfs5011 */ },
{ .vendor = 0x138a, .product = 0x0015 /* Validity device from Lenovo Preferred Pro USB Fingerprint Keyboard KUF1256 */ },
{ .vendor = 0x138a, .product = 0x0017 /* Validity device from Lenovo T440 laptops */ },
{ .vendor = 0x138a, .product = 0x0018 /* one more Validity device */ },
{ 0, 0, 0, },
};
struct fp_img_driver vfs5011_driver = {
.driver = {
.id = VFS5011_ID,
.name = "vfs5011",
.full_name = "Validity VFS5011",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_width = VFS5011_IMAGE_WIDTH,
.img_height = -1,
.bz3_threshold = 20,
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};