Commit c3689665 authored by Andrej Krutak's avatar Andrej Krutak Committed by Bastien Nocera
Browse files
parent 3d222ddd
......@@ -23,7 +23,7 @@ AC_SUBST(lt_major)
AC_SUBST(lt_revision)
AC_SUBST(lt_age)
all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes2501 aes4000 vfs101"
all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes2501 aes4000 vfs101 vfs301"
require_imaging='no'
require_aeslib='no'
......@@ -38,6 +38,7 @@ enable_aes1610='no'
enable_aes2501='no'
enable_aes4000='no'
enable_vfs101='no'
enable_vfs301='no'
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
[List of drivers to enable])],
......@@ -96,6 +97,10 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
AC_DEFINE([ENABLE_VFS101], [], [Build Validity VFS101 driver])
enable_vfs101="yes"
;;
vfs301)
AC_DEFINE([ENABLE_VFS301], [], [Build Validity VFS301/VFS300 driver])
enable_vfs301="yes"
;;
esac
done
......@@ -111,6 +116,7 @@ AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" = "yes"])
AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" = "yes"])
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"])
AM_CONDITIONAL([ENABLE_VFS101], [test "$enable_vfs101" = "yes"])
AM_CONDITIONAL([ENABLE_VFS301], [test "$enable_vfs301" = "yes"])
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
......@@ -298,6 +304,11 @@ if test x$enable_vfs101 != xno ; then
else
AC_MSG_NOTICE([ vfs101 driver disabled])
fi
if test x$enable_vfs301 != xno ; then
AC_MSG_NOTICE([** vfs301 driver enabled])
else
AC_MSG_NOTICE([ vfs301 driver disabled])
fi
if test x$require_aeslib != xno ; then
AC_MSG_NOTICE([** aeslib helper functions enabled])
else
......
......@@ -13,6 +13,7 @@ AES4000_SRC = drivers/aes4000.c
FDU2000_SRC = drivers/fdu2000.c
VCOM5S_SRC = drivers/vcom5s.c
VFS101_SRC = drivers/vfs101.c
VFS301_SRC = drivers/vfs301.c drivers/vfs301_proto.c drivers/vfs301_proto.h drivers/vfs301_proto_fragments.h
EXTRA_DIST = \
$(UPEKE2_SRC) \
......@@ -26,6 +27,7 @@ EXTRA_DIST = \
$(FDU2000_SRC) \
$(VCOM5S_SRC) \
$(VFS101_SRC) \
$(VFS301_SRC) \
aeslib.c aeslib.h \
imagemagick.c \
gdkpixbuf.c \
......@@ -130,6 +132,10 @@ if ENABLE_VFS101
DRIVER_SRC += $(VFS101_SRC)
endif
if ENABLE_VFS301
DRIVER_SRC += $(VFS301_SRC)
endif
if REQUIRE_IMAGEMAGICK
OTHER_SRC += imagemagick.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
......
......@@ -371,6 +371,9 @@ static struct fp_img_driver * const img_drivers[] = {
#ifdef ENABLE_VFS101
&vfs101_driver,
#endif
#ifdef ENABLE_VFS301
&vfs301_driver,
#endif
/*#ifdef ENABLE_UPEKTC
&upektc_driver,
#endif
......
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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 "vfs301"
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include "vfs301_proto.h"
#include <unistd.h>
#include <fp_internal.h>
/************************** GENERIC STUFF *************************************/
/* Callback of asynchronous sleep */
static void async_sleep_cb(void *data)
{
struct fpi_ssm *ssm = data;
fpi_ssm_next_state(ssm);
}
/* Submit asynchronous sleep */
static void async_sleep(unsigned int msec, struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fpi_timeout *timeout;
/* Add timeout */
timeout = fpi_timeout_add(msec, async_sleep_cb, ssm);
if (timeout == NULL) {
/* Failed to add timeout */
fp_err("failed to add timeout");
fpi_imgdev_session_error(dev, -ETIME);
fpi_ssm_mark_aborted(ssm, -ETIME);
}
}
static int submit_image(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
int height;
struct fp_img *img;
#if 0
/* XXX: This is probably handled by libfprint automagically? */
if (vdev->scanline_count < 20) {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
return 0;
}
#endif
img = fpi_img_new(VFS301_FP_OUTPUT_WIDTH * vdev->scanline_count);
if (img == NULL)
return 0;
vfs301_extract_image(vdev, img->data, &height);
/* TODO: how to detect flip? should the resulting image be
* oriented so that it is equal e.g. to a fingerprint on a paper,
* or to the finger when I look at it?) */
img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED;
img->width = VFS301_FP_OUTPUT_WIDTH;
img->height = height;
img = fpi_img_resize(img, img->height * img->width);
fpi_imgdev_image_captured(dev, img);
return 1;
}
/* Loop ssm states */
enum
{
/* Step 0 - Scan finger */
M_REQUEST_PRINT,
M_WAIT_PRINT,
M_CHECK_PRINT,
M_READ_PRINT_START,
M_READ_PRINT_WAIT,
M_READ_PRINT_POLL,
M_SUBMIT_PRINT,
/* Number of states */
M_LOOP_NUM_STATES,
};
/* Exec loop sequential state machine */
static void m_loop_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
switch (ssm->cur_state) {
case M_REQUEST_PRINT:
vfs301_proto_request_fingerprint(dev->udev, vdev);
fpi_ssm_next_state(ssm);
break;
case M_WAIT_PRINT:
/* Wait fingerprint scanning */
async_sleep(200, ssm);
break;
case M_CHECK_PRINT:
if (!vfs301_proto_peek_event(dev->udev, vdev))
fpi_ssm_jump_to_state(ssm, M_WAIT_PRINT);
else
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_START:
fpi_imgdev_report_finger_status(dev, TRUE);
vfs301_proto_process_event_start(dev->udev, vdev);
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_WAIT:
/* Wait fingerprint scanning */
async_sleep(200, ssm);
break;
case M_READ_PRINT_POLL:
{
int rv = vfs301_proto_process_event_poll(dev->udev, vdev);
assert(rv != VFS301_FAILURE);
if (rv == VFS301_ONGOING)
fpi_ssm_jump_to_state(ssm, M_READ_PRINT_WAIT);
else
fpi_ssm_next_state(ssm);
}
break;
case M_SUBMIT_PRINT:
if (submit_image(ssm)) {
fpi_ssm_mark_completed(ssm);
/* NOTE: finger off is expected only after submitting image... */
fpi_imgdev_report_finger_status(dev, FALSE);
} else {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
}
break;
}
}
/* Complete loop sequential state machine */
static void m_loop_complete(struct fpi_ssm *ssm)
{
/* Free sequential state machine */
fpi_ssm_free(ssm);
}
/* Exec init sequential state machine */
static void m_init_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
assert(ssm->cur_state == 0);
vfs301_proto_init(dev->udev, vdev);
fpi_ssm_mark_completed(ssm);
}
/* Complete init sequential state machine */
static void m_init_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fpi_ssm *ssm_loop;
if (!ssm->error) {
/* Notify activate complete */
fpi_imgdev_activate_complete(dev, 0);
/* Start loop ssm */
ssm_loop = fpi_ssm_new(dev->dev, m_loop_state, M_LOOP_NUM_STATES);
ssm_loop->priv = 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 fpi_ssm *ssm;
/* Start init ssm */
ssm = fpi_ssm_new(dev->dev, m_init_state, 1);
ssm->priv = dev;
fpi_ssm_start(ssm, m_init_complete);
return 0;
}
/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *dev)
{
fpi_imgdev_deactivate_complete(dev);
}
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
vfs301_dev_t *vdev = NULL;
int r;
/* Claim usb interface */
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
return r;
}
/* Set enroll stage number */
dev->dev->nr_enroll_stages = 1;
/* Initialize private structure */
vdev = g_malloc0(sizeof(vfs301_dev_t));
dev->priv = vdev;
vdev->scanline_buf = malloc(0);
vdev->scanline_count = 0;
/* Notify open complete */
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_close(struct fp_img_dev *dev)
{
/* Release private structure */
free(((vfs301_dev_t*)dev->priv)->scanline_buf);
g_free(dev->priv);
/* Release usb interface */
libusb_release_interface(dev->udev, 0);
/* Notify close complete */
fpi_imgdev_close_complete(dev);
}
/* Usb id table of device */
static const struct usb_id id_table[] =
{
{ .vendor = 0x138a, .product = 0x0005 /* vfs301 */ },
{ .vendor = 0x138a, .product = 0x0008 /* vfs300 */ },
{ 0, 0, 0, },
};
/* Device driver definition */
struct fp_img_driver vfs301_driver =
{
/* Driver specification */
.driver =
{
.id = 11,
.name = FP_COMPONENT,
.full_name = "Validity VFS301",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
/* Image specification */
.flags = 0,
.img_width = VFS301_FP_WIDTH,
.img_height = -1,
.bz3_threshold = 24,
/* Routine specification */
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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
*/
/*
* TODO:
* - async communication everywhere :)
* - protocol decyphering
* - what is needed and what is redundant
* - is some part of the initial data the firmware?
* - describe some interesting structures better
*/
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include "vfs301_proto.h"
#include "vfs301_proto_fragments.h"
#include <unistd.h>
#define min(a, b) (((a) < (b)) ? (a) : (b))
/************************** USB STUFF *****************************************/
#ifdef DEBUG
static void usb_print_packet(int dir, int rv, const unsigned char *data, int length)
{
fprintf(stderr, "%s, rv %d, len %d\n", dir ? "send" : "recv", rv, length);
#ifdef PRINT_VERBOSE
int i;
for (i = 0; i < min(length, 128); i++) {
fprintf(stderr, "%.2X ", data[i]);
if (i % 8 == 7)
fprintf(stderr, " ");
if (i % 32 == 31)
fprintf(stderr, "\n");
}
#endif
fprintf(stderr, "\n");
}
#endif
static int usb_recv(
vfs301_dev_t *dev,
struct libusb_device_handle *devh, unsigned char endpoint, int max_bytes)
{
assert(max_bytes <= sizeof(dev->recv_buf));
int r = libusb_bulk_transfer(
devh, endpoint,
dev->recv_buf, max_bytes,
&dev->recv_len, VFS301_DEFAULT_WAIT_TIMEOUT
);
#ifdef DEBUG
usb_print_packet(0, r, dev->recv_buf, dev->recv_len);
#endif
if (r < 0)
return r;
return 0;
}
static int usb_send(
struct libusb_device_handle *devh, const unsigned char *data, int length)
{
int transferred = 0;
int r = libusb_bulk_transfer(
devh, VFS301_SEND_ENDPOINT,
(unsigned char *)data, length, &transferred, VFS301_DEFAULT_WAIT_TIMEOUT
);
#ifdef DEBUG
usb_print_packet(1, r, data, length);
#endif
assert(r == 0);
if (r < 0)
return r;
if (transferred < length)
return r;
return 0;
}
/************************** OUT MESSAGES GENERATION ***************************/
static void vfs301_proto_generate_0B(int subtype, unsigned char *data, int *len)
{
*data = 0x0B;
*len = 1;
data++;
memset(data, 0, 39);
*len += 38;
data[20] = subtype;
switch (subtype) {
case 0x04:
data[34] = 0x9F;
break;
case 0x05:
data[34] = 0xAB;
len++;
break;
default:
assert(!"unsupported");
break;
}
}
#define HEX_TO_INT(c) \
(((c) >= '0' && (c) <= '9') ? ((c) - '0') : ((c) - 'A' + 10))
static void translate_str(const char **srcL, unsigned char *data, int *len)
{
const char *src;
unsigned char *dataOrig = data;
while (*srcL != NULL) {
src = *srcL;
while (*src != '\0') {
assert(*src != '\0');
assert(*(src +1) != '\0');
*data = (unsigned char)((HEX_TO_INT(*src) << 4) | (HEX_TO_INT(*(src + 1))));
data++;
src += 2;
}
srcL++;
}
*len = data - dataOrig;
}
static void vfs301_proto_generate(int type, int subtype, unsigned char *data, int *len)
{
switch (type) {
case 0x01:
case 0x04:
/* After cmd 0x04 is sent, a data is received on VALIDITY_RECEIVE_ENDPOINT_CTRL.
* If it is 0x0000:
* additional 64B and 224B are read from _DATA, then vfs301_next_scan_FA00 is
* sent, 0000 received from _CTRL, and then continue with wait loop
* If it is 0x1204:
* => reinit?
*/
case 0x17:
case 0x19:
case 0x1A:
*data = type;
*len = 1;
break;
case 0x0B:
vfs301_proto_generate_0B(subtype, data, len);
break;
case 0x02D0:
{
const char **dataLs[] = {
vfs301_02D0_01,
vfs301_02D0_02,
vfs301_02D0_03,
vfs301_02D0_04,
vfs301_02D0_05,
vfs301_02D0_06,
vfs301_02D0_07,
};
assert((int)subtype <= (int)(sizeof(dataLs) / sizeof(dataLs[0])));
translate_str(dataLs[subtype - 1], data, len);
}
break;
case 0x0220:
switch (subtype) {
case 1:
translate_str(vfs301_0220_01, data, len);
break;
case 2:
translate_str(vfs301_0220_02, data, len);
break;
case 3:
translate_str(vfs301_0220_03, data, len);
break;
case 0xFA00:
case 0x2C01:
case 0x5E01:
translate_str(vfs301_next_scan_template, data, len);
unsigned char *field = data + *len - (sizeof(S4_TAIL) - 1) / 2 - 4;
assert(*field == 0xDE);
assert(*(field + 1) == 0xAD);
assert(*(field + 2) == 0xDE);
assert(*(field + 3) == 0xAD);
*field = (unsigned char)((subtype >> 8) & 0xFF);
*(field + 1) = (unsigned char)(subtype & 0xFF);
*(field + 2) = *field;
*(field + 3) = *(field + 1);
break;
default:
assert(0);
break;
}
break;
case 0x06:
assert(!"Not generated");
break;
default:
assert(!"Unknown message type");
break;
}
}