lib: Add VFS300/VFS301 driver
https://bugs.freedesktop.org/show_bug.cgi?id=37574
This commit is contained in:
parent
3d222ddda7
commit
c3689665db
8 changed files with 3737 additions and 1 deletions
13
configure.ac
13
configure.ac
|
@ -23,7 +23,7 @@ AC_SUBST(lt_major)
|
||||||
AC_SUBST(lt_revision)
|
AC_SUBST(lt_revision)
|
||||||
AC_SUBST(lt_age)
|
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_imaging='no'
|
||||||
require_aeslib='no'
|
require_aeslib='no'
|
||||||
|
@ -38,6 +38,7 @@ enable_aes1610='no'
|
||||||
enable_aes2501='no'
|
enable_aes2501='no'
|
||||||
enable_aes4000='no'
|
enable_aes4000='no'
|
||||||
enable_vfs101='no'
|
enable_vfs101='no'
|
||||||
|
enable_vfs301='no'
|
||||||
|
|
||||||
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
|
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
|
||||||
[List of drivers to enable])],
|
[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])
|
AC_DEFINE([ENABLE_VFS101], [], [Build Validity VFS101 driver])
|
||||||
enable_vfs101="yes"
|
enable_vfs101="yes"
|
||||||
;;
|
;;
|
||||||
|
vfs301)
|
||||||
|
AC_DEFINE([ENABLE_VFS301], [], [Build Validity VFS301/VFS300 driver])
|
||||||
|
enable_vfs301="yes"
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -111,6 +116,7 @@ AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" = "yes"])
|
||||||
AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" = "yes"])
|
AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" = "yes"])
|
||||||
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"])
|
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"])
|
||||||
AM_CONDITIONAL([ENABLE_VFS101], [test "$enable_vfs101" = "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])
|
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
|
||||||
|
@ -298,6 +304,11 @@ if test x$enable_vfs101 != xno ; then
|
||||||
else
|
else
|
||||||
AC_MSG_NOTICE([ vfs101 driver disabled])
|
AC_MSG_NOTICE([ vfs101 driver disabled])
|
||||||
fi
|
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
|
if test x$require_aeslib != xno ; then
|
||||||
AC_MSG_NOTICE([** aeslib helper functions enabled])
|
AC_MSG_NOTICE([** aeslib helper functions enabled])
|
||||||
else
|
else
|
||||||
|
|
|
@ -13,6 +13,7 @@ AES4000_SRC = drivers/aes4000.c
|
||||||
FDU2000_SRC = drivers/fdu2000.c
|
FDU2000_SRC = drivers/fdu2000.c
|
||||||
VCOM5S_SRC = drivers/vcom5s.c
|
VCOM5S_SRC = drivers/vcom5s.c
|
||||||
VFS101_SRC = drivers/vfs101.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 = \
|
EXTRA_DIST = \
|
||||||
$(UPEKE2_SRC) \
|
$(UPEKE2_SRC) \
|
||||||
|
@ -26,6 +27,7 @@ EXTRA_DIST = \
|
||||||
$(FDU2000_SRC) \
|
$(FDU2000_SRC) \
|
||||||
$(VCOM5S_SRC) \
|
$(VCOM5S_SRC) \
|
||||||
$(VFS101_SRC) \
|
$(VFS101_SRC) \
|
||||||
|
$(VFS301_SRC) \
|
||||||
aeslib.c aeslib.h \
|
aeslib.c aeslib.h \
|
||||||
imagemagick.c \
|
imagemagick.c \
|
||||||
gdkpixbuf.c \
|
gdkpixbuf.c \
|
||||||
|
@ -130,6 +132,10 @@ if ENABLE_VFS101
|
||||||
DRIVER_SRC += $(VFS101_SRC)
|
DRIVER_SRC += $(VFS101_SRC)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if ENABLE_VFS301
|
||||||
|
DRIVER_SRC += $(VFS301_SRC)
|
||||||
|
endif
|
||||||
|
|
||||||
if REQUIRE_IMAGEMAGICK
|
if REQUIRE_IMAGEMAGICK
|
||||||
OTHER_SRC += imagemagick.c
|
OTHER_SRC += imagemagick.c
|
||||||
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
|
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
|
||||||
|
|
|
@ -371,6 +371,9 @@ static struct fp_img_driver * const img_drivers[] = {
|
||||||
#ifdef ENABLE_VFS101
|
#ifdef ENABLE_VFS101
|
||||||
&vfs101_driver,
|
&vfs101_driver,
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_VFS301
|
||||||
|
&vfs301_driver,
|
||||||
|
#endif
|
||||||
/*#ifdef ENABLE_UPEKTC
|
/*#ifdef ENABLE_UPEKTC
|
||||||
&upektc_driver,
|
&upektc_driver,
|
||||||
#endif
|
#endif
|
||||||
|
|
306
libfprint/drivers/vfs301.c
Normal file
306
libfprint/drivers/vfs301.c
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
/*
|
||||||
|
* 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,
|
||||||
|
};
|
639
libfprint/drivers/vfs301_proto.c
Normal file
639
libfprint/drivers/vfs301_proto.c
Normal file
|
@ -0,0 +1,639 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************** SCAN IMAGE PROCESSING *****************************/
|
||||||
|
|
||||||
|
#ifdef SCAN_FINISH_DETECTION
|
||||||
|
static int img_is_finished_scan(fp_line_t *lines, int no_lines)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
int rv = 1;
|
||||||
|
|
||||||
|
for (i = no_lines - VFS301_FP_SUM_LINES; i < no_lines; i++) {
|
||||||
|
/* check the line for fingerprint data */
|
||||||
|
for (j = 0; j < sizeof(lines[i].sum2); j++) {
|
||||||
|
if (lines[i].sum2[j] > (VFS301_FP_SUM_MEDIAN + VFS301_FP_SUM_EMPTY_RANGE))
|
||||||
|
rv = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int scanline_diff(const unsigned char *scanlines, int prev, int cur)
|
||||||
|
{
|
||||||
|
const unsigned char *line1 = scanlines + prev * VFS301_FP_OUTPUT_WIDTH;
|
||||||
|
const unsigned char *line2 = scanlines + cur * VFS301_FP_OUTPUT_WIDTH;
|
||||||
|
int i;
|
||||||
|
int diff;
|
||||||
|
|
||||||
|
#ifdef OUTPUT_RAW
|
||||||
|
/* We only need the image, not the surrounding stuff. */
|
||||||
|
line1 = ((vfs301_line_t*)line1)->scan;
|
||||||
|
line2 = ((vfs301_line_t*)line2)->scan;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* TODO: This doesn't work too well when there are parallel lines in the
|
||||||
|
* fingerprint. */
|
||||||
|
for (diff = 0, i = 0; i < VFS301_FP_WIDTH; i++) {
|
||||||
|
if (*line1 > *line2)
|
||||||
|
diff += *line1 - *line2;
|
||||||
|
else
|
||||||
|
diff += *line2 - *line1;
|
||||||
|
|
||||||
|
line1++;
|
||||||
|
line2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((diff / VFS301_FP_WIDTH) > VFS301_FP_LINE_DIFF_THRESHOLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transform the input data to a normalized fingerprint scan */
|
||||||
|
void vfs301_extract_image(
|
||||||
|
vfs301_dev_t *vfs, unsigned char *output, int *output_height
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const unsigned char *scanlines = vfs->scanline_buf;
|
||||||
|
int last_line;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(vfs->scanline_count >= 1);
|
||||||
|
|
||||||
|
*output_height = 1;
|
||||||
|
memcpy(output, scanlines, VFS301_FP_OUTPUT_WIDTH);
|
||||||
|
last_line = 0;
|
||||||
|
|
||||||
|
/* The following algorithm is quite trivial - it just picks lines that
|
||||||
|
* differ more than VFS301_FP_LINE_DIFF_THRESHOLD.
|
||||||
|
* TODO: A nicer approach would be to pick those lines and then do some kind
|
||||||
|
* of bi/tri-linear resampling to get the output (so that we don't get so
|
||||||
|
* many false edges etc.).
|
||||||
|
*/
|
||||||
|
for (i = 1; i < vfs->scanline_count; i++) {
|
||||||
|
if (scanline_diff(scanlines, last_line, i)) {
|
||||||
|
memcpy(
|
||||||
|
output + VFS301_FP_OUTPUT_WIDTH * (*output_height),
|
||||||
|
scanlines + VFS301_FP_OUTPUT_WIDTH * i,
|
||||||
|
VFS301_FP_OUTPUT_WIDTH
|
||||||
|
);
|
||||||
|
last_line = i;
|
||||||
|
(*output_height)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int img_process_data(
|
||||||
|
int first_block, vfs301_dev_t *dev, const unsigned char *buf, int len
|
||||||
|
)
|
||||||
|
{
|
||||||
|
vfs301_line_t *lines = (vfs301_line_t*)buf;
|
||||||
|
int no_lines = len / sizeof(vfs301_line_t);
|
||||||
|
int i;
|
||||||
|
/*int no_nonempty;*/
|
||||||
|
unsigned char *cur_line;
|
||||||
|
int last_img_height;
|
||||||
|
#ifdef SCAN_FINISH_DETECTION
|
||||||
|
int finished_scan;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (first_block) {
|
||||||
|
last_img_height = 0;
|
||||||
|
dev->scanline_count = no_lines;
|
||||||
|
} else {
|
||||||
|
last_img_height = dev->scanline_count;
|
||||||
|
dev->scanline_count += no_lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->scanline_buf = realloc(dev->scanline_buf, dev->scanline_count * VFS301_FP_OUTPUT_WIDTH);
|
||||||
|
assert(dev->scanline_buf != NULL);
|
||||||
|
|
||||||
|
for (cur_line = dev->scanline_buf + last_img_height * VFS301_FP_OUTPUT_WIDTH, i = 0;
|
||||||
|
i < no_lines;
|
||||||
|
i++, cur_line += VFS301_FP_OUTPUT_WIDTH
|
||||||
|
) {
|
||||||
|
#ifndef OUTPUT_RAW
|
||||||
|
memcpy(cur_line, lines[i].scan, VFS301_FP_OUTPUT_WIDTH);
|
||||||
|
#else
|
||||||
|
memcpy(cur_line, &lines[i], VFS301_FP_OUTPUT_WIDTH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SCAN_FINISH_DETECTION
|
||||||
|
finished_scan = img_is_finished_scan(lines, no_lines);
|
||||||
|
|
||||||
|
return !finished_scan;
|
||||||
|
#else /* SCAN_FINISH_DETECTION */
|
||||||
|
return 1; /* Just continue until data is coming */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************** PROTOCOL STUFF ************************************/
|
||||||
|
|
||||||
|
static unsigned char usb_send_buf[0x2000];
|
||||||
|
|
||||||
|
#define USB_RECV(from, len) \
|
||||||
|
usb_recv(dev, devh, from, len)
|
||||||
|
|
||||||
|
#define USB_SEND(type, subtype) \
|
||||||
|
{ \
|
||||||
|
int len; \
|
||||||
|
vfs301_proto_generate(type, subtype, usb_send_buf, &len); \
|
||||||
|
usb_send(devh, usb_send_buf, len); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RAW_DATA(x) x, sizeof(x)
|
||||||
|
|
||||||
|
#define IS_VFS301_FP_SEQ_START(b) ((b[0] == 0x01) && (b[1] == 0xfe))
|
||||||
|
|
||||||
|
static int vfs301_proto_process_data(int first_block, vfs301_dev_t *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const unsigned char *buf = dev->recv_buf;
|
||||||
|
int len = dev->recv_len;
|
||||||
|
|
||||||
|
if (first_block) {
|
||||||
|
assert(len >= VFS301_FP_FRAME_SIZE);
|
||||||
|
|
||||||
|
/* Skip bytes until start_sequence is found */
|
||||||
|
for (i = 0; i < VFS301_FP_FRAME_SIZE; i++, buf++, len--) {
|
||||||
|
if (IS_VFS301_FP_SEQ_START(buf))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img_process_data(first_block, dev, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vfs301_proto_request_fingerprint(
|
||||||
|
struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||||
|
{
|
||||||
|
USB_SEND(0x0220, 0xFA00);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 000000000000 */
|
||||||
|
}
|
||||||
|
|
||||||
|
int vfs301_proto_peek_event(
|
||||||
|
struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||||
|
{
|
||||||
|
const char no_event[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
const char got_event[] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
USB_SEND(0x17, -1);
|
||||||
|
assert(USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7) == 0);
|
||||||
|
|
||||||
|
if (memcmp(dev->recv_buf, no_event, sizeof(no_event)) == 0) {
|
||||||
|
return 0;
|
||||||
|
} else if (memcmp(dev->recv_buf, got_event, sizeof(no_event)) == 0) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
assert(!"unexpected reply to wait");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VARIABLE_ORDER(a, b) \
|
||||||
|
{ \
|
||||||
|
int _rv = a;\
|
||||||
|
b; \
|
||||||
|
if (_rv == -7) \
|
||||||
|
a; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vfs301_proto_process_event_cb(struct libusb_transfer *transfer)
|
||||||
|
{
|
||||||
|
vfs301_dev_t *dev = transfer->user_data;
|
||||||
|
struct libusb_device_handle *devh = transfer->dev_handle;
|
||||||
|
|
||||||
|
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||||
|
dev->recv_progress = VFS301_FAILURE;
|
||||||
|
goto end;
|
||||||
|
} else if (transfer->actual_length < dev->recv_exp_amt) {
|
||||||
|
/* TODO: process the data anyway? */
|
||||||
|
dev->recv_progress = VFS301_ENDED;
|
||||||
|
goto end;
|
||||||
|
} else {
|
||||||
|
dev->recv_len = transfer->actual_length;
|
||||||
|
if (!vfs301_proto_process_data(dev->recv_exp_amt == VFS301_FP_RECV_LEN_1, dev)) {
|
||||||
|
dev->recv_progress = VFS301_ENDED;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->recv_exp_amt = VFS301_FP_RECV_LEN_2;
|
||||||
|
libusb_fill_bulk_transfer(
|
||||||
|
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
|
||||||
|
dev->recv_buf, dev->recv_exp_amt,
|
||||||
|
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
|
||||||
|
|
||||||
|
if (libusb_submit_transfer(transfer) < 0) {
|
||||||
|
printf("cb::continue fail\n");
|
||||||
|
dev->recv_progress = VFS301_FAILURE;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
libusb_free_transfer(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vfs301_proto_process_event_start(
|
||||||
|
struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||||
|
{
|
||||||
|
struct libusb_transfer *transfer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notes:
|
||||||
|
*
|
||||||
|
* seen next_scan order:
|
||||||
|
* o FA00
|
||||||
|
* o FA00
|
||||||
|
* o 2C01
|
||||||
|
* o FA00
|
||||||
|
* o FA00
|
||||||
|
* o 2C01
|
||||||
|
* o FA00
|
||||||
|
* o FA00
|
||||||
|
* o 2C01
|
||||||
|
* o 5E01 !?
|
||||||
|
* o FA00
|
||||||
|
* o FA00
|
||||||
|
* o 2C01
|
||||||
|
* o FA00
|
||||||
|
* o FA00
|
||||||
|
* o 2C01
|
||||||
|
*/
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 64);
|
||||||
|
|
||||||
|
/* now read the fingerprint data, while there are some */
|
||||||
|
transfer = libusb_alloc_transfer(0);
|
||||||
|
if (!transfer) {
|
||||||
|
dev->recv_progress = VFS301_FAILURE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->recv_progress = VFS301_ONGOING;
|
||||||
|
dev->recv_exp_amt = VFS301_FP_RECV_LEN_1;
|
||||||
|
|
||||||
|
libusb_fill_bulk_transfer(
|
||||||
|
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
|
||||||
|
dev->recv_buf, dev->recv_exp_amt,
|
||||||
|
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
|
||||||
|
|
||||||
|
if (libusb_submit_transfer(transfer) < 0) {
|
||||||
|
libusb_free_transfer(transfer);
|
||||||
|
dev->recv_progress = VFS301_FAILURE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int /* vfs301_dev_t::recv_progress */ vfs301_proto_process_event_poll(
|
||||||
|
struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||||
|
{
|
||||||
|
if (dev->recv_progress != VFS301_ENDED)
|
||||||
|
return dev->recv_progress;
|
||||||
|
|
||||||
|
/* Finish the scan process... */
|
||||||
|
|
||||||
|
USB_SEND(0x04, -1);
|
||||||
|
/* the following may come in random order, data may not come at all, don't
|
||||||
|
* try for too long... */
|
||||||
|
VARIABLE_ORDER(
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 1204 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 16384)
|
||||||
|
);
|
||||||
|
|
||||||
|
USB_SEND(0x0220, 2);
|
||||||
|
VARIABLE_ORDER(
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760), /* seems to always come */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2) /* 0000 */
|
||||||
|
);
|
||||||
|
|
||||||
|
return dev->recv_progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||||
|
{
|
||||||
|
USB_SEND(0x01, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
|
||||||
|
USB_SEND(0x0B, 0x04);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 6); /* 000000000000 */
|
||||||
|
USB_SEND(0x0B, 0x05);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7); /* 00000000000000 */
|
||||||
|
USB_SEND(0x19, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 64);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 4); /* 6BB4D0BC */
|
||||||
|
usb_send(devh, RAW_DATA(vfs301_06_1));
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
|
||||||
|
USB_SEND(0x01, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
|
||||||
|
USB_SEND(0x1A, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
usb_send(devh, RAW_DATA(vfs301_06_2));
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_SEND(0x0220, 1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 256);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 32);
|
||||||
|
|
||||||
|
USB_SEND(0x1A, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
usb_send(devh, RAW_DATA(vfs301_06_3));
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
|
||||||
|
USB_SEND(0x01, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
|
||||||
|
USB_SEND(0x02D0, 1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 11648); /* 56 * vfs301_init_line_t[] */
|
||||||
|
USB_SEND(0x02D0, 2);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 53248); /* 2 * 128 * vfs301_init_line_t[] */
|
||||||
|
USB_SEND(0x02D0, 3);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 19968); /* 96 * vfs301_init_line_t[] */
|
||||||
|
USB_SEND(0x02D0, 4);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5824); /* 28 * vfs301_init_line_t[] */
|
||||||
|
USB_SEND(0x02D0, 5);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
|
||||||
|
USB_SEND(0x02D0, 6);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
|
||||||
|
USB_SEND(0x02D0, 7);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 832);
|
||||||
|
usb_send(devh, RAW_DATA(vfs301_12));
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
|
||||||
|
USB_SEND(0x1A, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
usb_send(devh, RAW_DATA(vfs301_06_2));
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
USB_SEND(0x0220, 2);
|
||||||
|
VARIABLE_ORDER(
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 0000 */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760)
|
||||||
|
);
|
||||||
|
|
||||||
|
USB_SEND(0x1A, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
usb_send(devh, RAW_DATA(vfs301_06_1));
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
|
||||||
|
USB_SEND(0x1A, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
usb_send(devh, RAW_DATA(vfs301_06_4));
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
usb_send(devh, RAW_DATA(vfs301_24)); /* turns on white */
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||||
|
|
||||||
|
USB_SEND(0x01, -1);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
|
||||||
|
USB_SEND(0x0220, 3);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2368);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 36);
|
||||||
|
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||||
|
{
|
||||||
|
}
|
137
libfprint/drivers/vfs301_proto.h
Normal file
137
libfprint/drivers/vfs301_proto.h
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
#include <libusb-1.0/libusb.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
VFS301_DEFAULT_WAIT_TIMEOUT = 300,
|
||||||
|
|
||||||
|
VFS301_SEND_ENDPOINT = 0x01,
|
||||||
|
VFS301_RECEIVE_ENDPOINT_CTRL = 0x81,
|
||||||
|
VFS301_RECEIVE_ENDPOINT_DATA = 0x82
|
||||||
|
};
|
||||||
|
|
||||||
|
#define VFS301_FP_RECV_LEN_1 (84032)
|
||||||
|
#define VFS301_FP_RECV_LEN_2 (84096)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* buffer for received data */
|
||||||
|
unsigned char recv_buf[0x20000];
|
||||||
|
int recv_len;
|
||||||
|
|
||||||
|
/* buffer to hold raw scanlines */
|
||||||
|
unsigned char *scanline_buf;
|
||||||
|
int scanline_count;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
VFS301_ONGOING = 0,
|
||||||
|
VFS301_ENDED = 1,
|
||||||
|
VFS301_FAILURE = -1
|
||||||
|
} recv_progress;
|
||||||
|
int recv_exp_amt;
|
||||||
|
} vfs301_dev_t;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* Width of the scanned data in px */
|
||||||
|
VFS301_FP_WIDTH = 200,
|
||||||
|
|
||||||
|
/* sizeof(fp_line_t) */
|
||||||
|
VFS301_FP_FRAME_SIZE = 288,
|
||||||
|
/* Width of output line */
|
||||||
|
#ifndef OUTPUT_RAW
|
||||||
|
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_WIDTH,
|
||||||
|
#else
|
||||||
|
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_FRAME_SIZE,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VFS301_FP_SUM_LINES = 3,
|
||||||
|
|
||||||
|
#ifdef SCAN_FINISH_DETECTION
|
||||||
|
/* TODO: The following changes (seen ~60 and ~80) In that
|
||||||
|
* case we'll need to calibrate this from empty data somehow... */
|
||||||
|
VFS301_FP_SUM_MEDIAN = 60,
|
||||||
|
VFS301_FP_SUM_EMPTY_RANGE = 5,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Minimum average difference between returned lines */
|
||||||
|
VFS301_FP_LINE_DIFF_THRESHOLD = 15,
|
||||||
|
|
||||||
|
/* Maximum waiting time for a single fingerprint frame */
|
||||||
|
VFS301_FP_RECV_TIMEOUT = 2000
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Arrays of this structure is returned during the initialization as a response
|
||||||
|
* to the 0x02D0 messages.
|
||||||
|
* It seems to be always the same - what is it for? Some kind of confirmation?
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
unsigned char sync_0x01;
|
||||||
|
unsigned char sync_0xfe;
|
||||||
|
|
||||||
|
unsigned char counter_lo;
|
||||||
|
unsigned char counter_hi; /* FIXME ? */
|
||||||
|
|
||||||
|
unsigned char flags[3];
|
||||||
|
|
||||||
|
unsigned char sync_0x00;
|
||||||
|
|
||||||
|
unsigned char scan[VFS301_FP_WIDTH];
|
||||||
|
} vfs301_init_line_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned char sync_0x01;
|
||||||
|
unsigned char sync_0xfe;
|
||||||
|
|
||||||
|
unsigned char counter_lo;
|
||||||
|
unsigned char counter_hi;
|
||||||
|
|
||||||
|
unsigned char sync_0x08[2]; /* XXX: always? 0x08 0x08 */
|
||||||
|
/* 0x08 | 0x18 - Looks like 0x08 marks good quality lines */
|
||||||
|
unsigned char flag_1;
|
||||||
|
unsigned char sync_0x00;
|
||||||
|
|
||||||
|
unsigned char scan[VFS301_FP_WIDTH];
|
||||||
|
|
||||||
|
/* A offseted, stretched, inverted copy of scan... probably could
|
||||||
|
* serve finger motion speed detection?
|
||||||
|
* Seems to be subdivided to some 10B + 53B + 1B blocks */
|
||||||
|
unsigned char mirror[64];
|
||||||
|
|
||||||
|
/* Some kind of sum of the scan, very low contrast */
|
||||||
|
unsigned char sum1[2];
|
||||||
|
unsigned char sum2[11];
|
||||||
|
unsigned char sum3[3];
|
||||||
|
} vfs301_line_t;
|
||||||
|
|
||||||
|
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||||
|
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||||
|
|
||||||
|
void vfs301_proto_request_fingerprint(
|
||||||
|
struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||||
|
|
||||||
|
/** returns 0 if no event is ready, or 1 if there is one... */
|
||||||
|
int vfs301_proto_peek_event(
|
||||||
|
struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||||
|
void vfs301_proto_process_event_start(
|
||||||
|
struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||||
|
int vfs301_proto_process_event_poll(
|
||||||
|
struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||||
|
|
||||||
|
void vfs301_extract_image(vfs301_dev_t *vfs, unsigned char *output, int *output_height);
|
2631
libfprint/drivers/vfs301_proto_fragments.h
Normal file
2631
libfprint/drivers/vfs301_proto_fragments.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -268,6 +268,9 @@ extern struct fp_img_driver vcom5s_driver;
|
||||||
#ifdef ENABLE_VFS101
|
#ifdef ENABLE_VFS101
|
||||||
extern struct fp_img_driver vfs101_driver;
|
extern struct fp_img_driver vfs101_driver;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_VFS301
|
||||||
|
extern struct fp_img_driver vfs301_driver;
|
||||||
|
#endif
|
||||||
|
|
||||||
extern libusb_context *fpi_usb_ctx;
|
extern libusb_context *fpi_usb_ctx;
|
||||||
extern GSList *opened_devices;
|
extern GSList *opened_devices;
|
||||||
|
|
Loading…
Reference in a new issue