virtual_misdev: Add virtual match-in-sensor device with internal storage
We will need new API for these devices such as deleting prints from the device. This is a basic initial driver only implementing the currently available API allowing tests and further development.
This commit is contained in:
parent
8cdeeeaaf1
commit
9982916294
4 changed files with 380 additions and 2 deletions
|
@ -43,6 +43,7 @@ enum {
|
|||
VFS0050_ID = 20,
|
||||
ELAN_ID = 21,
|
||||
VIRTUAL_IMG_ID = 22,
|
||||
VIRTUAL_MIS_ID = 23,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
374
libfprint/drivers/virtual_misdev.c
Normal file
374
libfprint/drivers/virtual_misdev.c
Normal file
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* Virtual match-in-sensor device with internal storage
|
||||
*
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.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
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a virtual driver to debug features that are relevant for
|
||||
* match-in-sensor (MIS) devices that store data on the sensor itself.
|
||||
* In this case data needs to be deleted both locally and from the device
|
||||
* and we should support garbage collection.
|
||||
*
|
||||
* The protocol is line based, when a verify/enroll/etc. command is started
|
||||
* (or is active when connecting) then we send the command and the UUID
|
||||
* terminated by a newline.
|
||||
*
|
||||
* IDLE\n
|
||||
* VERIFY UUID\n
|
||||
* ENROLL UUID\n
|
||||
* DELETE UUID\n (planned)
|
||||
* LIST (planned)
|
||||
*
|
||||
* The other end simply responds with an integer (terminated by newline)
|
||||
* that matches the internal fprint return codes.
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "virtual_misdev"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include "drivers_api.h"
|
||||
#include "fpi-async.h"
|
||||
|
||||
#define VIRT_ENROLL_STAGES 1
|
||||
|
||||
enum virtdev_state {
|
||||
STATE_IDLE = 0,
|
||||
STATE_VERIFY,
|
||||
STATE_ENROLL,
|
||||
};
|
||||
|
||||
struct virt_dev {
|
||||
enum virtdev_state state;
|
||||
|
||||
gchar *curr_uuid;
|
||||
|
||||
fpi_io_condition *socket_io_cond;
|
||||
fpi_io_condition *client_io_cond;
|
||||
|
||||
gint socket_fd;
|
||||
gint client_fd;
|
||||
|
||||
gssize recv_len;
|
||||
guchar *recv_buf;
|
||||
};
|
||||
|
||||
static void
|
||||
handle_response (struct fp_dev *dev, guchar *buf, gssize len)
|
||||
{
|
||||
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
|
||||
gint result = atoi ((gchar*) buf);
|
||||
|
||||
switch (virt->state) {
|
||||
case STATE_IDLE:
|
||||
fp_info ("Received unexpected status code %i\n", virt->state);
|
||||
break;
|
||||
case STATE_VERIFY:
|
||||
fp_info ("Reporting verify results back %i\n", result);
|
||||
fpi_drvcb_report_verify_result (dev, result, NULL);
|
||||
break;
|
||||
case STATE_ENROLL: {
|
||||
struct fp_print_data * fdata = NULL;
|
||||
|
||||
fp_info ("Reporting enroll results back %i\n", result);
|
||||
|
||||
/* If the enroll is "done", then report back the UUID for the print. */
|
||||
if (result == FP_ENROLL_COMPLETE) {
|
||||
struct fp_print_data_item *item = NULL;
|
||||
|
||||
fdata = fpi_print_data_new (dev);
|
||||
|
||||
item = fpi_print_data_item_new(strlen(virt->curr_uuid));
|
||||
memcpy(item->data, virt->curr_uuid, strlen(virt->curr_uuid));
|
||||
fpi_print_data_add_item(fdata, item);
|
||||
}
|
||||
|
||||
fpi_drvcb_enroll_stage_completed (dev, result, fdata, NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
send_status(struct fp_dev *dev)
|
||||
{
|
||||
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
|
||||
gchar *msg = NULL;
|
||||
|
||||
if (virt->client_fd < 0)
|
||||
return;
|
||||
|
||||
switch (virt->state) {
|
||||
case STATE_IDLE:
|
||||
msg = g_strdup ("IDLE\n");
|
||||
break;
|
||||
case STATE_ENROLL:
|
||||
msg = g_strdup_printf ("ENROLL %s\n", virt->curr_uuid);
|
||||
break;
|
||||
case STATE_VERIFY:
|
||||
msg = g_strdup_printf ("VERIFY %s\n", virt->curr_uuid);
|
||||
break;
|
||||
}
|
||||
|
||||
send(virt->client_fd, msg, strlen(msg), MSG_NOSIGNAL);
|
||||
|
||||
g_free (msg);
|
||||
}
|
||||
|
||||
static void
|
||||
client_socket_cb (struct fp_dev *dev, int fd, short int events, void *data)
|
||||
{
|
||||
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
|
||||
guchar *pos;
|
||||
guchar buf[512];
|
||||
gssize len;
|
||||
|
||||
len = read(fd, buf, sizeof(buf));
|
||||
fp_dbg("Received %zi bytes from client!", len);
|
||||
|
||||
if (len > 0) {
|
||||
virt->recv_buf = g_realloc(virt->recv_buf, virt->recv_len + len);
|
||||
memcpy(virt->recv_buf + virt->recv_len, buf, len);
|
||||
virt->recv_len += len;
|
||||
|
||||
while ((pos = memmem(virt->recv_buf, virt->recv_len, "\n", 1))) {
|
||||
/* Found a newline, parse the command */
|
||||
fp_dbg("got a command response! %p %p", virt->recv_buf, pos);
|
||||
*pos = '\0';
|
||||
handle_response(dev, virt->recv_buf, pos - virt->recv_buf);
|
||||
|
||||
/* And remove the parsed part from the buffer */
|
||||
virt->recv_len = virt->recv_len - (pos - virt->recv_buf) - 1;
|
||||
memmove(pos, virt->recv_buf, virt->recv_len);
|
||||
virt->recv_buf = realloc(virt->recv_buf, virt->recv_len);
|
||||
}
|
||||
} else {
|
||||
fp_dbg("Client disconnected!");
|
||||
close (virt->client_fd);
|
||||
virt->client_fd = -1;
|
||||
|
||||
fpi_io_condition_remove (virt->client_io_cond);
|
||||
virt->client_io_cond = NULL;
|
||||
|
||||
g_free(virt->recv_buf);
|
||||
virt->recv_buf = NULL;
|
||||
virt->recv_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
new_connection_cb (struct fp_dev *dev, int fd, short int events, void *data)
|
||||
{
|
||||
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
|
||||
int new_client_fd;
|
||||
|
||||
new_client_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
|
||||
fp_dbg("Got a new connection!");
|
||||
|
||||
/* Already have a connection, reject this one */
|
||||
if (virt->client_fd >= 0) {
|
||||
fp_warn("Rejecting new connection as we already have one!");
|
||||
close (new_client_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
virt->client_fd = new_client_fd;
|
||||
virt->client_io_cond = fpi_io_condition_add (virt->client_fd, POLL_IN, client_socket_cb, dev, NULL);
|
||||
|
||||
send_status(dev);
|
||||
}
|
||||
|
||||
static int
|
||||
dev_init(struct fp_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
struct virt_dev *virt;
|
||||
const char *env;
|
||||
struct sockaddr_un addr = {
|
||||
.sun_family = AF_UNIX
|
||||
};
|
||||
G_DEBUG_HERE();
|
||||
|
||||
fpi_dev_set_nr_enroll_stages(dev, VIRT_ENROLL_STAGES);
|
||||
|
||||
virt = g_new0(struct virt_dev, 1);
|
||||
fp_dev_set_instance_data(dev, virt);
|
||||
|
||||
virt->client_fd = -1;
|
||||
|
||||
env = fpi_dev_get_virtual_env (dev);
|
||||
|
||||
virt->socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||
if (virt->socket_fd < 0) {
|
||||
fp_err("Could not create socket: %m");
|
||||
return virt->socket_fd;
|
||||
}
|
||||
strncpy (addr.sun_path, env, sizeof(addr.sun_path) - 1);
|
||||
|
||||
unlink(env);
|
||||
if (bind(virt->socket_fd, &addr, sizeof(struct sockaddr_un)) < 0) {
|
||||
fp_err("Could not bind address '%s': %m", addr.sun_path);
|
||||
close (virt->socket_fd);
|
||||
virt->socket_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen (virt->socket_fd, 1) < 0) {
|
||||
fp_err("Could not open socket for listening: %m");
|
||||
close (virt->socket_fd);
|
||||
virt->socket_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
virt->socket_io_cond = fpi_io_condition_add (virt->socket_fd, POLL_IN, new_connection_cb, dev, NULL);
|
||||
|
||||
fpi_drvcb_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_dev *dev)
|
||||
{
|
||||
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (virt->client_fd >= 0) {
|
||||
fpi_io_condition_remove (virt->client_io_cond);
|
||||
close (virt->client_fd);
|
||||
}
|
||||
|
||||
if (virt->socket_fd >= 0) {
|
||||
fpi_io_condition_remove (virt->socket_io_cond);
|
||||
close (virt->socket_fd);
|
||||
}
|
||||
|
||||
g_free (virt->curr_uuid);
|
||||
virt->curr_uuid = NULL;
|
||||
|
||||
g_free(virt);
|
||||
fpi_drvcb_close_complete(dev);
|
||||
}
|
||||
|
||||
static int enroll_start(struct fp_dev *dev)
|
||||
{
|
||||
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (virt->state != STATE_IDLE)
|
||||
return -1;
|
||||
|
||||
g_assert (virt->curr_uuid == NULL);
|
||||
|
||||
virt->state = STATE_ENROLL;
|
||||
|
||||
virt->curr_uuid = g_uuid_string_random ();
|
||||
send_status(dev);
|
||||
|
||||
fpi_drvcb_enroll_started(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enroll_stop(struct fp_dev *dev)
|
||||
{
|
||||
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (virt->state != STATE_ENROLL)
|
||||
return -1;
|
||||
|
||||
virt->state = STATE_IDLE;
|
||||
g_free (virt->curr_uuid);
|
||||
virt->curr_uuid = NULL;
|
||||
send_status(dev);
|
||||
|
||||
fpi_drvcb_enroll_stopped(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_start(struct fp_dev *dev)
|
||||
{
|
||||
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
|
||||
struct fp_print_data *print;
|
||||
struct fp_print_data_item *item;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (virt->state != STATE_IDLE)
|
||||
return -1;
|
||||
|
||||
g_assert (virt->curr_uuid == NULL);
|
||||
|
||||
virt->state = STATE_VERIFY;
|
||||
|
||||
print = fpi_dev_get_verify_data(dev);
|
||||
item = fpi_print_data_get_item(print);
|
||||
|
||||
/* We expecte a UUID, that means 36 bytes. */
|
||||
g_assert(item->length == 36);
|
||||
|
||||
virt->curr_uuid = g_malloc(37);
|
||||
virt->curr_uuid[36] = '\0';
|
||||
memcpy(virt->curr_uuid, item->data, 36);
|
||||
|
||||
g_assert(g_uuid_string_is_valid (virt->curr_uuid));
|
||||
|
||||
send_status(dev);
|
||||
|
||||
fpi_drvcb_verify_started(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_stop(struct fp_dev *dev, gboolean iterating)
|
||||
{
|
||||
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (virt->state != STATE_VERIFY)
|
||||
return -1;
|
||||
|
||||
virt->state = STATE_IDLE;
|
||||
g_free (virt->curr_uuid);
|
||||
virt->curr_uuid = NULL;
|
||||
|
||||
send_status(dev);
|
||||
|
||||
fpi_drvcb_verify_stopped(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct fp_driver virtual_misdev_driver = {
|
||||
.id = VIRTUAL_MIS_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "Virtual match-in-sensor device with internal storage",
|
||||
.bus = BUS_TYPE_VIRTUAL,
|
||||
.id_table.virtual_envvar = "FP_VIRTUAL_MISDEV",
|
||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.enroll_start = enroll_start,
|
||||
.enroll_stop = enroll_stop,
|
||||
.verify_start = verify_start,
|
||||
.verify_stop = verify_stop,
|
||||
};
|
|
@ -127,6 +127,9 @@ foreach driver: drivers
|
|||
if driver == 'virtual_imgdev'
|
||||
drivers_sources += [ 'drivers/virtual_imgdev.c' ]
|
||||
endif
|
||||
if driver == 'virtual_misdev'
|
||||
drivers_sources += [ 'drivers/virtual_misdev.c' ]
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if aeslib
|
||||
|
|
|
@ -45,8 +45,8 @@ mathlib_dep = cc.find_library('m', required: false)
|
|||
|
||||
# Drivers
|
||||
drivers = get_option('drivers').split(',')
|
||||
all_drivers = [ 'upekts', 'upektc', 'upeksonly', 'vcom5s', 'uru4000', 'aes1610', 'aes1660', 'aes2501', 'aes2550', 'aes2660', 'aes3500', 'aes4000', 'vfs101', 'vfs301', 'vfs5011', 'upektc_img', 'etes603', 'vfs0050', 'elan', 'virtual_imgdev' ]
|
||||
primitive_drivers = [ 'upekts' ]
|
||||
all_drivers = [ 'upekts', 'upektc', 'upeksonly', 'vcom5s', 'uru4000', 'aes1610', 'aes1660', 'aes2501', 'aes2550', 'aes2660', 'aes3500', 'aes4000', 'vfs101', 'vfs301', 'vfs5011', 'upektc_img', 'etes603', 'vfs0050', 'elan', 'virtual_imgdev', 'virtual_misdev' ]
|
||||
primitive_drivers = [ 'upekts', 'virtual_misdev' ]
|
||||
|
||||
if drivers == [ 'all' ]
|
||||
drivers = all_drivers
|
||||
|
|
Loading…
Reference in a new issue