Commit c9ff2c55 authored by Daniel Drake's avatar Daniel Drake

Add Veridicom 5th Sense driver

Based on bus traffic analysis of the windows driver. This device is
working well, but finger presence detection is not yet implemented.
parent 617ca8cd
......@@ -7,8 +7,9 @@ AES1610_SRC = drivers/aes1610.c
AES2501_SRC = drivers/aes2501.c drivers/aes2501.h
AES4000_SRC = drivers/aes4000.c
FDU2000_SRC = drivers/fdu2000.c
VCOM5S_SRC = drivers/vcom5s.c
DRIVER_SRC = $(UPEKTS_SRC) $(AES4000_SRC) $(AES2501_SRC) $(URU4000_SRC)
DRIVER_SRC = $(UPEKTS_SRC) $(AES4000_SRC) $(AES2501_SRC) $(URU4000_SRC) $(VCOM5S_SRC)
#DRIVER_SRC = $(AES1610_SRC) $(UPEKTC_SRC) $(FDU2000_SRC)
NBIS_SRC = \
......
......@@ -333,6 +333,7 @@ static struct fp_img_driver * const img_drivers[] = {
&aes4000_driver,
&aes2501_driver,
&uru4000_driver,
&vcom5s_driver,
/* &aes1610_driver,
&upektc_driver,
&fdu2000_driver, */
......
/*
* Veridicom 5thSense driver for libfprint
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* 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 "vcom5s"
/* TODO:
* finger presence detection
* calibration?
*/
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <libusb.h>
#include <fp_internal.h>
#define CTRL_IN 0xc0
#define CTRL_OUT 0x40
#define CTRL_TIMEOUT 1000
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define IMG_WIDTH 300
#define IMG_HEIGHT 288
#define ROWS_PER_RQ 12
#define NR_REQS (IMG_HEIGHT / ROWS_PER_RQ)
#define RQ_SIZE (IMG_WIDTH * ROWS_PER_RQ)
#define IMG_SIZE (IMG_WIDTH * IMG_HEIGHT)
struct v5s_dev {
int capture_iteration;
struct fp_img *capture_img;
gboolean loop_running;
gboolean deactivating;
};
/***** REGISTER I/O *****/
static void sm_write_reg_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_aborted(ssm, -EIO);
else
fpi_ssm_next_state(ssm);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void sm_write_reg(struct fpi_ssm *ssm, unsigned char reg,
unsigned char value)
{
struct fp_img_dev *dev = ssm->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer();
unsigned char *data;
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
fp_dbg("set %02x=%02x", reg, value);
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(data, CTRL_OUT, reg, value, 0, 0);
libusb_fill_control_transfer(transfer, dev->udev, data, sm_write_reg_cb,
ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
static void sm_exec_cmd_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_aborted(ssm, -EIO);
else
fpi_ssm_next_state(ssm);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void sm_exec_cmd(struct fpi_ssm *ssm, unsigned char cmd,
unsigned char param)
{
struct fp_img_dev *dev = ssm->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer();
unsigned char *data;
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
fp_dbg("cmd %02x param %02x", cmd, param);
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(data, CTRL_IN, cmd, param, 0, 0);
libusb_fill_control_transfer(transfer, dev->udev, data, sm_exec_cmd_cb,
ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
/***** IMAGE ACQUISITION *****/
static void capture_iterate(struct fpi_ssm *ssm);
static void capture_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
if (++vdev->capture_iteration == NR_REQS) {
struct fp_img *img = vdev->capture_img;
/* must clear this early, otherwise the call chain takes us into
* loopsm_complete where we would free it, when in fact we are
* supposed to be handing off this image */
vdev->capture_img = NULL;
fpi_imgdev_report_finger_status(dev, TRUE);
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_next_state(ssm);
} else {
capture_iterate(ssm);
}
out:
libusb_free_transfer(transfer);
}
static void capture_iterate(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
int iteration = vdev->capture_iteration;
struct libusb_transfer *transfer = libusb_alloc_transfer();
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN,
vdev->capture_img->data + (RQ_SIZE * iteration), RQ_SIZE,
capture_cb, ssm, CTRL_TIMEOUT);
transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
static void sm_do_capture(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
fp_dbg("");
vdev->capture_img = fpi_img_new_for_imgdev(dev);
vdev->capture_iteration = 0;
capture_iterate(ssm);
}
/***** CAPTURE LOOP *****/
enum loop_states {
LOOP_WR02,
LOOP_WR03,
LOOP_CMDC1,
LOOP_CAPTURE,
LOOP_CAPTURE_DONE,
LOOP_NUM_STATES,
};
static void loop_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
switch (ssm->cur_state) {
case LOOP_WR02:
sm_write_reg(ssm, 0x02, 0x02);
break;
case LOOP_WR03:
sm_write_reg(ssm, 0x03, 0x29);
break;
case LOOP_CMDC1:
if (vdev->deactivating) {
fp_dbg("deactivating, marking completed");
fpi_ssm_mark_completed(ssm);
} else
sm_exec_cmd(ssm, 0xc1, 0x00);
break;
case LOOP_CAPTURE:
sm_do_capture(ssm);
break;
case LOOP_CAPTURE_DONE:
fpi_ssm_jump_to_state(ssm, LOOP_CMDC1);
break;
}
}
static void loopsm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
int r = ssm->error;
fpi_ssm_free(ssm);
fp_img_free(vdev->capture_img);
vdev->capture_img = NULL;
vdev->loop_running = FALSE;
if (r)
fpi_imgdev_session_error(dev, r);
if (vdev->deactivating)
fpi_imgdev_deactivate_complete(dev);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct v5s_dev *vdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, loop_run_state,
LOOP_NUM_STATES);
ssm->priv = dev;
vdev->deactivating = FALSE;
fpi_ssm_start(ssm, loopsm_complete);
vdev->loop_running = TRUE;
fpi_imgdev_activate_complete(dev, 0);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct v5s_dev *vdev = dev->priv;
if (vdev->loop_running)
vdev->deactivating = TRUE;
else
fpi_imgdev_deactivate_complete(dev);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
dev->priv = g_malloc0(sizeof(struct v5s_dev));
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
fp_err("could not claim interface 0");
if (r == 0)
fpi_imgdev_open_complete(dev, 0);
return r;
}
static void dev_deinit(struct fp_img_dev *dev)
{
g_free(dev->priv);
libusb_release_interface(dev->udev, 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x061a, .product = 0x0110 },
{ 0, 0, 0, },
};
struct fp_img_driver vcom5s_driver = {
.driver = {
.id = 8,
.name = FP_COMPONENT,
.full_name = "Veridicom 5thSense",
.id_table = id_table,
},
.flags = 0,
.img_height = IMG_HEIGHT,
.img_width = IMG_WIDTH,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};
......@@ -240,6 +240,7 @@ extern struct fp_img_driver aes1610_driver;
extern struct fp_img_driver aes2501_driver;
extern struct fp_img_driver aes4000_driver;
extern struct fp_img_driver fdu2000_driver;
extern struct fp_img_driver vcom5s_driver;
extern GSList *opened_devices;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment