1563 lines
36 KiB
C
1563 lines
36 KiB
C
/*
|
|
* Validity VFS101 driver for libfprint
|
|
* Copyright (C) 2011 Sergio Cerlesi <sergio.cerlesi@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 "vfs101"
|
|
|
|
#include "drivers_api.h"
|
|
|
|
/* Input-Output usb endpoint */
|
|
#define EP_IN(n) (n | LIBUSB_ENDPOINT_IN)
|
|
#define EP_OUT(n) (n | LIBUSB_ENDPOINT_OUT)
|
|
|
|
/* Usb bulk timeout */
|
|
#define BULK_TIMEOUT 100
|
|
|
|
/* The device send back the image into block of 16 frames of 292 bytes */
|
|
#define VFS_FRAME_SIZE 292
|
|
#define VFS_BLOCK_SIZE 16 * VFS_FRAME_SIZE
|
|
|
|
/* Buffer height */
|
|
#define VFS_BUFFER_HEIGHT 5000
|
|
|
|
/* Buffer size */
|
|
#define VFS_BUFFER_SIZE (VFS_BUFFER_HEIGHT * VFS_FRAME_SIZE)
|
|
|
|
/* Image width */
|
|
#define VFS_IMG_WIDTH 200
|
|
|
|
/* Maximum image height */
|
|
#define VFS_IMG_MAX_HEIGHT 1023
|
|
|
|
/* Minimum image height */
|
|
#define VFS_IMG_MIN_HEIGHT 200
|
|
|
|
/* Scan level thresold */
|
|
#define VFS_IMG_SLT_BEGIN 768
|
|
#define VFS_IMG_SLT_END 64
|
|
#define VFS_IMG_SLT_LINES 4
|
|
|
|
/* Minimum image level */
|
|
#define VFS_IMG_MIN_IMAGE_LEVEL 144
|
|
|
|
/* Best image contrast */
|
|
#define VFS_IMG_BEST_CONRAST 128
|
|
|
|
/* Device parameters address */
|
|
#define VFS_PAR_000E 0x000e
|
|
#define VFS_PAR_0011 0x0011
|
|
#define VFS_PAR_THRESHOLD 0x0057
|
|
#define VFS_PAR_STATE_3 0x005e
|
|
#define VFS_PAR_STATE_5 0x005f
|
|
#define VFS_PAR_INFO_RATE 0x0062
|
|
#define VFS_PAR_0076 0x0076
|
|
#define VFS_PAR_INFO_CONTRAST 0x0077
|
|
#define VFS_PAR_0078 0x0078
|
|
|
|
/* Device regiones address */
|
|
#define VFS_REG_IMG_EXPOSURE 0xff500e
|
|
#define VFS_REG_IMG_CONTRAST 0xff5038
|
|
|
|
/* Device settings */
|
|
#define VFS_VAL_000E 0x0001
|
|
#define VFS_VAL_0011 0x0008
|
|
#define VFS_VAL_THRESHOLD 0x0096
|
|
#define VFS_VAL_STATE_3 0x0064
|
|
#define VFS_VAL_STATE_5 0x00c8
|
|
#define VFS_VAL_INFO_RATE 0x0001
|
|
#define VFS_VAL_0076 0x0012
|
|
#define VFS_VAL_0078 0x2230
|
|
#define VFS_VAL_IMG_EXPOSURE 0x21c0
|
|
|
|
/* Structure for Validity device */
|
|
struct vfs101_dev
|
|
{
|
|
/* Action state */
|
|
int active;
|
|
|
|
/* Sequential number */
|
|
unsigned int seqnum;
|
|
|
|
/* Usb transfer */
|
|
struct libusb_transfer *transfer;
|
|
|
|
/* Buffer for input/output */
|
|
unsigned char buffer[VFS_BUFFER_SIZE];
|
|
|
|
/* Length of data to send or received */
|
|
unsigned int length;
|
|
|
|
/* Ignore usb error */
|
|
int ignore_error;
|
|
|
|
/* Timeout */
|
|
fpi_timeout *timeout;
|
|
|
|
/* Loop counter */
|
|
int counter;
|
|
|
|
/* Number of enroll stage */
|
|
int enroll_stage;
|
|
|
|
/* Image contrast */
|
|
int contrast;
|
|
|
|
/* Best contrast */
|
|
int best_contrast;
|
|
|
|
/* Best contrast level */
|
|
int best_clevel;
|
|
|
|
/* Bottom line of image */
|
|
int bottom;
|
|
|
|
/* Image height */
|
|
int height;
|
|
};
|
|
|
|
/* Return byte at specified position */
|
|
static inline unsigned char byte(int position, int value)
|
|
{
|
|
return (value >> (position * 8)) & 0xff;
|
|
}
|
|
|
|
/* Return sequential number */
|
|
static inline unsigned short get_seqnum(int h, int l)
|
|
{
|
|
return (h<<8) | l;
|
|
}
|
|
|
|
/* Check sequential number */
|
|
static inline int check_seqnum(struct vfs101_dev *vdev)
|
|
{
|
|
if ((byte(0, vdev->seqnum) == vdev->buffer[0]) &&
|
|
(byte(1, vdev->seqnum) == vdev->buffer[1]))
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
/* Internal result codes */
|
|
enum
|
|
{
|
|
RESULT_RETRY,
|
|
RESULT_RETRY_SHORT,
|
|
RESULT_RETRY_REMOVE,
|
|
RESULT_COUNT,
|
|
};
|
|
|
|
/* Enroll result codes */
|
|
static int result_codes[2][RESULT_COUNT] =
|
|
{
|
|
{
|
|
FP_ENROLL_RETRY,
|
|
FP_ENROLL_RETRY_TOO_SHORT,
|
|
FP_ENROLL_RETRY_REMOVE_FINGER,
|
|
},
|
|
{
|
|
FP_VERIFY_RETRY,
|
|
FP_VERIFY_RETRY_TOO_SHORT,
|
|
FP_VERIFY_RETRY_REMOVE_FINGER,
|
|
},
|
|
};
|
|
|
|
/* Return result code based on current action */
|
|
static int result_code(struct fp_img_dev *dev, int result)
|
|
{
|
|
/* Check result value */
|
|
if (result < 0 || result >= RESULT_COUNT)
|
|
return result;
|
|
|
|
/* Return result code */
|
|
if (fpi_imgdev_get_action(dev) == IMG_ACTION_ENROLL)
|
|
return result_codes[0][result];
|
|
else
|
|
return result_codes[1][result];
|
|
};
|
|
|
|
/* Dump buffer for debug */
|
|
#define dump_buffer(buf) \
|
|
fp_dbg("%02x %02x %02x %02x %02x %02x %02x %02x", \
|
|
buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13] \
|
|
)
|
|
|
|
/* Callback of asynchronous send */
|
|
static void async_send_cb(struct libusb_transfer *transfer)
|
|
{
|
|
fpi_ssm *ssm = transfer->user_data;
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
/* Cleanup transfer */
|
|
vdev->transfer = NULL;
|
|
|
|
/* Skip error check if ignore_error is set */
|
|
if (!vdev->ignore_error)
|
|
{
|
|
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(dev, -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(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
/* Reset ignore_error flag */
|
|
vdev->ignore_error = FALSE;
|
|
|
|
/* Dump buffer for debug */
|
|
dump_buffer(vdev->buffer);
|
|
|
|
fpi_ssm_next_state(ssm);
|
|
|
|
out:
|
|
libusb_free_transfer(transfer);
|
|
}
|
|
|
|
/* Submit asynchronous send */
|
|
static void async_send(fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
int r;
|
|
|
|
/* Allocation of transfer */
|
|
vdev->transfer = libusb_alloc_transfer(0);
|
|
if (!vdev->transfer)
|
|
{
|
|
/* Allocation transfer failed, return no memory error */
|
|
fp_err("allocation of usb transfer failed");
|
|
fpi_imgdev_session_error(dev, -ENOMEM);
|
|
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
|
return;
|
|
}
|
|
|
|
/* Put sequential number into the buffer */
|
|
vdev->seqnum++;
|
|
vdev->buffer[0] = byte(0, vdev->seqnum);
|
|
vdev->buffer[1] = byte(1, vdev->seqnum);
|
|
|
|
/* Prepare bulk transfer */
|
|
libusb_fill_bulk_transfer(vdev->transfer, fpi_imgdev_get_usb_dev(dev), EP_OUT(1), vdev->buffer, vdev->length, async_send_cb, ssm, BULK_TIMEOUT);
|
|
|
|
/* Submit transfer */
|
|
r = libusb_submit_transfer(vdev->transfer);
|
|
if (r != 0)
|
|
{
|
|
/* Submission of transfer failed, return IO error */
|
|
libusb_free_transfer(vdev->transfer);
|
|
fp_err("submit of usb transfer failed");
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Callback of asynchronous recv */
|
|
static void async_recv_cb(struct libusb_transfer *transfer)
|
|
{
|
|
fpi_ssm *ssm = transfer->user_data;
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
/* Cleanup transfer */
|
|
vdev->transfer = NULL;
|
|
|
|
/* Skip error check if ignore_error is set */
|
|
if (!vdev->ignore_error)
|
|
{
|
|
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(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
goto out;
|
|
}
|
|
|
|
if (check_seqnum(vdev))
|
|
{
|
|
/* Sequential number received mismatch, return protocol error */
|
|
fp_err("seqnum mismatch, got %04x, expected %04x",
|
|
get_seqnum(vdev->buffer[1], vdev->buffer[0]), vdev->seqnum);
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
/* Reset ignore_error flag */
|
|
vdev->ignore_error = FALSE;
|
|
|
|
/* Dump buffer for debug */
|
|
dump_buffer(vdev->buffer);
|
|
|
|
/* Set length of received data */
|
|
vdev->length = transfer->actual_length;
|
|
|
|
fpi_ssm_next_state(ssm);
|
|
|
|
out:
|
|
libusb_free_transfer(transfer);
|
|
}
|
|
|
|
/* Submit asynchronous recv */
|
|
static void async_recv(fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
int r;
|
|
|
|
/* Allocation of transfer */
|
|
vdev->transfer = libusb_alloc_transfer(0);
|
|
if (!vdev->transfer)
|
|
{
|
|
/* Allocation transfer failed, return no memory error */
|
|
fp_err("allocation of usb transfer failed");
|
|
fpi_imgdev_session_error(dev, -ENOMEM);
|
|
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
|
return;
|
|
}
|
|
|
|
/* Prepare bulk transfer */
|
|
libusb_fill_bulk_transfer(vdev->transfer, fpi_imgdev_get_usb_dev(dev), EP_IN(1), vdev->buffer, 0x0f, async_recv_cb, ssm, BULK_TIMEOUT);
|
|
|
|
/* Submit transfer */
|
|
r = libusb_submit_transfer(vdev->transfer);
|
|
if (r != 0)
|
|
{
|
|
/* Submission of transfer failed, free transfer and return IO error */
|
|
libusb_free_transfer(vdev->transfer);
|
|
fp_err("submit of usb transfer failed");
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void async_load(fpi_ssm *ssm);
|
|
|
|
/* Callback of asynchronous load */
|
|
static void async_load_cb(struct libusb_transfer *transfer)
|
|
{
|
|
fpi_ssm *ssm = transfer->user_data;
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
/* Cleanup transfer */
|
|
vdev->transfer = NULL;
|
|
|
|
/* Skip error check if ignore_error is set */
|
|
if (!vdev->ignore_error)
|
|
{
|
|
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
|
|
{
|
|
/* Transfer not completed */
|
|
fp_err("transfer not completed, status = %d, length = %d", transfer->status, vdev->length);
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
goto out;
|
|
}
|
|
|
|
if (transfer->actual_length % VFS_FRAME_SIZE)
|
|
{
|
|
/* Received incomplete frame, return protocol error */
|
|
fp_err("received incomplete frame");
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Increase image length */
|
|
vdev->length += transfer->actual_length;
|
|
|
|
if (transfer->actual_length == VFS_BLOCK_SIZE)
|
|
{
|
|
if ((VFS_BUFFER_SIZE - vdev->length) < VFS_BLOCK_SIZE)
|
|
{
|
|
/* Buffer full, image too large, return no memory error */
|
|
fp_err("buffer full, image too large");
|
|
fpi_imgdev_session_error(dev, -ENOMEM);
|
|
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
|
goto out;
|
|
}
|
|
else
|
|
/* Image load not completed, submit another asynchronous load */
|
|
async_load(ssm);
|
|
}
|
|
else
|
|
{
|
|
/* Reset ignore_error flag */
|
|
if (vdev->ignore_error)
|
|
vdev->ignore_error = FALSE;
|
|
|
|
/* Image load completed, go to next state */
|
|
vdev->height = vdev->length / VFS_FRAME_SIZE;
|
|
fp_dbg("image loaded, height = %d", vdev->height);
|
|
fpi_ssm_next_state(ssm);
|
|
}
|
|
|
|
out:
|
|
libusb_free_transfer(transfer);
|
|
}
|
|
|
|
/* Submit asynchronous load */
|
|
static void async_load(fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
unsigned char *buffer;
|
|
int r;
|
|
|
|
/* Allocation of transfer */
|
|
vdev->transfer = libusb_alloc_transfer(0);
|
|
if (!vdev->transfer)
|
|
{
|
|
/* Allocation transfer failed, return no memory error */
|
|
fp_err("allocation of usb transfer failed");
|
|
fpi_imgdev_session_error(dev, -ENOMEM);
|
|
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
|
return;
|
|
}
|
|
|
|
/* Append new data into the buffer */
|
|
buffer = vdev->buffer + vdev->length;
|
|
|
|
/* Prepare bulk transfer */
|
|
libusb_fill_bulk_transfer(vdev->transfer, fpi_imgdev_get_usb_dev(dev), EP_IN(2), buffer, VFS_BLOCK_SIZE, async_load_cb, ssm, BULK_TIMEOUT);
|
|
|
|
/* Submit transfer */
|
|
r = libusb_submit_transfer(vdev->transfer);
|
|
if (r != 0)
|
|
{
|
|
/* Submission of transfer failed, return IO error */
|
|
libusb_free_transfer(vdev->transfer);
|
|
fp_err("submit of usb transfer failed");
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Callback of asynchronous sleep */
|
|
static void async_sleep_cb(void *data)
|
|
{
|
|
fpi_ssm *ssm = data;
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
/* Cleanup timeout */
|
|
vdev->timeout = NULL;
|
|
|
|
fpi_ssm_next_state(ssm);
|
|
}
|
|
|
|
/* Submit asynchronous sleep */
|
|
static void async_sleep(unsigned int msec, fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
/* Add timeout */
|
|
vdev->timeout = fpi_timeout_add(msec, async_sleep_cb, ssm);
|
|
|
|
if (vdev->timeout == NULL)
|
|
{
|
|
/* Failed to add timeout */
|
|
fp_err("failed to add timeout");
|
|
fpi_imgdev_session_error(dev, -ETIME);
|
|
fpi_ssm_mark_failed(ssm, -ETIME);
|
|
}
|
|
}
|
|
|
|
/* Swap ssm states */
|
|
enum
|
|
{
|
|
M_SWAP_SEND,
|
|
M_SWAP_RECV,
|
|
M_SWAP_NUM_STATES,
|
|
};
|
|
|
|
/* Exec swap sequential state machine */
|
|
static void m_swap_state(fpi_ssm *ssm)
|
|
{
|
|
switch (fpi_ssm_get_cur_state(ssm))
|
|
{
|
|
case M_SWAP_SEND:
|
|
/* Send data */
|
|
async_send(ssm);
|
|
break;
|
|
|
|
case M_SWAP_RECV:
|
|
/* Recv response */
|
|
async_recv(ssm);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Start swap sequential state machine */
|
|
static void m_swap(fpi_ssm *ssm, unsigned char *data, size_t length)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
fpi_ssm *subsm;
|
|
|
|
/* Prepare data for sending */
|
|
memcpy(vdev->buffer, data, length);
|
|
memset(vdev->buffer + length, 0, 16 - length);
|
|
vdev->length = length;
|
|
|
|
/* Start swap ssm */
|
|
subsm = fpi_ssm_new(FP_DEV(dev), m_swap_state, M_SWAP_NUM_STATES);
|
|
fpi_ssm_set_user_data(subsm, dev);
|
|
fpi_ssm_start_subsm(ssm, subsm);
|
|
}
|
|
|
|
/* Retrieve fingerprint image */
|
|
static void vfs_get_print(fpi_ssm *ssm, unsigned int param, int type)
|
|
{
|
|
unsigned char data[2][0x0e] = {
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
|
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01 },
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01 }
|
|
};
|
|
|
|
fp_dbg("param = %04x, type = %d", param, type);
|
|
|
|
/* Prepare data for sending */
|
|
data[type][6] = byte(0, param);
|
|
data[type][7] = byte(1, param);
|
|
|
|
/* Run swap sequential state machine */
|
|
m_swap(ssm, data[type], 0x0e);
|
|
}
|
|
|
|
/* Set a parameter value on the device */
|
|
static void vfs_set_param(fpi_ssm *ssm, unsigned int param, unsigned int value)
|
|
{
|
|
unsigned char data[0x0a] = { 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
fp_dbg("param = %04x, value = %04x", param, value);
|
|
|
|
/* Prepare data for sending */
|
|
data[6] = byte(0, param);
|
|
data[7] = byte(1, param);
|
|
data[8] = byte(0, value);
|
|
data[9] = byte(1, value);
|
|
|
|
/* Run swap sequential state machine */
|
|
m_swap(ssm, data, 0x0a);
|
|
}
|
|
|
|
/* Abort previous print */
|
|
static void vfs_abort_print(fpi_ssm *ssm)
|
|
{
|
|
unsigned char data[0x06] = { 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00 };
|
|
|
|
G_DEBUG_HERE();
|
|
|
|
/* Run swap sequential state machine */
|
|
m_swap (ssm, data, 0x06);
|
|
}
|
|
|
|
/* Poke a value on a region */
|
|
static void vfs_poke(fpi_ssm *ssm, unsigned int addr, unsigned int value, unsigned int size)
|
|
{
|
|
unsigned char data[0x0f] = { 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
fp_dbg("addr = %04x, value = %04x", addr, value);
|
|
|
|
/* Prepare data for sending */
|
|
data[6] = byte(0, addr);
|
|
data[7] = byte(1, addr);
|
|
data[8] = byte(2, addr);
|
|
data[9] = byte(3, addr);
|
|
data[10] = byte(0, value);
|
|
data[11] = byte(1, value);
|
|
data[12] = byte(2, value);
|
|
data[13] = byte(3, value);
|
|
data[14] = byte(0, size);
|
|
|
|
/* Run swap sequential state machine */
|
|
m_swap(ssm, data, 0x0f);
|
|
}
|
|
|
|
/* Get current finger state */
|
|
static void vfs_get_finger_state(fpi_ssm *ssm)
|
|
{
|
|
unsigned char data[0x06] = { 0x00, 0x00, 0x00, 0x00, 0x16, 0x00 };
|
|
|
|
G_DEBUG_HERE();
|
|
|
|
/* Run swap sequential state machine */
|
|
m_swap (ssm, data, 0x06);
|
|
}
|
|
|
|
/* Load raw image from reader */
|
|
static void vfs_img_load(fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
G_DEBUG_HERE();
|
|
|
|
/* Reset buffer length */
|
|
vdev->length = 0;
|
|
|
|
/* Reset image properties */
|
|
vdev->bottom = 0;
|
|
vdev->height = -1;
|
|
|
|
/* Asynchronous load */
|
|
async_load(ssm);
|
|
}
|
|
|
|
/* Check if action is completed */
|
|
static int action_completed(struct fp_img_dev *dev)
|
|
{
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
if ((fpi_imgdev_get_action(dev) == IMG_ACTION_ENROLL) &&
|
|
(vdev->enroll_stage < fpi_dev_get_nr_enroll_stages(FP_DEV(dev))))
|
|
/* Enroll not completed, return false */
|
|
return FALSE;
|
|
|
|
else if (vdev->enroll_stage < 1)
|
|
/* Action not completed, return false */
|
|
return FALSE;
|
|
|
|
/* Action completed, return true */
|
|
return TRUE;
|
|
}
|
|
|
|
#define offset(x, y) ((x) + ((y) * VFS_FRAME_SIZE))
|
|
|
|
/* Screen image to remove noise and find bottom line and height od image */
|
|
static void img_screen(struct vfs101_dev *vdev)
|
|
{
|
|
int y, x, count, top;
|
|
long int level;
|
|
int last_line = vdev->height - 1;
|
|
|
|
fp_dbg("image height before screen = %d", vdev->height);
|
|
|
|
count = 0;
|
|
|
|
/* Image returned from sensor can contain many empty lines,
|
|
* for remove these lines compare byte 282-283 (scan level information)
|
|
* with two differents threshold, one for the begin of finger image and
|
|
* one for the end. To increase stability of the code use a counter
|
|
* of lines that satisfy the threshold.
|
|
*/
|
|
for (y = last_line, top = last_line; y >= 0; y--)
|
|
{
|
|
/* Take image scan level */
|
|
level = vdev->buffer[offset(283, y)] * 256 +
|
|
vdev->buffer[offset(282, y)];
|
|
|
|
fp_dbg("line = %d, scan level = %ld", y, level);
|
|
|
|
if (level >= VFS_IMG_SLT_BEGIN && top == last_line)
|
|
{
|
|
/* Begin threshold satisfied */
|
|
if (count < VFS_IMG_SLT_LINES)
|
|
/* Increase count */
|
|
count++;
|
|
else
|
|
{
|
|
/* Found top fingerprint line */
|
|
top = y + VFS_IMG_SLT_LINES;
|
|
count = 0;
|
|
}
|
|
}
|
|
else if ((level < VFS_IMG_SLT_END || level >= 65535) &&
|
|
top != last_line)
|
|
{
|
|
/* End threshold satisfied */
|
|
if (count < VFS_IMG_SLT_LINES)
|
|
/* Increase count */
|
|
count++;
|
|
else
|
|
{
|
|
/* Found bottom fingerprint line */
|
|
vdev->bottom = y + VFS_IMG_SLT_LINES + 1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
/* Not threshold satisfied, reset count */
|
|
count = 0;
|
|
}
|
|
|
|
vdev->height = top - vdev->bottom + 1;
|
|
|
|
/* Checkk max height */
|
|
if (vdev->height > VFS_IMG_MAX_HEIGHT)
|
|
vdev->height = VFS_IMG_MAX_HEIGHT;
|
|
|
|
fp_dbg("image height after screen = %d", vdev->height);
|
|
|
|
/* Scan image and remove noise */
|
|
for (y = vdev->bottom; y <= top; y++)
|
|
for (x = 6; x < VFS_IMG_WIDTH + 6; x++)
|
|
if (vdev->buffer[offset(x, y)] > VFS_IMG_MIN_IMAGE_LEVEL)
|
|
vdev->buffer[offset(x, y)] = 255;
|
|
};
|
|
|
|
/* Copy image from reader buffer and put it into image data */
|
|
static void img_copy(struct vfs101_dev *vdev, struct fp_img *img)
|
|
{
|
|
unsigned int line;
|
|
unsigned char *img_buffer = img->data;
|
|
unsigned char *vdev_buffer = vdev->buffer + (vdev->bottom * VFS_FRAME_SIZE) + 6;
|
|
|
|
for (line = 0; line < img->height; line++)
|
|
{
|
|
/* Copy image line from reader buffer to image data */
|
|
memcpy(img_buffer, vdev_buffer, VFS_IMG_WIDTH);
|
|
|
|
/* Next line of reader buffer */
|
|
vdev_buffer = vdev_buffer + VFS_FRAME_SIZE;
|
|
|
|
/* Next line of image buffer */
|
|
img_buffer = img_buffer + VFS_IMG_WIDTH;
|
|
}
|
|
}
|
|
|
|
/* Extract fingerpint image from raw data */
|
|
static void img_extract(fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
struct fp_img *img;
|
|
|
|
/* Screen image to remove noise and find top and bottom line */
|
|
img_screen(vdev);
|
|
|
|
/* Check image height */
|
|
if (vdev->height < VFS_IMG_MIN_HEIGHT)
|
|
{
|
|
/* Image too short */
|
|
vdev->height = 0;
|
|
return;
|
|
}
|
|
|
|
/* Fingerprint is present, load image from reader */
|
|
fpi_imgdev_report_finger_status(dev, TRUE);
|
|
|
|
/* Create new image */
|
|
img = fpi_img_new(vdev->height * VFS_IMG_WIDTH);
|
|
img->width = VFS_IMG_WIDTH;
|
|
img->height = vdev->height;
|
|
img->flags = FP_IMG_V_FLIPPED;
|
|
|
|
/* Copy data into image */
|
|
img_copy(vdev, img);
|
|
|
|
/* Notify image captured */
|
|
fpi_imgdev_image_captured(dev, img);
|
|
|
|
/* Check captured result */
|
|
if (fpi_imgdev_get_action_result(dev) >= 0 &&
|
|
fpi_imgdev_get_action_result(dev) != FP_ENROLL_RETRY &&
|
|
fpi_imgdev_get_action_result(dev) != FP_VERIFY_RETRY)
|
|
{
|
|
/* Image captured, increase enroll stage */
|
|
vdev->enroll_stage++;
|
|
|
|
/* Check if action is completed */
|
|
if (!action_completed(dev))
|
|
fpi_imgdev_set_action_result(dev, FP_ENROLL_PASS);
|
|
}
|
|
else
|
|
{
|
|
/* Image capture failed */
|
|
if (fpi_imgdev_get_action(dev) == IMG_ACTION_ENROLL)
|
|
/* Return retry */
|
|
fpi_imgdev_set_action_result(dev, result_code(dev, RESULT_RETRY));
|
|
else
|
|
{
|
|
/* Return no match */
|
|
vdev->enroll_stage++;
|
|
fpi_imgdev_set_action_result(dev, FP_VERIFY_NO_MATCH);
|
|
}
|
|
}
|
|
|
|
/* Fingerprint is removed from reader */
|
|
fpi_imgdev_report_finger_status(dev, FALSE);
|
|
};
|
|
|
|
/* Finger states */
|
|
enum
|
|
{
|
|
VFS_FINGER_EMPTY,
|
|
VFS_FINGER_PRESENT,
|
|
VFS_FINGER_UNKNOWN,
|
|
};
|
|
|
|
/* Return finger state */
|
|
static inline int vfs_finger_state(struct vfs101_dev *vdev)
|
|
{
|
|
/* Check finger state */
|
|
switch (vdev->buffer[0x0a])
|
|
{
|
|
case 0x00:
|
|
case 0x01:
|
|
/* Finger is empty */
|
|
return VFS_FINGER_EMPTY;
|
|
break;
|
|
|
|
case 0x02:
|
|
case 0x03:
|
|
case 0x04:
|
|
case 0x05:
|
|
case 0x06:
|
|
/* Finger is present */
|
|
return VFS_FINGER_PRESENT;
|
|
break;
|
|
|
|
default:
|
|
return VFS_FINGER_UNKNOWN;
|
|
}
|
|
};
|
|
|
|
/* Check contrast of image */
|
|
static void vfs_check_contrast(struct vfs101_dev *vdev)
|
|
{
|
|
int y;
|
|
long int count = 0;
|
|
|
|
/* Check difference from byte 4 to byte 5 for verify contrast of image */
|
|
for (y = 0; y < vdev->height; y++)
|
|
count = count + vdev->buffer[offset(5, y)] - vdev->buffer[offset(4, y)];
|
|
count = count / vdev->height;
|
|
|
|
if (count < 16)
|
|
{
|
|
/* Contrast not valid, retry */
|
|
vdev->contrast++;
|
|
return;
|
|
}
|
|
|
|
fp_dbg("contrast = %d, level = %ld", vdev->contrast, count);
|
|
|
|
if (abs(count - VFS_IMG_BEST_CONRAST) < abs(vdev->best_clevel - VFS_IMG_BEST_CONRAST))
|
|
{
|
|
/* Better contrast found, use it */
|
|
vdev->best_contrast = vdev->contrast;
|
|
vdev->best_clevel = count;
|
|
}
|
|
}
|
|
|
|
/* Loop ssm states */
|
|
enum
|
|
{
|
|
/* Step 0 - Scan finger */
|
|
M_LOOP_0_GET_PRINT,
|
|
M_LOOP_0_SLEEP,
|
|
M_LOOP_0_GET_STATE,
|
|
M_LOOP_0_LOAD_IMAGE,
|
|
M_LOOP_0_EXTRACT_IMAGE,
|
|
M_LOOP_0_CHECK_ACTION,
|
|
|
|
/* Step 1 - Scan failed */
|
|
M_LOOP_1_GET_STATE,
|
|
M_LOOP_1_CHECK_STATE,
|
|
M_LOOP_1_GET_PRINT,
|
|
M_LOOP_1_LOAD_IMAGE,
|
|
M_LOOP_1_LOOP,
|
|
M_LOOP_1_SLEEP,
|
|
|
|
/* Step 2 - Abort print */
|
|
M_LOOP_2_ABORT_PRINT,
|
|
M_LOOP_2_LOAD_IMAGE,
|
|
|
|
/* Step 3 - Wait aborting */
|
|
M_LOOP_3_GET_PRINT,
|
|
M_LOOP_3_LOAD_IMAGE,
|
|
M_LOOP_3_CHECK_IMAGE,
|
|
M_LOOP_3_LOOP,
|
|
|
|
/* Number of states */
|
|
M_LOOP_NUM_STATES,
|
|
};
|
|
|
|
/* Exec loop sequential state machine */
|
|
static void m_loop_state(fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
/* Check action state */
|
|
if (!vdev->active)
|
|
{
|
|
/* Action not active, mark sequential state machine completed */
|
|
fpi_ssm_mark_completed(ssm);
|
|
return;
|
|
}
|
|
|
|
switch (fpi_ssm_get_cur_state(ssm))
|
|
{
|
|
case M_LOOP_0_GET_PRINT:
|
|
/* Send get print command to the reader */
|
|
vfs_get_print(ssm, VFS_BUFFER_HEIGHT, 1);
|
|
break;
|
|
|
|
case M_LOOP_0_SLEEP:
|
|
/* Wait fingerprint scanning */
|
|
async_sleep(50, ssm);
|
|
break;
|
|
|
|
case M_LOOP_0_GET_STATE:
|
|
/* Get finger state */
|
|
vfs_get_finger_state(ssm);
|
|
break;
|
|
|
|
case M_LOOP_0_LOAD_IMAGE:
|
|
/* Check finger state */
|
|
switch (vfs_finger_state(vdev))
|
|
{
|
|
case VFS_FINGER_EMPTY:
|
|
/* Finger isn't present, loop */
|
|
fpi_ssm_jump_to_state(ssm, M_LOOP_0_SLEEP);
|
|
break;
|
|
|
|
case VFS_FINGER_PRESENT:
|
|
/* Load image from reader */
|
|
vdev->ignore_error = TRUE;
|
|
vfs_img_load(ssm);
|
|
break;
|
|
|
|
default:
|
|
/* Unknown state */
|
|
fp_err("unknown device state 0x%02x", vdev->buffer[0x0a]);
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case M_LOOP_0_EXTRACT_IMAGE:
|
|
/* Check if image is loaded */
|
|
if (vdev->height > 0)
|
|
/* Fingerprint is loaded, extract image from raw data */
|
|
img_extract(ssm);
|
|
|
|
/* Wait handling image */
|
|
async_sleep(10, ssm);
|
|
break;
|
|
|
|
case M_LOOP_0_CHECK_ACTION:
|
|
/* Check if action is completed */
|
|
if (action_completed(dev))
|
|
/* Action completed */
|
|
fpi_ssm_mark_completed(ssm);
|
|
else
|
|
/* Action not completed */
|
|
if (vdev->height > 0)
|
|
/* Continue loop */
|
|
fpi_ssm_jump_to_state(ssm, M_LOOP_2_ABORT_PRINT);
|
|
else
|
|
/* Error found */
|
|
fpi_ssm_next_state(ssm);
|
|
break;
|
|
|
|
case M_LOOP_1_GET_STATE:
|
|
/* Get finger state */
|
|
vfs_get_finger_state(ssm);
|
|
break;
|
|
|
|
case M_LOOP_1_CHECK_STATE:
|
|
/* Check finger state */
|
|
if (vfs_finger_state(vdev) == VFS_FINGER_PRESENT)
|
|
{
|
|
if (vdev->counter < 20)
|
|
{
|
|
if (vdev->counter == 1)
|
|
{
|
|
/* The user should remove their finger from the scanner */
|
|
fp_warn("finger present after scan, remove it");
|
|
fpi_imgdev_session_error(dev, result_code(dev, RESULT_RETRY_REMOVE));
|
|
}
|
|
|
|
/* Wait removing finger */
|
|
vdev->counter++;
|
|
async_sleep(250, ssm);
|
|
}
|
|
else
|
|
{
|
|
/* reach max loop counter, return protocol error */
|
|
fp_err("finger not removed from the scanner");
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Finger not present */
|
|
if (vdev->counter == 0)
|
|
{
|
|
/* Check image height */
|
|
if (vdev->height == 0)
|
|
{
|
|
/* Return retry to short */
|
|
fp_warn("image too short, retry");
|
|
fpi_imgdev_session_error(dev, result_code(dev, RESULT_RETRY_SHORT));
|
|
}
|
|
else
|
|
{
|
|
/* Return retry result */
|
|
fp_warn("load image failed, retry");
|
|
fpi_imgdev_session_error(dev, result_code(dev, RESULT_RETRY));
|
|
}
|
|
}
|
|
|
|
/* Continue */
|
|
vdev->counter = 0;
|
|
fpi_ssm_jump_to_state(ssm, M_LOOP_1_SLEEP);
|
|
}
|
|
break;
|
|
|
|
case M_LOOP_1_GET_PRINT:
|
|
/* Send get print command to the reader */
|
|
vfs_get_print(ssm, VFS_BUFFER_HEIGHT, 1);
|
|
break;
|
|
|
|
case M_LOOP_1_LOAD_IMAGE:
|
|
/* Load image */
|
|
vdev->ignore_error = TRUE;
|
|
vfs_img_load(ssm);
|
|
break;
|
|
|
|
case M_LOOP_1_LOOP:
|
|
/* Loop */
|
|
fpi_ssm_jump_to_state(ssm, M_LOOP_1_GET_STATE);
|
|
break;
|
|
|
|
case M_LOOP_1_SLEEP:
|
|
/* Wait fingerprint scanning */
|
|
async_sleep(10, ssm);
|
|
break;
|
|
|
|
case M_LOOP_2_ABORT_PRINT:
|
|
/* Abort print command */
|
|
vfs_abort_print(ssm);
|
|
break;
|
|
|
|
case M_LOOP_2_LOAD_IMAGE:
|
|
/* Load abort image */
|
|
vdev->ignore_error = TRUE;
|
|
vfs_img_load(ssm);
|
|
break;
|
|
|
|
case M_LOOP_3_GET_PRINT:
|
|
/* Get empty image */
|
|
vfs_get_print(ssm, 0x000a, 0);
|
|
break;
|
|
|
|
case M_LOOP_3_LOAD_IMAGE:
|
|
/* Load abort image */
|
|
vdev->ignore_error = TRUE;
|
|
vfs_img_load(ssm);
|
|
break;
|
|
|
|
case M_LOOP_3_CHECK_IMAGE:
|
|
if (vdev->height == 10)
|
|
{
|
|
/* Image load correctly, jump to step 0 */
|
|
vdev->counter = 0;
|
|
fpi_ssm_jump_to_state(ssm, M_LOOP_0_GET_PRINT);
|
|
}
|
|
else if (vdev->counter < 10)
|
|
{
|
|
/* Wait aborting */
|
|
vdev->counter++;
|
|
async_sleep(100, ssm);
|
|
}
|
|
else
|
|
{
|
|
/* reach max loop counter, return protocol error */
|
|
fp_err("waiting abort reach max loop counter");
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
}
|
|
break;
|
|
|
|
case M_LOOP_3_LOOP:
|
|
/* Loop */
|
|
fpi_ssm_jump_to_state(ssm, M_LOOP_3_GET_PRINT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Complete loop sequential state machine */
|
|
static void m_loop_complete(fpi_ssm *ssm)
|
|
{
|
|
/* Free sequential state machine */
|
|
fpi_ssm_free(ssm);
|
|
}
|
|
|
|
/* Init ssm states */
|
|
enum
|
|
{
|
|
/* Step 0 - Cleanup device buffer */
|
|
M_INIT_0_RECV_DIRTY,
|
|
M_INIT_0_ABORT_PRINT,
|
|
M_INIT_0_LOAD_IMAGE,
|
|
|
|
/* Step 1 - Wait aborting */
|
|
M_INIT_1_GET_PRINT,
|
|
M_INIT_1_LOAD_IMAGE,
|
|
M_INIT_1_CHECK_IMAGE,
|
|
M_INIT_1_LOOP,
|
|
|
|
/* Step 2 - Handle unexpected finger presence */
|
|
M_INIT_2_GET_STATE,
|
|
M_INIT_2_CHECK_STATE,
|
|
M_INIT_2_GET_PRINT,
|
|
M_INIT_2_LOAD_IMAGE,
|
|
M_INIT_2_LOOP,
|
|
|
|
/* Step 3 - Set parameters */
|
|
M_INIT_3_SET_000E,
|
|
M_INIT_3_SET_0011,
|
|
M_INIT_3_SET_0076,
|
|
M_INIT_3_SET_0078,
|
|
M_INIT_3_SET_THRESHOLD,
|
|
M_INIT_3_SET_STATE3_COUNT,
|
|
M_INIT_3_SET_STATE5_COUNT,
|
|
M_INIT_3_SET_INFO_CONTRAST,
|
|
M_INIT_3_SET_INFO_RATE,
|
|
|
|
/* Step 4 - Autocalibrate contrast */
|
|
M_INIT_4_SET_EXPOSURE,
|
|
M_INIT_4_SET_CONTRAST,
|
|
M_INIT_4_GET_PRINT,
|
|
M_INIT_4_LOAD_IMAGE,
|
|
M_INIT_4_CHECK_CONTRAST,
|
|
|
|
/* Step 5 - Set info line parameters */
|
|
M_INIT_5_SET_EXPOSURE,
|
|
M_INIT_5_SET_CONTRAST,
|
|
M_INIT_5_SET_INFO_CONTRAST,
|
|
M_INIT_5_SET_INFO_RATE,
|
|
|
|
/* Number of states */
|
|
M_INIT_NUM_STATES,
|
|
};
|
|
|
|
/* Exec init sequential state machine */
|
|
static void m_init_state(fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
/* Check action state */
|
|
if (!vdev->active)
|
|
{
|
|
/* Action not active, mark sequential state machine completed */
|
|
fpi_ssm_mark_completed(ssm);
|
|
return;
|
|
}
|
|
|
|
switch (fpi_ssm_get_cur_state(ssm))
|
|
{
|
|
case M_INIT_0_RECV_DIRTY:
|
|
/* Recv eventualy dirty data */
|
|
vdev->ignore_error = TRUE;
|
|
async_recv(ssm);
|
|
break;
|
|
|
|
case M_INIT_0_ABORT_PRINT:
|
|
/* Abort print command */
|
|
vfs_abort_print(ssm);
|
|
break;
|
|
|
|
case M_INIT_0_LOAD_IMAGE:
|
|
/* Load abort image */
|
|
vdev->ignore_error = TRUE;
|
|
vfs_img_load(ssm);
|
|
break;
|
|
|
|
case M_INIT_1_GET_PRINT:
|
|
/* Get empty image */
|
|
vfs_get_print(ssm, 0x000a, 0);
|
|
break;
|
|
|
|
case M_INIT_1_LOAD_IMAGE:
|
|
/* Load abort image */
|
|
vdev->ignore_error = TRUE;
|
|
vfs_img_load(ssm);
|
|
break;
|
|
|
|
case M_INIT_1_CHECK_IMAGE:
|
|
if (vdev->height == 10)
|
|
{
|
|
/* Image load correctly, jump to step 2 */
|
|
vdev->counter = 0;
|
|
fpi_ssm_jump_to_state(ssm, M_INIT_2_GET_STATE);
|
|
}
|
|
else if (vdev->counter < 10)
|
|
{
|
|
/* Wait aborting */
|
|
vdev->counter++;
|
|
async_sleep(100, ssm);
|
|
}
|
|
else
|
|
{
|
|
/* reach max loop counter, return protocol error */
|
|
fp_err("waiting abort reach max loop counter");
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
}
|
|
break;
|
|
|
|
case M_INIT_1_LOOP:
|
|
/* Loop */
|
|
fpi_ssm_jump_to_state(ssm, M_INIT_1_GET_PRINT);
|
|
break;
|
|
|
|
case M_INIT_2_GET_STATE:
|
|
/* Get finger state */
|
|
vfs_get_finger_state(ssm);
|
|
break;
|
|
|
|
case M_INIT_2_CHECK_STATE:
|
|
/* Check finger state */
|
|
if (vfs_finger_state(vdev) == VFS_FINGER_PRESENT)
|
|
{
|
|
if (vdev->counter < 20)
|
|
{
|
|
if (vdev->counter == 2)
|
|
{
|
|
/* The user should remove their finger from the scanner */
|
|
fp_warn("unexpected finger find, remove finger from the scanner");
|
|
fpi_imgdev_session_error(dev, result_code(dev, RESULT_RETRY_REMOVE));
|
|
}
|
|
|
|
/* Wait removing finger */
|
|
vdev->counter++;
|
|
async_sleep(250, ssm);
|
|
}
|
|
else
|
|
{
|
|
/* reach max loop counter, return protocol error */
|
|
fp_err("finger not removed from the scanner");
|
|
fpi_imgdev_session_error(dev, -EIO);
|
|
fpi_ssm_mark_failed(ssm, -EIO);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Finger not present */
|
|
if (vdev->counter == 0)
|
|
/* Continue */
|
|
fpi_ssm_jump_to_state(ssm, M_INIT_3_SET_000E);
|
|
else
|
|
{
|
|
/* Finger removed, jump to abort */
|
|
vdev->counter = 0;
|
|
fpi_ssm_jump_to_state(ssm, M_INIT_0_ABORT_PRINT);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case M_INIT_2_GET_PRINT:
|
|
/* Send get print command to the reader */
|
|
vfs_get_print(ssm, VFS_BUFFER_HEIGHT, 1);
|
|
break;
|
|
|
|
case M_INIT_2_LOAD_IMAGE:
|
|
/* Load unexpected image */
|
|
vdev->ignore_error = TRUE;
|
|
vfs_img_load(ssm);
|
|
break;
|
|
|
|
case M_INIT_2_LOOP:
|
|
/* Loop */
|
|
fpi_ssm_jump_to_state(ssm, M_INIT_2_GET_STATE);
|
|
break;
|
|
|
|
case M_INIT_3_SET_000E:
|
|
/* Set param 0x000e, required for take image */
|
|
vfs_set_param(ssm, VFS_PAR_000E, VFS_VAL_000E);
|
|
break;
|
|
|
|
case M_INIT_3_SET_0011:
|
|
/* Set param 0x0011, required for take image */
|
|
vfs_set_param(ssm, VFS_PAR_0011, VFS_VAL_0011);
|
|
break;
|
|
|
|
case M_INIT_3_SET_0076:
|
|
/* Set param 0x0076, required for use info line */
|
|
vfs_set_param(ssm, VFS_PAR_0076, VFS_VAL_0076);
|
|
break;
|
|
|
|
case M_INIT_3_SET_0078:
|
|
/* Set param 0x0078, required for use info line */
|
|
vfs_set_param(ssm, VFS_PAR_0078, VFS_VAL_0078);
|
|
break;
|
|
|
|
case M_INIT_3_SET_THRESHOLD:
|
|
/* Set threshold */
|
|
vfs_set_param(ssm, VFS_PAR_THRESHOLD, VFS_VAL_THRESHOLD);
|
|
break;
|
|
|
|
case M_INIT_3_SET_STATE3_COUNT:
|
|
/* Set state 3 count */
|
|
vfs_set_param(ssm, VFS_PAR_STATE_3, VFS_VAL_STATE_3);
|
|
break;
|
|
|
|
case M_INIT_3_SET_STATE5_COUNT:
|
|
/* Set state 5 count */
|
|
vfs_set_param(ssm, VFS_PAR_STATE_5, VFS_VAL_STATE_5);
|
|
break;
|
|
|
|
case M_INIT_3_SET_INFO_CONTRAST:
|
|
/* Set info line contrast */
|
|
vfs_set_param(ssm, VFS_PAR_INFO_CONTRAST, 10);
|
|
break;
|
|
|
|
case M_INIT_3_SET_INFO_RATE:
|
|
/* Set info line rate */
|
|
vfs_set_param(ssm, VFS_PAR_INFO_RATE, 32);
|
|
break;
|
|
|
|
case M_INIT_4_SET_EXPOSURE:
|
|
/* Set exposure level of reader */
|
|
vfs_poke(ssm, VFS_REG_IMG_EXPOSURE, 0x4000, 0x02);
|
|
vdev->counter = 1;
|
|
break;
|
|
|
|
case M_INIT_4_SET_CONTRAST:
|
|
/* Set contrast level of reader */
|
|
vfs_poke(ssm, VFS_REG_IMG_CONTRAST, vdev->contrast, 0x01);
|
|
break;
|
|
|
|
case M_INIT_4_GET_PRINT:
|
|
/* Get empty image */
|
|
vfs_get_print(ssm, 0x000a, 0);
|
|
break;
|
|
|
|
case M_INIT_4_LOAD_IMAGE:
|
|
/* Load empty image */
|
|
vfs_img_load(ssm);
|
|
break;
|
|
|
|
case M_INIT_4_CHECK_CONTRAST:
|
|
/* Check contrast */
|
|
vfs_check_contrast(vdev);
|
|
|
|
if (vdev->contrast <= 6 || vdev->counter >= 12)
|
|
{
|
|
/* End contrast scan, continue */
|
|
vdev->contrast = vdev->best_contrast;
|
|
vdev->counter = 0;
|
|
fp_dbg("use contrast value = %d", vdev->contrast);
|
|
fpi_ssm_next_state(ssm);
|
|
}
|
|
else
|
|
{
|
|
/* Continue contrast scan, loop */
|
|
vdev->contrast--;
|
|
vdev->counter++;
|
|
fpi_ssm_jump_to_state(ssm, M_INIT_4_SET_CONTRAST);
|
|
}
|
|
break;
|
|
|
|
case M_INIT_5_SET_EXPOSURE:
|
|
/* Set exposure level of reader */
|
|
vfs_poke(ssm, VFS_REG_IMG_EXPOSURE, VFS_VAL_IMG_EXPOSURE, 0x02);
|
|
break;
|
|
|
|
case M_INIT_5_SET_CONTRAST:
|
|
/* Set contrast level of reader */
|
|
vfs_poke(ssm, VFS_REG_IMG_CONTRAST, vdev->contrast, 0x01);
|
|
break;
|
|
|
|
case M_INIT_5_SET_INFO_CONTRAST:
|
|
/* Set info line contrast */
|
|
vfs_set_param(ssm, VFS_PAR_INFO_CONTRAST, vdev->contrast);
|
|
break;
|
|
|
|
case M_INIT_5_SET_INFO_RATE:
|
|
/* Set info line rate */
|
|
vfs_set_param(ssm, VFS_PAR_INFO_RATE, VFS_VAL_INFO_RATE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Complete init sequential state machine */
|
|
static void m_init_complete(fpi_ssm *ssm)
|
|
{
|
|
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
fpi_ssm *ssm_loop;
|
|
|
|
if (!fpi_ssm_get_error(ssm) && vdev->active)
|
|
{
|
|
/* Notify activate complete */
|
|
fpi_imgdev_activate_complete(dev, 0);
|
|
|
|
/* Start loop ssm */
|
|
ssm_loop = fpi_ssm_new(FP_DEV(dev), m_loop_state, M_LOOP_NUM_STATES);
|
|
fpi_ssm_set_user_data(ssm_loop, dev);
|
|
fpi_ssm_start(ssm_loop, m_loop_complete);
|
|
}
|
|
|
|
/* Free sequential state machine */
|
|
fpi_ssm_free(ssm);
|
|
}
|
|
|
|
/* Activate device */
|
|
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
|
|
{
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
fpi_ssm *ssm;
|
|
|
|
/* Check if already active */
|
|
if (vdev->active)
|
|
{
|
|
fp_err("device already activated");
|
|
fpi_imgdev_session_error(dev, -EBUSY);
|
|
return 1;
|
|
}
|
|
|
|
/* Set active state */
|
|
vdev->active = TRUE;
|
|
|
|
/* Set contrast */
|
|
vdev->contrast = 15;
|
|
vdev->best_clevel = -1;
|
|
|
|
/* Reset loop counter and enroll stage */
|
|
vdev->counter = 0;
|
|
vdev->enroll_stage = 0;
|
|
|
|
/* Start init ssm */
|
|
ssm = fpi_ssm_new(FP_DEV(dev), m_init_state, M_INIT_NUM_STATES);
|
|
fpi_ssm_set_user_data(ssm, dev);
|
|
fpi_ssm_start(ssm, m_init_complete);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Deactivate device */
|
|
static void dev_deactivate(struct fp_img_dev *dev)
|
|
{
|
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
|
|
/* Reset active state */
|
|
vdev->active = FALSE;
|
|
|
|
/* Handle eventualy existing events */
|
|
while (vdev->transfer || vdev->timeout)
|
|
fp_handle_events();
|
|
|
|
/* Notify deactivate complete */
|
|
fpi_imgdev_deactivate_complete(dev);
|
|
}
|
|
|
|
/* Open device */
|
|
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
|
|
{
|
|
struct vfs101_dev *vdev = NULL;
|
|
int r;
|
|
|
|
/* Claim usb interface */
|
|
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
|
|
if (r < 0)
|
|
{
|
|
/* Interface not claimed, return error */
|
|
fp_err("could not claim interface 0: %s", libusb_error_name(r));
|
|
return r;
|
|
}
|
|
|
|
/* Initialize private structure */
|
|
vdev = g_malloc0(sizeof(struct vfs101_dev));
|
|
vdev->seqnum = -1;
|
|
fp_dev_set_instance_data(FP_DEV(dev), vdev);
|
|
|
|
/* Notify open complete */
|
|
fpi_imgdev_open_complete(dev, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Close device */
|
|
static void dev_close(struct fp_img_dev *dev)
|
|
{
|
|
struct vfs101_dev *vdev;
|
|
|
|
/* Release private structure */
|
|
vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
|
g_free(vdev);
|
|
|
|
/* Release usb interface */
|
|
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
|
|
|
|
/* Notify close complete */
|
|
fpi_imgdev_close_complete(dev);
|
|
}
|
|
|
|
/* Usb id table of device */
|
|
static const struct usb_id id_table[] =
|
|
{
|
|
{ .vendor = 0x138a, .product = 0x0001 },
|
|
{ 0, 0, 0, },
|
|
};
|
|
|
|
/* Device driver definition */
|
|
struct fp_img_driver vfs101_driver =
|
|
{
|
|
/* Driver specification */
|
|
.driver =
|
|
{
|
|
.id = VFS101_ID,
|
|
.name = FP_COMPONENT,
|
|
.full_name = "Validity VFS101",
|
|
.id_table = id_table,
|
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
|
},
|
|
|
|
/* Image specification */
|
|
.flags = 0,
|
|
.img_width = VFS_IMG_WIDTH,
|
|
.img_height = -1,
|
|
.bz3_threshold = 24,
|
|
|
|
/* Routine specification */
|
|
.open = dev_open,
|
|
.close = dev_close,
|
|
.activate = dev_activate,
|
|
.deactivate = dev_deactivate,
|
|
};
|