Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
50cc336455 |
23 changed files with 1083 additions and 698 deletions
|
@ -1,6 +1,6 @@
|
|||
include:
|
||||
- local: '.gitlab-ci/libfprint-templates.yaml'
|
||||
- project: 'freedesktop/ci-templates'
|
||||
- project: 'wayland/ci-templates'
|
||||
ref: master
|
||||
file: '/templates/fedora.yml'
|
||||
- remote: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/master/flatpak/flatpak_ci_initiative.yml'
|
||||
|
@ -9,7 +9,6 @@ variables:
|
|||
extends: .libfprint_common_variables
|
||||
FDO_DISTRIBUTION_TAG: latest
|
||||
FDO_DISTRIBUTION_VERSION: rawhide
|
||||
FDO_UPSTREAM_REPO: "libfprint/$CI_PROJECT_NAME"
|
||||
FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG"
|
||||
BUNDLE: "org.freedesktop.libfprint.Demo.flatpak"
|
||||
LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546"
|
||||
|
@ -20,7 +19,7 @@ stages:
|
|||
- test
|
||||
- flatpak
|
||||
|
||||
image: $FEDORA_IMAGE
|
||||
image: "$FEDORA_IMAGE"
|
||||
|
||||
.build_one_driver_template: &build_one_driver
|
||||
script:
|
||||
|
@ -160,7 +159,6 @@ container_fedora_build:
|
|||
- $CI_PIPELINE_SOURCE == "schedule" && $CRON_TASK == "BUILD_CI_IMAGES"
|
||||
variables:
|
||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||
FDO_FORCE_REBUILD: 1
|
||||
# a list of packages to install
|
||||
FDO_DISTRIBUTION_PACKAGES:
|
||||
$LIBFPRINT_DEPENDENCIES
|
||||
|
|
|
@ -26,4 +26,3 @@
|
|||
uncrustify
|
||||
valgrind
|
||||
clang-analyzer
|
||||
diffutils
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
# Supported by libfprint driver aes1610
|
||||
usb:v08FFp1600*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes1660
|
||||
usb:v08FFp1660*
|
||||
|
@ -25,19 +24,16 @@ usb:v08FFp168D*
|
|||
usb:v08FFp168E*
|
||||
usb:v08FFp168F*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes2501
|
||||
usb:v08FFp2500*
|
||||
usb:v08FFp2580*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes2550
|
||||
usb:v08FFp2550*
|
||||
usb:v08FFp2810*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes2660
|
||||
usb:v08FFp2660*
|
||||
|
@ -59,23 +55,19 @@ usb:v08FFp268E*
|
|||
usb:v08FFp268F*
|
||||
usb:v08FFp2691*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes3500
|
||||
usb:v08FFp5731*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes4000
|
||||
usb:v5501p08FF*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver egis0570
|
||||
usb:v1C7Ap0570*
|
||||
usb:v1C7Ap0571*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver elan
|
||||
usb:v04F3p0903*
|
||||
|
@ -139,17 +131,14 @@ usb:v04F3p0C63*
|
|||
usb:v04F3p0C6E*
|
||||
usb:v04F3p0C58*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver elanmoc
|
||||
usb:v04F3p0C7E*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver etes603
|
||||
usb:v1C7Ap0603*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver goodixmoc
|
||||
usb:v27C6p5840*
|
||||
|
@ -157,7 +146,6 @@ usb:v27C6p609C*
|
|||
usb:v27C6p60A2*
|
||||
usb:v27C6p639C*
|
||||
usb:v27C6p63AC*
|
||||
usb:v27C6p63BC*
|
||||
usb:v27C6p6496*
|
||||
usb:v27C6p6584*
|
||||
usb:v27C6p658C*
|
||||
|
@ -166,12 +154,10 @@ usb:v27C6p6594*
|
|||
usb:v27C6p659C*
|
||||
usb:v27C6p6A94*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver nb1010
|
||||
usb:v298Dp1010*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver synaptics
|
||||
usb:v06CBp00BD*
|
||||
|
@ -183,29 +169,22 @@ usb:v06CBp00C9*
|
|||
usb:v06CBp0100*
|
||||
usb:v06CBp00F0*
|
||||
usb:v06CBp0103*
|
||||
usb:v06CBp0123*
|
||||
usb:v06CBp0126*
|
||||
usb:v06CBp0129*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver upeksonly
|
||||
usb:v147Ep2016*
|
||||
usb:v147Ep1000*
|
||||
usb:v147Ep1001*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver upektc
|
||||
usb:v0483p2015*
|
||||
usb:v147Ep3001*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver upektc_img
|
||||
usb:v147Ep2020*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver uru4000
|
||||
usb:v045Ep00BC*
|
||||
|
@ -215,28 +194,23 @@ usb:v05BAp0007*
|
|||
usb:v05BAp0008*
|
||||
usb:v05BAp000A*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vcom5s
|
||||
usb:v061Ap0110*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs0050
|
||||
usb:v138Ap0050*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs101
|
||||
usb:v138Ap0001*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs301
|
||||
usb:v138Ap0005*
|
||||
usb:v138Ap0008*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs5011
|
||||
usb:v138Ap0010*
|
||||
|
@ -245,12 +219,10 @@ usb:v138Ap0015*
|
|||
usb:v138Ap0017*
|
||||
usb:v138Ap0018*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs7552
|
||||
usb:v138Ap0091*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Known unsupported devices
|
||||
usb:v04F3p036B*
|
||||
|
@ -282,7 +254,6 @@ usb:v0A5Cp5840*
|
|||
usb:v0A5Cp5841*
|
||||
usb:v0A5Cp5842*
|
||||
usb:v0A5Cp5843*
|
||||
usb:v0A5Cp5844*
|
||||
usb:v0A5Cp5845*
|
||||
usb:v10A5p0007*
|
||||
usb:v1188p9545*
|
||||
|
@ -325,4 +296,3 @@ usb:v2808p9338*
|
|||
usb:v298Dp2033*
|
||||
usb:v3538p0930*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
|
|
@ -806,7 +806,7 @@ elanmoc_enroll (FpDevice *device)
|
|||
FpPrint *print = NULL;
|
||||
GVariant *data = NULL;
|
||||
GVariant *uid = NULL;
|
||||
g_autofree gchar *user_id = NULL;
|
||||
g_autofree gchar *user_id;
|
||||
gsize user_id_len;
|
||||
guint8 *userdata = g_malloc0 (ELAN_USERDATE_SIZE);
|
||||
|
||||
|
|
827
libfprint/drivers/elanmoc2/elanmoc2.c
Normal file
827
libfprint/drivers/elanmoc2/elanmoc2.c
Normal file
|
@ -0,0 +1,827 @@
|
|||
/*
|
||||
* Driver for ELAN Match-On-Chip sensors
|
||||
* Copyright (C) 2021 Davide Depau
|
||||
*
|
||||
* 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 "elanmoc2"
|
||||
|
||||
// Library includes
|
||||
#include <glib.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
// Local includes
|
||||
#include "drivers_api.h"
|
||||
|
||||
#include "elanmoc2.h"
|
||||
|
||||
struct _FpiDeviceElanMoC2 {
|
||||
FpDevice parent;
|
||||
|
||||
/* Device properties */
|
||||
unsigned short dev_type;
|
||||
|
||||
/* USB response data */
|
||||
unsigned char *buffer_in;
|
||||
gssize buffer_in_len;
|
||||
|
||||
/* Command status data */
|
||||
FpiSsm *ssm;
|
||||
unsigned char enrolled_num;
|
||||
unsigned char print_index;
|
||||
GPtrArray *list_result;
|
||||
|
||||
// Enroll
|
||||
gint enroll_stage;
|
||||
FpPrint *enroll_print;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FpiDeviceElanMoC2, fpi_device_elanmoc2, FP_TYPE_DEVICE);
|
||||
|
||||
|
||||
static void
|
||||
elanmoc2_cmd_usb_receive_callback(FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, GError *error) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
|
||||
if (self->ssm == NULL) {
|
||||
fp_info("Received USB callback with no ongoing action");
|
||||
if (error)
|
||||
fp_info ("USB callback error: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
fpi_ssm_mark_failed(g_steal_pointer(&self->ssm), error);
|
||||
} else if (transfer->actual_length > 0 && transfer->buffer[0] != 0x40) {
|
||||
fpi_ssm_mark_failed(g_steal_pointer(&self->ssm), fpi_device_error_new_msg(FP_DEVICE_ERROR_PROTO,
|
||||
"Error receiving data from sensor"));
|
||||
} else {
|
||||
self->buffer_in = g_memdup(transfer->buffer, transfer->actual_length);
|
||||
self->buffer_in_len = transfer->actual_length;
|
||||
fpi_ssm_next_state(self->ssm);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
elanmoc2_cmd_send_sync(FpDevice *device, const struct elanmoc2_cmd *cmd, guint8 *buffer_out, GError **error) {
|
||||
g_autoptr(FpiUsbTransfer) transfer_out = fpi_usb_transfer_new(device);
|
||||
transfer_out->short_is_error = TRUE;
|
||||
fpi_usb_transfer_fill_bulk_full(transfer_out, ELANMOC2_EP_CMD_OUT, g_steal_pointer(&buffer_out), cmd->out_len,
|
||||
g_free);
|
||||
return fpi_usb_transfer_submit_sync(transfer_out, ELANMOC2_USB_SEND_TIMEOUT, error);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_cmd_transceive(FpDevice *device, FpiSsm *ssm, const struct elanmoc2_cmd *cmd, guint8 *buffer_out) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
|
||||
GError *error = NULL;
|
||||
gboolean send_status = elanmoc2_cmd_send_sync(device, cmd, g_steal_pointer(&buffer_out), &error);
|
||||
if (!send_status)
|
||||
return fpi_ssm_mark_failed(g_steal_pointer(&self->ssm), error);
|
||||
|
||||
FpiUsbTransfer *transfer_in = fpi_usb_transfer_new(device);
|
||||
transfer_in->short_is_error = TRUE;
|
||||
|
||||
fpi_usb_transfer_fill_bulk(transfer_in, cmd->ep_in, cmd->in_len);
|
||||
fpi_usb_transfer_submit(transfer_in,
|
||||
cmd->cancellable ? ELANMOC2_USB_RECV_CANCELLABLE_TIMEOUT : ELANMOC2_USB_RECV_TIMEOUT,
|
||||
cmd->cancellable ? fpi_device_get_cancellable(device) : NULL,
|
||||
elanmoc2_cmd_usb_receive_callback,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static uint8_t *
|
||||
elanmoc2_prepare_cmd(FpiDeviceElanMoC2 *self, const struct elanmoc2_cmd *cmd) {
|
||||
if (cmd->devices != ELANMOC2_ALL_DEV && !(cmd->devices & self->dev_type))
|
||||
return NULL;
|
||||
|
||||
g_autofree uint8_t *buffer = g_malloc0(cmd->out_len);
|
||||
buffer[0] = 0x40;
|
||||
memcpy(&buffer[1], cmd->cmd, cmd->short_command ? 1 : 2);
|
||||
return g_steal_pointer(&buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_print_set_data(FpPrint *print, guchar finger_id, guchar user_id_len, const guchar *user_id) {
|
||||
fpi_print_set_type(print, FPI_PRINT_RAW);
|
||||
fpi_print_set_device_stored(print, TRUE);
|
||||
|
||||
GVariant *user_id_v = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, user_id, user_id_len, sizeof(guchar));
|
||||
GVariant *fpi_data = g_variant_new("(y@ay)", finger_id, user_id_v);
|
||||
g_object_set(print, "fpi-data", fpi_data, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_print_get_data(FpPrint *print, guchar *finger_id, guchar *user_id_len, const guchar **user_id) {
|
||||
g_autoptr(GVariant) fpi_data = NULL;
|
||||
g_autoptr(GVariant) user_id_v = NULL;
|
||||
|
||||
g_object_get(print, "fpi-data", &fpi_data, NULL);
|
||||
g_assert_nonnull(fpi_data);
|
||||
|
||||
g_variant_get(fpi_data, "(y@ay)", finger_id, &user_id_v);
|
||||
g_assert_nonnull(user_id_v);
|
||||
|
||||
gsize user_id_len_s = 0;
|
||||
gconstpointer user_id_tmp = g_variant_get_fixed_array(user_id_v, &user_id_len_s, sizeof(guchar));
|
||||
g_assert(user_id_len_s <= 255);
|
||||
|
||||
*user_id_len = user_id_len_s;
|
||||
*user_id = g_memdup(user_id_tmp, user_id_len_s);
|
||||
}
|
||||
|
||||
static FpPrint *
|
||||
elanmoc2_print_new_with_user_id(FpiDeviceElanMoC2 *self, guchar finger_id, guchar user_id_len, const guchar *user_id) {
|
||||
FpPrint *print = fp_print_new(FP_DEVICE(self));
|
||||
elanmoc2_print_set_data(print, finger_id, user_id_len, user_id);
|
||||
return g_steal_pointer(&print);
|
||||
}
|
||||
|
||||
static FpPrint *
|
||||
elanmoc2_print_new_from_finger_info(FpiDeviceElanMoC2 *self, guint8 finger_id, const guint8 *finger_info_response) {
|
||||
guint8 user_id_max_len = cmd_finger_info.in_len - 2; // 2-byte header
|
||||
|
||||
g_autofree guint8 *user_id = g_malloc(user_id_max_len + 1);
|
||||
memcpy(user_id, &finger_info_response[2], user_id_max_len);
|
||||
user_id[user_id_max_len] = '\0';
|
||||
|
||||
guint8 user_id_len = user_id_max_len;
|
||||
if (g_str_has_prefix((const gchar *) user_id, "FP1-"))
|
||||
user_id_len = strnlen((const char *) user_id, user_id_max_len);
|
||||
|
||||
fp_info("Creating new print: finger %d, user id[%d]: %s", finger_id, user_id_len, user_id);
|
||||
|
||||
FpPrint *print = elanmoc2_print_new_with_user_id(self, finger_id, user_id_len, user_id);
|
||||
|
||||
if (!fpi_print_fill_from_user_id(print, (const char *) user_id)) {
|
||||
// Fingerprint matched with on-sensor print, but the on-sensor print was not added by libfprint.
|
||||
// Wipe it and report a failure.
|
||||
fp_info("Finger info not generated by libfprint");
|
||||
} else {
|
||||
fp_info("Finger info with libfprint user ID");
|
||||
}
|
||||
|
||||
return g_steal_pointer(&print);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
elanmoc2_finger_info_is_present(const guint8 *finger_info_response) {
|
||||
// Response for not enrolled finger is either all 0x00 or 0xFF
|
||||
guchar first_byte = finger_info_response[2];
|
||||
if (first_byte != 0x00 && first_byte != 0xFF)
|
||||
return TRUE;
|
||||
|
||||
for (gsize i = 3; i < cmd_finger_info.in_len; i++) {
|
||||
if (finger_info_response[i] != first_byte)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_cancel(FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
fp_info("Cancelling any ongoing requests");
|
||||
|
||||
GError *error = NULL;
|
||||
g_autofree uint8_t *buffer_out = elanmoc2_prepare_cmd(self, &cmd_abort);
|
||||
elanmoc2_cmd_send_sync(device, &cmd_abort, g_steal_pointer(&buffer_out), &error);
|
||||
|
||||
if (error) {
|
||||
fp_warn("Error while cancelling action: %s", error->message);
|
||||
g_clear_error(&error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_open(FpDevice *device) {
|
||||
GError *error = NULL;
|
||||
FpiDeviceElanMoC2 *self;
|
||||
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
if (!g_usb_device_reset(fpi_device_get_usb_device(device), &error))
|
||||
return fpi_device_open_complete(device, error);
|
||||
|
||||
if (!g_usb_device_claim_interface(fpi_device_get_usb_device(FP_DEVICE(device)), 0, 0, &error))
|
||||
return fpi_device_open_complete(device, error);
|
||||
|
||||
self = FPI_DEVICE_ELANMOC2(device);
|
||||
self->dev_type = fpi_device_get_driver_data(FP_DEVICE(device));
|
||||
fpi_device_open_complete(device, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_close(FpDevice *device) {
|
||||
GError *error = NULL;
|
||||
fp_info("Closing device");
|
||||
elanmoc2_cancel(device);
|
||||
g_usb_device_release_interface(fpi_device_get_usb_device(FP_DEVICE(device)), 0, 0, &error);
|
||||
fpi_device_close_complete(device, error);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_ssm_completed_callback(FpiSsm *ssm, FpDevice *device, GError *error) {
|
||||
if (error)
|
||||
fpi_device_action_error(device, error);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_perform_get_num_enrolled(FpiDeviceElanMoC2 *self, FpiSsm *ssm) {
|
||||
g_autofree uint8_t *buffer_out = NULL;
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_get_enrolled_count)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
return;
|
||||
}
|
||||
elanmoc2_cmd_transceive(FP_DEVICE(self), ssm, &cmd_get_enrolled_count, g_steal_pointer(&buffer_out));
|
||||
fp_info("Sent query for number of enrolled fingers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a command status code and, if an error has occurred, creates a new error object.
|
||||
* Returns whether the operation needs to be retried..
|
||||
* @param self FpiDeviceElanMoC2 pointer
|
||||
* @param error A pointer where the GError will be placed in case of errors
|
||||
* @return Whether the current action should be retried
|
||||
*/
|
||||
static gboolean
|
||||
elanmoc2_get_finger_error(FpiDeviceElanMoC2 *self, GError **error) {
|
||||
g_assert(self->buffer_in != NULL);
|
||||
|
||||
// Regular status codes never have the most-significant nibble set; errors do
|
||||
if ((self->buffer_in[1] & 0xF0) == 0) {
|
||||
*error = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
switch ((unsigned char) self->buffer_in[1]) {
|
||||
case ELANMOC2_RESP_MOVE_DOWN:
|
||||
*error = fpi_device_retry_new_msg(FP_DEVICE_RETRY_CENTER_FINGER,
|
||||
"Move your finger slightly downwards");
|
||||
return TRUE;
|
||||
case ELANMOC2_RESP_MOVE_RIGHT:
|
||||
*error = fpi_device_retry_new_msg(FP_DEVICE_RETRY_CENTER_FINGER,
|
||||
"Move your finger slightly to the right");
|
||||
return TRUE;
|
||||
case ELANMOC2_RESP_MOVE_UP:
|
||||
*error = fpi_device_retry_new_msg(FP_DEVICE_RETRY_CENTER_FINGER,
|
||||
"Move your finger slightly upwards");
|
||||
return TRUE;
|
||||
case ELANMOC2_RESP_MOVE_LEFT:
|
||||
*error = fpi_device_retry_new_msg(FP_DEVICE_RETRY_CENTER_FINGER,
|
||||
"Move your finger slightly to the left");
|
||||
return TRUE;
|
||||
case ELANMOC2_RESP_MAX_ENROLLED_REACHED:
|
||||
*error = fpi_device_retry_new_msg(FP_DEVICE_RETRY_CENTER_FINGER,
|
||||
"Move your finger slightly to the right");
|
||||
return TRUE;
|
||||
case ELANMOC2_RESP_SENSOR_DIRTY:
|
||||
*error = fpi_device_retry_new_msg(FP_DEVICE_RETRY_REMOVE_FINGER,
|
||||
"Sensor is dirty or wet");
|
||||
return TRUE;
|
||||
case ELANMOC2_RESP_NOT_ENOUGH_SURFACE:
|
||||
*error = fpi_device_retry_new_msg(FP_DEVICE_RETRY_REMOVE_FINGER,
|
||||
"Press your finger slightly harder on the sensor");
|
||||
return TRUE;
|
||||
case ELANMOC2_RESP_NOT_ENROLLED:
|
||||
*error = fpi_device_error_new_msg(FP_DEVICE_ERROR_DATA_NOT_FOUND,
|
||||
"Finger not recognized");
|
||||
return FALSE;
|
||||
default:
|
||||
*error = fpi_device_error_new_msg(FP_DEVICE_ERROR_GENERAL,
|
||||
"Unknown error");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_identify_verify_complete(FpDevice *device, GError *error) {
|
||||
if (fpi_device_get_current_action(device) == FPI_DEVICE_ACTION_IDENTIFY) {
|
||||
fpi_device_identify_complete(device, error);
|
||||
} else {
|
||||
if (error != NULL && error->domain == FP_DEVICE_ERROR && error->code == FP_DEVICE_ERROR_DATA_NOT_FOUND) {
|
||||
g_clear_error(&error);
|
||||
FpPrint *expected = NULL;
|
||||
fpi_device_get_verify_data(device, &expected);
|
||||
fpi_device_verify_report(device, FPI_MATCH_FAIL, g_steal_pointer(&expected), NULL);
|
||||
}
|
||||
fpi_device_verify_complete(device, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the correct verify or identify report function based on the input data.
|
||||
* Returns whether the action should be completed.
|
||||
* @param device FpDevice
|
||||
* @param print Identified fingerprint
|
||||
* @param error Optional error
|
||||
* @return Whether to complete the action.
|
||||
*/
|
||||
static gboolean
|
||||
elanmoc2_identify_verify_report(FpDevice *device, FpPrint *print, GError **error) {
|
||||
if (*error != NULL && (*error)->domain != FP_DEVICE_RETRY)
|
||||
return TRUE;
|
||||
|
||||
if (fpi_device_get_current_action(device) == FPI_DEVICE_ACTION_IDENTIFY) {
|
||||
if (print != NULL) {
|
||||
g_autoptr(GPtrArray) gallery = NULL;
|
||||
fpi_device_get_identify_data(device, &gallery);
|
||||
g_ptr_array_ref(gallery);
|
||||
|
||||
for (int i = 0; i < gallery->len; i++) {
|
||||
FpPrint *to_match = g_ptr_array_index(gallery, i);
|
||||
if (fp_print_equal(to_match, print)) {
|
||||
fpi_device_identify_report(device, to_match, print, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
fpi_device_identify_report(device, NULL, print, NULL);
|
||||
return TRUE;
|
||||
} else {
|
||||
FpiMatchResult result = FPI_MATCH_FAIL;
|
||||
if (print != NULL) {
|
||||
FpPrint *to_match = NULL;
|
||||
fpi_device_get_verify_data(device, &to_match);
|
||||
if (fp_print_equal(to_match, print))
|
||||
result = FPI_MATCH_SUCCESS;
|
||||
}
|
||||
fpi_device_verify_report(device, result, print, *error);
|
||||
return result == FPI_MATCH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_identify_run_state(FpiSsm *ssm, FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
g_autofree uint8_t *buffer_out = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case IDENTIFY_GET_NUM_ENROLLED: {
|
||||
elanmoc2_perform_get_num_enrolled(self, ssm);
|
||||
break;
|
||||
}
|
||||
case IDENTIFY_CHECK_NUM_ENROLLED: {
|
||||
self->enrolled_num = self->buffer_in[1];
|
||||
if (self->enrolled_num == 0) {
|
||||
fp_info("No fingers enrolled, no need to identify finger");
|
||||
error = NULL;
|
||||
elanmoc2_identify_verify_report(device, NULL, &error);
|
||||
elanmoc2_identify_verify_complete(device, NULL);
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
break;
|
||||
}
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
case IDENTIFY_IDENTIFY: {
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_identify)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
elanmoc2_cmd_transceive(device, ssm, &cmd_identify, g_steal_pointer(&buffer_out));
|
||||
fpi_device_report_finger_status(device, FP_FINGER_STATUS_NEEDED);
|
||||
fp_info("Sent identification request");
|
||||
break;
|
||||
}
|
||||
case IDENTIFY_GET_FINGER_INFO: {
|
||||
fpi_device_report_finger_status(device, FP_FINGER_STATUS_PRESENT);
|
||||
error = NULL;
|
||||
gboolean retry = elanmoc2_get_finger_error(self, &error);
|
||||
if (error != NULL) {
|
||||
fp_info("Identify failed: %s", error->message);
|
||||
if (retry) {
|
||||
elanmoc2_identify_verify_report(device, NULL, &error);
|
||||
fpi_ssm_jump_to_state(ssm, IDENTIFY_IDENTIFY);
|
||||
} else {
|
||||
elanmoc2_identify_verify_complete(device, g_steal_pointer(&error));
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
}
|
||||
break;
|
||||
}
|
||||
self->print_index = self->buffer_in[1];
|
||||
fp_info("Identified finger %d; requesting finger info", self->print_index);
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_finger_info)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
buffer_out[3] = self->print_index;
|
||||
elanmoc2_cmd_transceive(device, ssm, &cmd_finger_info, g_steal_pointer(&buffer_out));
|
||||
break;
|
||||
}
|
||||
case IDENTIFY_CHECK_FINGER_INFO: {
|
||||
fpi_device_report_finger_status(device, FP_FINGER_STATUS_NONE);
|
||||
|
||||
FpPrint *print = elanmoc2_print_new_from_finger_info(self, self->print_index, self->buffer_in);
|
||||
|
||||
error = NULL;
|
||||
if (elanmoc2_identify_verify_report(device, print, &error)) {
|
||||
elanmoc2_identify_verify_complete(device, NULL);
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
} else {
|
||||
fpi_ssm_jump_to_state(ssm, IDENTIFY_IDENTIFY);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
g_clear_pointer(&self->buffer_in, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_identify_verify(FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
fp_info("[elanmoc2] New identify/verify operation");
|
||||
self->ssm = fpi_ssm_new(device, elanmoc2_identify_run_state, IDENTIFY_NUM_STATES);
|
||||
fpi_ssm_start(self->ssm, elanmoc2_ssm_completed_callback);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_list_ssm_completed_callback(FpiSsm *ssm, FpDevice *device, GError *error) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
g_clear_pointer(&self->list_result, g_ptr_array_free);
|
||||
elanmoc2_ssm_completed_callback(ssm, device, error);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_list_run_state(FpiSsm *ssm, FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
g_autofree uint8_t *buffer_out = NULL;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case LIST_GET_NUM_ENROLLED:
|
||||
elanmoc2_perform_get_num_enrolled(self, ssm);
|
||||
break;
|
||||
case LIST_CHECK_NUM_ENROLLED:
|
||||
self->enrolled_num = self->buffer_in[1];
|
||||
fp_info("List: fingers enrolled: %d", self->enrolled_num);
|
||||
if (self->enrolled_num == 0) {
|
||||
fpi_device_list_complete(device, g_steal_pointer(&self->list_result), NULL);
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
break;
|
||||
}
|
||||
self->print_index = 0;
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
case LIST_GET_FINGER_INFO:
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_finger_info)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
buffer_out[3] = self->print_index;
|
||||
elanmoc2_cmd_transceive(device, ssm, &cmd_finger_info, g_steal_pointer(&buffer_out));
|
||||
fp_info("Sent get finger info command for finger %d", self->print_index);
|
||||
break;
|
||||
case LIST_CHECK_FINGER_INFO:
|
||||
fpi_device_report_finger_status(device, FP_FINGER_STATUS_NONE);
|
||||
fp_info("Successfully retrieved finger info for %d", self->print_index);
|
||||
|
||||
if (elanmoc2_finger_info_is_present(self->buffer_in)) {
|
||||
FpPrint *print = elanmoc2_print_new_from_finger_info(self, self->print_index, self->buffer_in);
|
||||
g_ptr_array_add(self->list_result, g_object_ref_sink(print));
|
||||
}
|
||||
|
||||
if (++(self->print_index) < ELANMOC2_MAX_PRINTS) {
|
||||
fpi_ssm_jump_to_state(ssm, LIST_GET_FINGER_INFO);
|
||||
} else {
|
||||
fpi_device_list_complete(device, g_steal_pointer(&self->list_result), NULL);
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
g_clear_pointer(&self->buffer_in, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_list(FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
fp_info("[elanmoc2] New list operation");
|
||||
self->ssm = fpi_ssm_new(device, elanmoc2_list_run_state, LIST_NUM_STATES);
|
||||
self->list_result = g_ptr_array_new_with_free_func(g_object_unref);
|
||||
fpi_ssm_start(self->ssm, elanmoc2_list_ssm_completed_callback);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_enroll_ssm_completed_callback(FpiSsm *ssm, FpDevice *device, GError *error) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
// Pointer is either stolen by fpi_device_enroll_complete() or otherwise unref'd by libfprint behind the scenes.
|
||||
self->enroll_print = NULL;
|
||||
elanmoc2_ssm_completed_callback(ssm, device, error);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_enroll_run_state(FpiSsm *ssm, FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
g_assert_nonnull(self->enroll_print);
|
||||
|
||||
g_autofree uint8_t *buffer_out = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case ENROLL_GET_NUM_ENROLLED:
|
||||
elanmoc2_perform_get_num_enrolled(self, ssm);
|
||||
break;
|
||||
case ENROLL_CHECK_NUM_ENROLLED:
|
||||
self->enrolled_num = self->buffer_in[1];
|
||||
if (self->enrolled_num >= ELANMOC2_MAX_PRINTS) {
|
||||
fp_info("Can't enroll, sensor storage is full");
|
||||
error = fpi_device_error_new_msg(FP_DEVICE_ERROR_DATA_FULL,
|
||||
"Sensor storage is full");
|
||||
fpi_device_enroll_complete(device, NULL, g_steal_pointer(&error));
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
} else if (self->enrolled_num == 0) {
|
||||
fp_info("Enrolled count is 0, proceeding with enroll stage");
|
||||
fpi_ssm_jump_to_state(ssm, ENROLL_ENROLL);
|
||||
} else {
|
||||
fp_info("Fingers enrolled: %d, need to check for re-enroll", self->enrolled_num);
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
break;
|
||||
case ENROLL_ENROLL:
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_enroll)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
buffer_out[3] = self->enrolled_num;
|
||||
buffer_out[4] = ELANMOC2_ENROLL_TIMES;
|
||||
buffer_out[5] = self->enroll_stage;
|
||||
buffer_out[6] = 0;
|
||||
elanmoc2_cmd_transceive(device, ssm, &cmd_enroll, g_steal_pointer(&buffer_out));
|
||||
fp_info("Enroll command sent: %d/%d", self->enroll_stage, ELANMOC2_ENROLL_TIMES);
|
||||
fpi_device_report_finger_status(device, FP_FINGER_STATUS_NEEDED);
|
||||
break;
|
||||
case ENROLL_CHECK_ENROLLED:
|
||||
fpi_device_report_finger_status(device, FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
if (self->buffer_in[1] == 0) {
|
||||
// Stage okay
|
||||
fp_info("Enroll stage succeeded");
|
||||
self->enroll_stage++;
|
||||
fpi_device_enroll_progress(device, self->enroll_stage, self->enroll_print, NULL);
|
||||
if (self->enroll_stage >= ELANMOC2_ENROLL_TIMES) {
|
||||
fp_info("Enroll completed");
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Detection error
|
||||
error = NULL;
|
||||
gboolean retry = elanmoc2_get_finger_error(self, &error);
|
||||
if (error != NULL) {
|
||||
fp_info("Enroll stage failed: %s", error->message);
|
||||
if (self->buffer_in[1] == ELANMOC2_RESP_NOT_ENROLLED) {
|
||||
// Not enrolled is a fatal error for "identify" but not for "enroll"
|
||||
error->domain = FP_DEVICE_RETRY;
|
||||
error->code = FP_DEVICE_RETRY_TOO_SHORT;
|
||||
retry = false;
|
||||
}
|
||||
if (retry) {
|
||||
fpi_device_enroll_progress(device, self->enroll_stage, NULL, error);
|
||||
} else {
|
||||
fpi_device_enroll_complete(device, NULL, g_steal_pointer(&error));
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
}
|
||||
} else {
|
||||
fp_info("Enroll stage failed for unknown reasons");
|
||||
}
|
||||
}
|
||||
fp_info("Performing another enroll");
|
||||
fpi_ssm_jump_to_state(ssm, ENROLL_ENROLL);
|
||||
break;
|
||||
case ENROLL_UNK_AFTER_ENROLL:
|
||||
fpi_device_report_finger_status(device, FP_FINGER_STATUS_NONE);
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_unk_after_enroll)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
elanmoc2_cmd_transceive(device, ssm, &cmd_unk_after_enroll, g_steal_pointer(&buffer_out));
|
||||
fp_info("Unknown after-enroll command sent");
|
||||
break;
|
||||
case ENROLL_COMMIT:
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_commit)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
gchar *user_id = fpi_print_generate_user_id(self->enroll_print);
|
||||
elanmoc2_print_set_data(self->enroll_print, self->enrolled_num, strlen(user_id), (guint8 *) user_id);
|
||||
|
||||
buffer_out[3] = 0xf0 | (self->enrolled_num + 5);
|
||||
strncpy((char *) &buffer_out[4], user_id, cmd_commit.out_len - 4);
|
||||
elanmoc2_cmd_transceive(device, ssm, &cmd_commit, g_steal_pointer(&buffer_out));
|
||||
fp_info("Commit command sent");
|
||||
break;
|
||||
case ENROLL_CHECK_COMMITTED:
|
||||
error = NULL;
|
||||
if (self->buffer_in[1] != 0) {
|
||||
fp_info("Commit failed with error code %d", self->buffer_in[1]);
|
||||
error = fpi_device_error_new_msg(FP_DEVICE_ERROR_GENERAL,
|
||||
"Failed to store fingerprint for unknown reasons");
|
||||
fpi_device_enroll_complete(device, NULL, error);
|
||||
fpi_ssm_mark_failed(g_steal_pointer(&self->ssm), error);
|
||||
self->enroll_print = NULL;
|
||||
} else {
|
||||
fp_info("Commit succeeded");
|
||||
fpi_device_enroll_complete(device, g_steal_pointer(&self->enroll_print), NULL);
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
g_clear_pointer(&self->buffer_in, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_enroll(FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
fp_info("[elanmoc2] New enroll operation");
|
||||
|
||||
fpi_device_get_enroll_data(device, &self->enroll_print);
|
||||
|
||||
self->ssm = fpi_ssm_new(device, elanmoc2_enroll_run_state, ENROLL_NUM_STATES);
|
||||
fpi_ssm_start(self->ssm, elanmoc2_enroll_ssm_completed_callback);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_delete_run_state(FpiSsm *ssm, FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
g_autofree guint8 *buffer_out = NULL;
|
||||
g_autofree const guint8 *user_id = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case DELETE_GET_NUM_ENROLLED:
|
||||
elanmoc2_perform_get_num_enrolled(self, ssm);
|
||||
break;
|
||||
case DELETE_DELETE:
|
||||
self->enrolled_num = self->buffer_in[1];
|
||||
if (self->enrolled_num == 0) {
|
||||
error = fpi_device_error_new_msg(FP_DEVICE_ERROR_DATA_NOT_FOUND,
|
||||
"Sensor storage is empty, nothing to delete");
|
||||
fpi_device_delete_complete(device, error);
|
||||
fpi_ssm_mark_failed(g_steal_pointer(&self->ssm), error);
|
||||
break;
|
||||
}
|
||||
FpPrint *print = NULL;
|
||||
fpi_device_get_delete_data(device, &print);
|
||||
|
||||
guint8 finger_id = 0xFF;
|
||||
guint8 user_id_len = 0;
|
||||
elanmoc2_print_get_data(print, &finger_id, &user_id_len, &user_id);
|
||||
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_delete)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
buffer_out[3] = 0xf0 | (finger_id + 5);
|
||||
memcpy((char *) &buffer_out[4], (char *) user_id, MIN(cmd_delete.out_len - 4, user_id_len));
|
||||
elanmoc2_cmd_transceive(device, ssm, &cmd_delete, g_steal_pointer(&buffer_out));
|
||||
break;
|
||||
case DELETE_CHECK_DELETED:
|
||||
error = NULL;
|
||||
if (self->buffer_in[1] != 0) {
|
||||
error = fpi_device_error_new_msg(FP_DEVICE_ERROR_DATA_NOT_FOUND,
|
||||
"Failed to delete fingerprint");
|
||||
fpi_ssm_mark_failed(g_steal_pointer(&self->ssm), error);
|
||||
} else {
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
}
|
||||
fpi_device_delete_complete(device, error);
|
||||
break;
|
||||
}
|
||||
|
||||
g_clear_pointer(&self->buffer_in, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_delete(FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
fp_info("[elanmoc2] New delete operation");
|
||||
self->ssm = fpi_ssm_new(device, elanmoc2_delete_run_state, DELETE_NUM_STATES);
|
||||
fpi_ssm_start(self->ssm, elanmoc2_ssm_completed_callback);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_clear_storage_run_state(FpiSsm *ssm, FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
g_autofree uint8_t *buffer_out = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case CLEAR_STORAGE_GET_NUM_ENROLLED:
|
||||
elanmoc2_perform_get_num_enrolled(self, ssm);
|
||||
break;
|
||||
case CLEAR_STORAGE_CHECK_NUM_ENROLLED:
|
||||
self->enrolled_num = self->buffer_in[1];
|
||||
if (self->enrolled_num == 0) {
|
||||
fpi_device_clear_storage_complete(device, NULL);
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
break;
|
||||
}
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
case CLEAR_STORAGE_GET_FINGER_INFO:
|
||||
if (self->print_index >= ELANMOC2_MAX_PRINTS) {
|
||||
fpi_device_clear_storage_complete(device, NULL);
|
||||
fpi_ssm_mark_completed(g_steal_pointer(&self->ssm));
|
||||
break;
|
||||
}
|
||||
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_finger_info)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
buffer_out[3] = self->print_index;
|
||||
elanmoc2_cmd_transceive(device, ssm, &cmd_finger_info, g_steal_pointer(&buffer_out));
|
||||
break;
|
||||
case CLEAR_STORAGE_DELETE:
|
||||
fpi_device_report_finger_status(device, FP_FINGER_STATUS_NONE);
|
||||
|
||||
if (!elanmoc2_finger_info_is_present(self->buffer_in)) {
|
||||
// Not enrolled
|
||||
self->print_index++;
|
||||
fpi_ssm_jump_to_state(ssm, CLEAR_STORAGE_GET_FINGER_INFO);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((buffer_out = elanmoc2_prepare_cmd(self, &cmd_delete)) == NULL) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
buffer_out[3] = 0xf0 | (self->print_index + 5);
|
||||
memcpy(&buffer_out[4], &self->buffer_in[2], MIN(self->buffer_in_len - 2, cmd_delete.out_len - 4));
|
||||
|
||||
elanmoc2_cmd_transceive(device, ssm, &cmd_delete, g_steal_pointer(&buffer_out));
|
||||
break;
|
||||
case CLEAR_STORAGE_CHECK_DELETED:
|
||||
if (self->buffer_in[1] != 0) {
|
||||
error = fpi_device_error_new_msg(FP_DEVICE_ERROR_GENERAL,
|
||||
"Failed to delete fingerprint");
|
||||
fpi_device_clear_storage_complete(device, error);
|
||||
fpi_ssm_mark_failed(g_steal_pointer(&self->ssm), error);
|
||||
break;
|
||||
}
|
||||
self->print_index++;
|
||||
fpi_ssm_jump_to_state(ssm, CLEAR_STORAGE_GET_FINGER_INFO);
|
||||
break;
|
||||
}
|
||||
|
||||
g_clear_pointer(&self->buffer_in, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
elanmoc2_clear_storage(FpDevice *device) {
|
||||
FpiDeviceElanMoC2 *self = FPI_DEVICE_ELANMOC2(device);
|
||||
fp_info("[elanmoc2] New clear storage operation");
|
||||
self->print_index = 0;
|
||||
self->ssm = fpi_ssm_new(device, elanmoc2_clear_storage_run_state, CLEAR_STORAGE_NUM_STATES);
|
||||
fpi_ssm_start(self->ssm, elanmoc2_ssm_completed_callback);
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_elanmoc2_init(FpiDeviceElanMoC2 *self) {
|
||||
G_DEBUG_HERE ();
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_elanmoc2_class_init(FpiDeviceElanMoC2Class *klass) {
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS(klass);
|
||||
|
||||
dev_class->id = FP_COMPONENT;
|
||||
dev_class->full_name = ELANMOC2_DRIVER_FULLNAME;
|
||||
|
||||
dev_class->type = FP_DEVICE_TYPE_USB;
|
||||
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
|
||||
dev_class->id_table = elanmoc2_id_table;
|
||||
|
||||
dev_class->nr_enroll_stages = ELANMOC2_ENROLL_TIMES;
|
||||
dev_class->temp_hot_seconds = -1;
|
||||
|
||||
dev_class->open = elanmoc2_open;
|
||||
dev_class->close = elanmoc2_close;
|
||||
dev_class->identify = elanmoc2_identify_verify;
|
||||
dev_class->verify = elanmoc2_identify_verify;
|
||||
dev_class->enroll = elanmoc2_enroll;
|
||||
dev_class->delete = elanmoc2_delete;
|
||||
dev_class->clear_storage = elanmoc2_clear_storage;
|
||||
dev_class->list = elanmoc2_list;
|
||||
dev_class->cancel = elanmoc2_cancel;
|
||||
|
||||
fpi_device_class_auto_initialize_features(dev_class);
|
||||
}
|
195
libfprint/drivers/elanmoc2/elanmoc2.h
Normal file
195
libfprint/drivers/elanmoc2/elanmoc2.h
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Driver for ELAN Match-On-Chip sensors
|
||||
* Copyright (C) 2021 Davide Depau
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Stdlib includes
|
||||
#include <stdbool.h>
|
||||
|
||||
// Library includes
|
||||
#include <libusb.h>
|
||||
|
||||
// Local includes
|
||||
#include "fpi-device.h"
|
||||
#include "fpi-ssm.h"
|
||||
|
||||
#define ELANMOC2_DRIVER_FULLNAME "ELAN Match-on-Chip 2"
|
||||
#define ELANMOC2_VEND_ID 0x04f3
|
||||
|
||||
#define ELANMOC2_ENROLL_TIMES 8
|
||||
#define ELANMOC2_CMD_MAX_LEN 2
|
||||
#define ELANMOC2_MAX_PRINTS 10
|
||||
|
||||
#define ELANMOC2_LIST_VERIFY_RETRIES 3
|
||||
|
||||
// USB parameters
|
||||
#define ELANMOC2_EP_CMD_OUT (0x1 | LIBUSB_ENDPOINT_OUT)
|
||||
#define ELANMOC2_EP_CMD_IN (0x3 | LIBUSB_ENDPOINT_IN)
|
||||
#define ELANMOC2_EP_MOC_CMD_IN (0x4 | LIBUSB_ENDPOINT_IN)
|
||||
#define ELANMOC2_USB_SEND_TIMEOUT 10000
|
||||
#define ELANMOC2_USB_RECV_TIMEOUT 10000
|
||||
#define ELANMOC2_USB_RECV_CANCELLABLE_TIMEOUT 5000
|
||||
|
||||
// Response codes
|
||||
#define ELANMOC2_RESP_MOVE_DOWN 0x41
|
||||
#define ELANMOC2_RESP_MOVE_RIGHT 0x42
|
||||
#define ELANMOC2_RESP_MOVE_UP 0x43
|
||||
#define ELANMOC2_RESP_MOVE_LEFT 0x44
|
||||
#define ELANMOC2_RESP_MAX_ENROLLED_REACHED 0xdd
|
||||
#define ELANMOC2_RESP_SENSOR_DIRTY 0xfb
|
||||
#define ELANMOC2_RESP_NOT_ENROLLED 0xfd
|
||||
#define ELANMOC2_RESP_NOT_ENOUGH_SURFACE 0xfe
|
||||
|
||||
// Currently only one device is supported, but I'd like to future-proof this driver for any new contributions.
|
||||
#define ELANMOC2_ALL_DEV 0
|
||||
#define ELANMOC2_DEV_0C4C (1 << 0)
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FpiDeviceElanMoC2, fpi_device_elanmoc2, FPI, DEVICE_ELANMOC2, FpDevice)
|
||||
|
||||
struct elanmoc2_cmd {
|
||||
unsigned char cmd[ELANMOC2_CMD_MAX_LEN];
|
||||
gboolean short_command;
|
||||
int out_len;
|
||||
int in_len;
|
||||
int ep_in;
|
||||
unsigned short devices;
|
||||
gboolean cancellable;
|
||||
};
|
||||
|
||||
|
||||
// Cancellable commands
|
||||
|
||||
static const struct elanmoc2_cmd cmd_identify = {
|
||||
.cmd = {0xff, 0x03},
|
||||
.out_len = 3,
|
||||
.in_len = 2,
|
||||
.ep_in = ELANMOC2_EP_MOC_CMD_IN,
|
||||
.cancellable = true,
|
||||
};
|
||||
|
||||
static const struct elanmoc2_cmd cmd_enroll = {
|
||||
.cmd = {0xff, 0x01},
|
||||
.out_len = 7,
|
||||
.in_len = 2,
|
||||
.ep_in = ELANMOC2_EP_MOC_CMD_IN,
|
||||
.cancellable = true,
|
||||
};
|
||||
|
||||
|
||||
// Not cancellable / quick commands
|
||||
|
||||
static const struct elanmoc2_cmd cmd_get_fw_ver = {
|
||||
.cmd = {0x19},
|
||||
.short_command = true,
|
||||
.out_len = 2,
|
||||
.in_len = 2,
|
||||
.ep_in = ELANMOC2_EP_CMD_IN,
|
||||
};
|
||||
|
||||
static const struct elanmoc2_cmd cmd_finger_info = {
|
||||
.cmd = {0xff, 0x12},
|
||||
.out_len = 4,
|
||||
.in_len = 64,
|
||||
.ep_in = ELANMOC2_EP_CMD_IN,
|
||||
};
|
||||
|
||||
static const struct elanmoc2_cmd cmd_get_enrolled_count = {
|
||||
.cmd = {0xff, 0x04},
|
||||
.out_len = 3,
|
||||
.in_len = 2,
|
||||
.ep_in = ELANMOC2_EP_CMD_IN,
|
||||
};
|
||||
|
||||
static const struct elanmoc2_cmd cmd_abort = {
|
||||
.cmd = {0xff, 0x02},
|
||||
.out_len = 3,
|
||||
.in_len = 2,
|
||||
.ep_in = ELANMOC2_EP_CMD_IN,
|
||||
};
|
||||
|
||||
static const struct elanmoc2_cmd cmd_commit = {
|
||||
.cmd = {0xff, 0x11},
|
||||
.out_len = 72,
|
||||
.in_len = 2,
|
||||
.ep_in = ELANMOC2_EP_CMD_IN,
|
||||
};
|
||||
|
||||
static const struct elanmoc2_cmd cmd_unk_after_enroll = {
|
||||
.cmd = {0xff, 0x10},
|
||||
.out_len = 3,
|
||||
.in_len = 3,
|
||||
.ep_in = ELANMOC2_EP_CMD_IN,
|
||||
};
|
||||
|
||||
static const struct elanmoc2_cmd cmd_delete = {
|
||||
.cmd = {0xff, 0x13},
|
||||
.out_len = 72,
|
||||
.in_len = 2,
|
||||
.ep_in = ELANMOC2_EP_CMD_IN,
|
||||
};
|
||||
|
||||
|
||||
enum identify_states {
|
||||
IDENTIFY_GET_NUM_ENROLLED,
|
||||
IDENTIFY_CHECK_NUM_ENROLLED,
|
||||
IDENTIFY_IDENTIFY,
|
||||
IDENTIFY_GET_FINGER_INFO,
|
||||
IDENTIFY_CHECK_FINGER_INFO,
|
||||
IDENTIFY_NUM_STATES
|
||||
};
|
||||
|
||||
enum list_states {
|
||||
LIST_GET_NUM_ENROLLED,
|
||||
LIST_CHECK_NUM_ENROLLED,
|
||||
LIST_GET_FINGER_INFO,
|
||||
LIST_CHECK_FINGER_INFO,
|
||||
LIST_NUM_STATES
|
||||
};
|
||||
|
||||
enum enroll_states {
|
||||
ENROLL_GET_NUM_ENROLLED,
|
||||
ENROLL_CHECK_NUM_ENROLLED,
|
||||
ENROLL_ENROLL,
|
||||
ENROLL_CHECK_ENROLLED,
|
||||
ENROLL_UNK_AFTER_ENROLL,
|
||||
ENROLL_COMMIT,
|
||||
ENROLL_CHECK_COMMITTED,
|
||||
ENROLL_NUM_STATES
|
||||
};
|
||||
|
||||
enum delete_states {
|
||||
DELETE_GET_NUM_ENROLLED,
|
||||
DELETE_DELETE,
|
||||
DELETE_CHECK_DELETED,
|
||||
DELETE_NUM_STATES
|
||||
};
|
||||
|
||||
enum clear_storage_states {
|
||||
CLEAR_STORAGE_GET_NUM_ENROLLED,
|
||||
CLEAR_STORAGE_CHECK_NUM_ENROLLED,
|
||||
CLEAR_STORAGE_GET_FINGER_INFO,
|
||||
CLEAR_STORAGE_DELETE,
|
||||
CLEAR_STORAGE_CHECK_DELETED,
|
||||
CLEAR_STORAGE_NUM_STATES
|
||||
};
|
||||
|
||||
static const FpIdEntry elanmoc2_id_table[] = {
|
||||
{.vid = ELANMOC2_VEND_ID, .pid = 0x0c4c, .driver_data = ELANMOC2_ALL_DEV},
|
||||
{.vid = 0, .pid = 0, .driver_data = ELANMOC2_DEV_0C4C}
|
||||
};
|
|
@ -1320,7 +1320,6 @@ gx_fp_probe (FpDevice *device)
|
|||
case 0x609C:
|
||||
case 0x639C:
|
||||
case 0x63AC:
|
||||
case 0x63BC:
|
||||
case 0x6A94:
|
||||
self->max_enroll_stage = 12;
|
||||
break;
|
||||
|
@ -1544,7 +1543,6 @@ static const FpIdEntry id_table[] = {
|
|||
{ .vid = 0x27c6, .pid = 0x60A2, },
|
||||
{ .vid = 0x27c6, .pid = 0x639C, },
|
||||
{ .vid = 0x27c6, .pid = 0x63AC, },
|
||||
{ .vid = 0x27c6, .pid = 0x63BC, },
|
||||
{ .vid = 0x27c6, .pid = 0x6496, },
|
||||
{ .vid = 0x27c6, .pid = 0x6584, },
|
||||
{ .vid = 0x27c6, .pid = 0x658C, },
|
||||
|
|
|
@ -40,9 +40,6 @@ static const FpIdEntry id_table[] = {
|
|||
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0100, },
|
||||
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00F0, },
|
||||
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0103, },
|
||||
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0123, },
|
||||
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0126, },
|
||||
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0129, },
|
||||
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
|
||||
};
|
||||
|
||||
|
|
|
@ -360,9 +360,9 @@ start_irq_handler (FpImageDevice *dev)
|
|||
transfer = fpi_usb_transfer_new (FP_DEVICE (dev));
|
||||
transfer->ssm = NULL;
|
||||
transfer->short_is_error = TRUE;
|
||||
fpi_usb_transfer_fill_interrupt (transfer,
|
||||
EP_INTR,
|
||||
IRQ_LENGTH);
|
||||
fpi_usb_transfer_fill_bulk (transfer,
|
||||
EP_INTR,
|
||||
IRQ_LENGTH);
|
||||
fpi_usb_transfer_submit (transfer, 0, self->irq_cancellable, irq_handler, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -245,7 +245,6 @@ fp_device_get_property (GObject *object,
|
|||
{
|
||||
FpDevice *self = FP_DEVICE (object);
|
||||
FpDevicePrivate *priv = fp_device_get_instance_private (self);
|
||||
FpDeviceClass *cls = FP_DEVICE_GET_CLASS (self);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
|
@ -285,24 +284,6 @@ fp_device_get_property (GObject *object,
|
|||
g_value_set_boolean (value, priv->is_removed);
|
||||
break;
|
||||
|
||||
case PROP_FPI_USB_DEVICE:
|
||||
g_value_set_object (value, priv->usb_device);
|
||||
break;
|
||||
|
||||
case PROP_FPI_UDEV_DATA_SPIDEV:
|
||||
if (cls->type == FP_DEVICE_TYPE_UDEV)
|
||||
g_value_set_string (value, g_strdup (priv->udev_data.spidev_path));
|
||||
else
|
||||
g_value_set_string (value, NULL);
|
||||
break;
|
||||
|
||||
case PROP_FPI_UDEV_DATA_HIDRAW:
|
||||
if (cls->type == FP_DEVICE_TYPE_UDEV)
|
||||
g_value_set_string (value, g_strdup (priv->udev_data.hidraw_path));
|
||||
else
|
||||
g_value_set_string (value, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
|
@ -549,7 +530,7 @@ fp_device_class_init (FpDeviceClass *klass)
|
|||
"USB Device",
|
||||
"Private: The USB device for the device",
|
||||
G_USB_TYPE_DEVICE,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
||||
/**
|
||||
* FpDevice::fpi-udev-data-spidev: (skip)
|
||||
*
|
||||
|
@ -562,7 +543,7 @@ fp_device_class_init (FpDeviceClass *klass)
|
|||
"Udev data: spidev path",
|
||||
"Private: The path to /dev/spidevN.M",
|
||||
NULL,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
||||
/**
|
||||
* FpDevice::fpi-udev-data-hidraw: (skip)
|
||||
*
|
||||
|
@ -575,7 +556,7 @@ fp_device_class_init (FpDeviceClass *klass)
|
|||
"Udev data: hidraw path",
|
||||
"Private: The path to /dev/hidrawN",
|
||||
NULL,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
/**
|
||||
* FpDevice::fpi-driver-data: (skip)
|
||||
|
|
|
@ -58,7 +58,6 @@ static const FpIdEntry whitelist_id_table[] = {
|
|||
{ .vid = 0x0a5c, .pid = 0x5841 },
|
||||
{ .vid = 0x0a5c, .pid = 0x5842 },
|
||||
{ .vid = 0x0a5c, .pid = 0x5843 },
|
||||
{ .vid = 0x0a5c, .pid = 0x5844 },
|
||||
{ .vid = 0x0a5c, .pid = 0x5845 },
|
||||
{ .vid = 0x10a5, .pid = 0x0007 },
|
||||
{ .vid = 0x1188, .pid = 0x9545 },
|
||||
|
@ -167,10 +166,7 @@ print_driver (const FpDeviceClass *cls)
|
|||
}
|
||||
|
||||
if (num_printed > 0)
|
||||
{
|
||||
g_print (" ID_AUTOSUSPEND=1\n");
|
||||
g_print (" ID_PERSIST=0\n");
|
||||
}
|
||||
g_print (" ID_AUTOSUSPEND=1\n");
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -125,6 +125,8 @@ driver_sources = {
|
|||
[ 'drivers/elan.c' ],
|
||||
'elanmoc' :
|
||||
[ 'drivers/elanmoc/elanmoc.c' ],
|
||||
'elanmoc2' :
|
||||
[ 'drivers/elanmoc2/elanmoc2.c' ],
|
||||
'elanspi' :
|
||||
[ 'drivers/elanspi.c' ],
|
||||
'nb1010' :
|
||||
|
|
|
@ -118,6 +118,7 @@ default_drivers = [
|
|||
'synaptics',
|
||||
'elan',
|
||||
'elanmoc',
|
||||
'elanmoc2',
|
||||
'uru4000',
|
||||
'upektc',
|
||||
'upeksonly',
|
||||
|
|
|
@ -15,23 +15,57 @@ script, capture it and store the capture to `custom.pcapng`.
|
|||
-----------------------
|
||||
A new 'capture' test is created by means of `capture.py` script:
|
||||
|
||||
1. Make sure that libfprint is built with support for the device driver
|
||||
that you want to capture a test case for.
|
||||
1. Create (if needed) a directory for the driver under `tests`
|
||||
directory:
|
||||
|
||||
2. From the build directory, run tests/create-driver-test.py as root. Note
|
||||
that if you're capturing data for a driver which already has a test case
|
||||
but the hardware is slightly different, you might want to pass a variant
|
||||
name as a command-line options, for example:
|
||||
```sh
|
||||
$ sudo tests/create-driver-test.py driver [variant]
|
||||
```
|
||||
`mkdir DRIVER`
|
||||
|
||||
3. If the capture is not successful, run the tool again to start another capture.
|
||||
Note that the name must be the exact name of the libfprint driver,
|
||||
or the exact name of the driver followed by a `-` and a unique identifier
|
||||
of your choosing.
|
||||
|
||||
4. Add driver test name to `drivers_tests` in the `meson.build`, as instructed,
|
||||
and change the ownership of the just-created test directory in the source.
|
||||
2. Prepare your execution environment.
|
||||
|
||||
5. Check whether `meson test` passes with this new test.
|
||||
In the next step a working and up to date libfprint is needed. This can be
|
||||
achieved by installing it into your system. Alternatively, you can set
|
||||
the following environment variables to run a local build:
|
||||
- `export LD_PRELOAD=<meson-build-dir>/libfprint/libfprint-2.so`
|
||||
- `export GI_TYPELIB_PATH=<meson-build-dir>/libfprint`
|
||||
|
||||
Also, sometimes the driver must be adapted to the emulated environment
|
||||
(mainly if it uses random numbers, see `synaptics.c` for an example).
|
||||
Set the following environment variable to enable this adaptation:
|
||||
- `export FP_DEVICE_EMULATION=1`
|
||||
|
||||
Run the next steps in the same terminal.
|
||||
|
||||
3. Find the real USB fingerprint device with `lsusb`, e.g.:
|
||||
|
||||
`Bus 001 Device 005: ID 138a:0090 Validity Sensors, Inc. VFS7500 Touch Fingerprint Sensor`
|
||||
|
||||
The following USB device is used in the example above:
|
||||
`/dev/bus/usb/001/005`.
|
||||
|
||||
For the following commands, it is assumed that the user that's
|
||||
running the commands has full access to the device node, whether
|
||||
by running the commands as `root`, or changing the permissions for
|
||||
that device node.
|
||||
|
||||
4. Record information about this device:
|
||||
|
||||
`umockdev-record /dev/bus/usb/001/005 > DRIVER/device`
|
||||
|
||||
5. Record interaction of `capture.py` (or other test) with the device. To do
|
||||
so, start wireshark and record `usbmonX` (where X is the bus number). Then
|
||||
run the test script:
|
||||
|
||||
`python3 ./capture.py DRIVER/capture.png`
|
||||
|
||||
Save the wireshark recording as `capture.pcapng`. The command will create
|
||||
`capture.png`.
|
||||
|
||||
6. Add driver's name to `drivers_tests` in the `meson.build`.
|
||||
7. Check whether everything works as expected.
|
||||
|
||||
**Note.** To avoid submitting a real fingerprint, the side of finger,
|
||||
arm, or anything else producing an image with the device can be used.
|
||||
|
|
|
@ -1,162 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
BUILDDIR='@BUILDDIR@'
|
||||
SRCDIR='@SRCDIR@'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
library_path = BUILDDIR + '/libfprint/'
|
||||
|
||||
# Relaunch ourselves with a changed environment so
|
||||
# that we're loading the development version of libfprint
|
||||
if 'LD_LIBRARY_PATH' not in os.environ or not library_path in os.environ['LD_LIBRARY_PATH']:
|
||||
os.environ['LD_LIBRARY_PATH'] = library_path
|
||||
os.environ['GI_TYPELIB_PATH'] = f'{BUILDDIR}/libfprint/'
|
||||
os.environ['FP_DEVICE_EMULATION'] = '1'
|
||||
try:
|
||||
os.execv(sys.argv[0], sys.argv)
|
||||
except Exception as e:
|
||||
print('Could not run script with new library path')
|
||||
sys.exit(1)
|
||||
|
||||
import gi
|
||||
gi.require_version('FPrint', '2.0')
|
||||
from gi.repository import FPrint
|
||||
|
||||
gi.require_version('GUsb', '1.0')
|
||||
from gi.repository import GUsb
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
def print_usage():
|
||||
print(f'Usage: {sys.argv[0]} driver [test-variant-name]')
|
||||
print('A test variant name is optional, and must be all lower case letters, or dashes, with no spaces')
|
||||
print(f'The captured data will be stored in {SRCDIR}/tests/[driver name]-[test variant name]')
|
||||
|
||||
if len(sys.argv) > 3:
|
||||
print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
driver_name = sys.argv[1]
|
||||
os.environ['FP_DRIVERS_WHITELIST'] = driver_name
|
||||
|
||||
test_variant = None
|
||||
if len(sys.argv) == 3:
|
||||
valid_re = re.compile('[a-z-]*')
|
||||
test_variant = sys.argv[2]
|
||||
if (not valid_re.match(test_variant) or
|
||||
test_variant.startswith('-') or
|
||||
test_variant.endswith('-')):
|
||||
print(f'Invalid variant name {test_variant}\n')
|
||||
print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
# Check that running as root
|
||||
|
||||
if os.geteuid() != 0:
|
||||
print(f'{sys.argv[0]} is expected to be run as root')
|
||||
sys.exit(1)
|
||||
|
||||
# Check that tshark is available
|
||||
|
||||
tshark = shutil.which('tshark')
|
||||
if not tshark:
|
||||
print("The 'tshark' WireShark command-line tool must be installed to capture USB traffic")
|
||||
sys.exit(1)
|
||||
|
||||
# Find the fingerprint reader
|
||||
ctx = FPrint.Context()
|
||||
ctx.enumerate()
|
||||
devices = ctx.get_devices()
|
||||
if len(devices) == 0:
|
||||
print('Could not find a supported fingerprint reader')
|
||||
sys.exit(1)
|
||||
elif len(devices) > 1:
|
||||
print('Capture requires a single supported fingerprint reader to be plugged in')
|
||||
sys.exit(1)
|
||||
|
||||
test_name = driver_name
|
||||
if test_variant:
|
||||
test_name = driver_name + '-' + test_variant
|
||||
usb_device = devices[0].get_property('fpi-usb-device')
|
||||
bus_num = usb_device.get_bus()
|
||||
device_num = usb_device.get_address()
|
||||
|
||||
print(f'### Detected USB device /dev/bus/usb/{bus_num:03d}/{device_num:03d}')
|
||||
|
||||
# Make directory
|
||||
|
||||
test_dir = SRCDIR + '/tests/' + test_name
|
||||
os.makedirs(test_dir, mode=0o775, exist_ok=True)
|
||||
|
||||
# Capture device info
|
||||
|
||||
args = ['umockdev-record', f'/dev/bus/usb/{bus_num:03d}/{device_num:03d}']
|
||||
device_out = open(test_dir + '/device', 'w')
|
||||
process = subprocess.Popen(args, stdout=device_out)
|
||||
process.wait()
|
||||
|
||||
# Run capture
|
||||
# https://osqa-ask.wireshark.org/questions/53919/how-can-i-precisely-specify-a-usb-device-to-capture-with-tshark/
|
||||
|
||||
print(f'### Starting USB capture on usbmon{bus_num}')
|
||||
capture_pid = os.fork()
|
||||
assert(capture_pid >= 0)
|
||||
|
||||
unfiltered_cap_path = os.path.join(tempfile.gettempdir(), 'capture-unfiltered.pcapng')
|
||||
if capture_pid == 0:
|
||||
os.setpgrp()
|
||||
args = ['tshark', '-q', '-i', f'usbmon{bus_num}', '-w', unfiltered_cap_path]
|
||||
os.execv(tshark, args)
|
||||
|
||||
# Wait 1 sec to settle (we can assume setpgrp happened)
|
||||
time.sleep(1)
|
||||
|
||||
print('### Capturing fingerprint, please swipe or press your finger on the reader')
|
||||
with subprocess.Popen(['python3', SRCDIR + '/tests/capture.py', test_dir + '/capture.png']) as capture_process:
|
||||
capture_process.wait()
|
||||
if capture_process.returncode != 0:
|
||||
print('Failed to capture fingerprint')
|
||||
os.killpg(capture_pid, signal.SIGKILL)
|
||||
sys.exit(1)
|
||||
|
||||
def t_waitpid(pid, timeout):
|
||||
timeout = time.time() + timeout
|
||||
r = os.waitpid(pid, os.WNOHANG)
|
||||
while timeout > time.time() and r[0] == 0:
|
||||
time.sleep(0.1)
|
||||
r = os.waitpid(pid, os.WNOHANG)
|
||||
|
||||
return r
|
||||
|
||||
os.kill(capture_pid, signal.SIGTERM)
|
||||
try:
|
||||
r = t_waitpid(capture_pid, 2)
|
||||
# Kill if nothing died
|
||||
if r[0] == 0:
|
||||
os.kill(capture_pid, signal.SIGKILL)
|
||||
except ChildProcessError:
|
||||
pass
|
||||
|
||||
try:
|
||||
while True:
|
||||
r = t_waitpid(-capture_pid, timeout=2)
|
||||
# Kill the process group, if nothing died (and there are children)
|
||||
if r[0] == 0:
|
||||
os.killpg(capture_pid, signal.SIGKILL)
|
||||
except ChildProcessError:
|
||||
pass
|
||||
|
||||
# Filter the capture
|
||||
print(f'\n### Saving USB capture as test case {test_name}')
|
||||
args = ['tshark', '-r', unfiltered_cap_path, '-Y', f'usb.bus_id == {bus_num} and usb.device_address == {device_num}',
|
||||
'-w', test_dir + '/capture.pcapng']
|
||||
with subprocess.Popen(args, stderr=subprocess.DEVNULL) as filter_process:
|
||||
filter_process.wait()
|
||||
|
||||
print(f"\nDone! Don't forget to add {test_name} to tests/meson.build")
|
|
@ -30,8 +30,6 @@ drivers_tests = [
|
|||
'elanspi',
|
||||
'synaptics',
|
||||
'upektc_img',
|
||||
'uru4000-msv2',
|
||||
'uru4000-4500',
|
||||
'vfs0050',
|
||||
'vfs301',
|
||||
'vfs5011',
|
||||
|
@ -41,15 +39,6 @@ drivers_tests = [
|
|||
'egis0570',
|
||||
]
|
||||
|
||||
if get_option('introspection')
|
||||
conf = configuration_data()
|
||||
conf.set('SRCDIR', meson.source_root())
|
||||
conf.set('BUILDDIR', meson.build_root())
|
||||
configure_file(configuration: conf,
|
||||
input: 'create-driver-test.py.in',
|
||||
output: 'create-driver-test.py')
|
||||
endif
|
||||
|
||||
if get_option('introspection')
|
||||
envs.prepend('GI_TYPELIB_PATH', join_paths(meson.build_root(), 'libfprint'))
|
||||
virtual_devices_tests = [
|
||||
|
|
|
@ -18,7 +18,7 @@ try:
|
|||
if version < (0, 13, 2):
|
||||
print('umockdev is too old for test to be reliable, expect random failures!')
|
||||
print('Please update umockdev to at least 0.13.2.')
|
||||
pcap_supported = version >= (0, 16, 3) or os.getenv('CI_PROJECT_NAME') == "libfprint"
|
||||
pcap_supported = version >= (0, 16, 2) or os.getenv('CI_PROJECT_NAME') == "libfprint"
|
||||
spi_supported = version >= (0, 16) or os.getenv('CI_PROJECT_NAME') == "libfprint"
|
||||
|
||||
except FileNotFoundError:
|
||||
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 94 KiB |
|
@ -1,220 +0,0 @@
|
|||
P: /devices/pci0000:00/0000:00:14.0/usb1/1-10
|
||||
N: bus/usb/001/050=1201000200000040BA050A000301010203010902200001010080640904000002FFFFFF000705810340000807058202400000
|
||||
E: DEVNAME=/dev/bus/usb/001/050
|
||||
E: DEVTYPE=usb_device
|
||||
E: DRIVER=usb
|
||||
E: PRODUCT=5ba/a/103
|
||||
E: TYPE=0/0/0
|
||||
E: BUSNUM=001
|
||||
E: DEVNUM=050
|
||||
E: MAJOR=189
|
||||
E: MINOR=49
|
||||
E: SUBSYSTEM=usb
|
||||
E: ID_VENDOR=DigitalPersona__Inc.
|
||||
E: ID_VENDOR_ENC=DigitalPersona\x2c\x20Inc.
|
||||
E: ID_VENDOR_ID=05ba
|
||||
E: ID_MODEL=U.are.U®_4500_Fingerprint_Reader
|
||||
E: ID_MODEL_ENC=U.are.U®\x204500\x20Fingerprint\x20Reader
|
||||
E: ID_MODEL_ID=000a
|
||||
E: ID_REVISION=0103
|
||||
E: ID_SERIAL=DigitalPersona__Inc._U.are.U®_4500_Fingerprint_Reader__FB0B9071-2E08-7742-BC16-2FAA247CEF66_
|
||||
E: ID_SERIAL_SHORT=_FB0B9071-2E08-7742-BC16-2FAA247CEF66_
|
||||
E: ID_BUS=usb
|
||||
E: ID_USB_INTERFACES=:ffffff:
|
||||
E: ID_VENDOR_FROM_DATABASE=DigitalPersona, Inc.
|
||||
E: ID_AUTOSUSPEND=1
|
||||
E: ID_MODEL_FROM_DATABASE=Fingerprint Reader
|
||||
E: ID_PATH=pci-0000:00:14.0-usb-0:10
|
||||
E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_10
|
||||
E: LIBFPRINT_DRIVER=Digital Persona U.are.U 4000/4000B/4500
|
||||
E: ID_FOR_SEAT=usb-pci-0000_00_14_0-usb-0_10
|
||||
E: TAGS=:seat:
|
||||
E: CURRENT_TAGS=:seat:
|
||||
A: authorized=1\n
|
||||
A: avoid_reset_quirk=0\n
|
||||
A: bConfigurationValue=1\n
|
||||
A: bDeviceClass=00\n
|
||||
A: bDeviceProtocol=00\n
|
||||
A: bDeviceSubClass=00\n
|
||||
A: bMaxPacketSize0=64\n
|
||||
A: bMaxPower=200mA\n
|
||||
A: bNumConfigurations=1\n
|
||||
A: bNumInterfaces= 1\n
|
||||
A: bcdDevice=0103\n
|
||||
A: bmAttributes=80\n
|
||||
A: busnum=1\n
|
||||
A: configuration=
|
||||
H: descriptors=1201000200000040BA050A000301010203010902200001010080640904000002FFFFFF000705810340000807058202400000
|
||||
A: dev=189:49\n
|
||||
A: devnum=50\n
|
||||
A: devpath=10\n
|
||||
L: driver=../../../../../bus/usb/drivers/usb
|
||||
L: firmware_node=../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4c/device:4d/device:57
|
||||
A: idProduct=000a\n
|
||||
A: idVendor=05ba\n
|
||||
A: ltm_capable=no\n
|
||||
A: manufacturer=DigitalPersona, Inc.\n
|
||||
A: maxchild=0\n
|
||||
L: port=../1-0:1.0/usb1-port10
|
||||
A: power/active_duration=2761\n
|
||||
A: power/autosuspend=2\n
|
||||
A: power/autosuspend_delay_ms=2000\n
|
||||
A: power/connected_duration=118841\n
|
||||
A: power/control=auto\n
|
||||
A: power/level=auto\n
|
||||
A: power/persist=0\n
|
||||
A: power/runtime_active_time=2616\n
|
||||
A: power/runtime_status=active\n
|
||||
A: power/runtime_suspended_time=115982\n
|
||||
A: product=U.are.U\302\256 4500 Fingerprint Reader\n
|
||||
A: quirks=0x0\n
|
||||
A: removable=removable\n
|
||||
A: rx_lanes=1\n
|
||||
A: serial={FB0B9071-2E08-7742-BC16-2FAA247CEF66}\n
|
||||
A: speed=12\n
|
||||
A: tx_lanes=1\n
|
||||
A: urbnum=13\n
|
||||
A: version= 2.00\n
|
||||
|
||||
P: /devices/pci0000:00/0000:00:14.0/usb1
|
||||
N: bus/usb/001/001=12010002090001406B1D020013050302010109021900010100E0000904000001090000000705810304000C
|
||||
E: DEVNAME=/dev/bus/usb/001/001
|
||||
E: DEVTYPE=usb_device
|
||||
E: DRIVER=usb
|
||||
E: PRODUCT=1d6b/2/513
|
||||
E: TYPE=9/0/1
|
||||
E: BUSNUM=001
|
||||
E: DEVNUM=001
|
||||
E: MAJOR=189
|
||||
E: MINOR=0
|
||||
E: SUBSYSTEM=usb
|
||||
E: ID_VENDOR=Linux_5.13.12-200.fc34.x86_64_xhci-hcd
|
||||
E: ID_VENDOR_ENC=Linux\x205.13.12-200.fc34.x86_64\x20xhci-hcd
|
||||
E: ID_VENDOR_ID=1d6b
|
||||
E: ID_MODEL=xHCI_Host_Controller
|
||||
E: ID_MODEL_ENC=xHCI\x20Host\x20Controller
|
||||
E: ID_MODEL_ID=0002
|
||||
E: ID_REVISION=0513
|
||||
E: ID_SERIAL=Linux_5.13.12-200.fc34.x86_64_xhci-hcd_xHCI_Host_Controller_0000:00:14.0
|
||||
E: ID_SERIAL_SHORT=0000:00:14.0
|
||||
E: ID_BUS=usb
|
||||
E: ID_USB_INTERFACES=:090000:
|
||||
E: ID_VENDOR_FROM_DATABASE=Linux Foundation
|
||||
E: ID_AUTOSUSPEND=1
|
||||
E: ID_MODEL_FROM_DATABASE=2.0 root hub
|
||||
E: ID_PATH=pci-0000:00:14.0
|
||||
E: ID_PATH_TAG=pci-0000_00_14_0
|
||||
E: ID_FOR_SEAT=usb-pci-0000_00_14_0
|
||||
E: TAGS=:seat:
|
||||
E: CURRENT_TAGS=:seat:
|
||||
A: authorized=1\n
|
||||
A: authorized_default=1\n
|
||||
A: avoid_reset_quirk=0\n
|
||||
A: bConfigurationValue=1\n
|
||||
A: bDeviceClass=09\n
|
||||
A: bDeviceProtocol=01\n
|
||||
A: bDeviceSubClass=00\n
|
||||
A: bMaxPacketSize0=64\n
|
||||
A: bMaxPower=0mA\n
|
||||
A: bNumConfigurations=1\n
|
||||
A: bNumInterfaces= 1\n
|
||||
A: bcdDevice=0513\n
|
||||
A: bmAttributes=e0\n
|
||||
A: busnum=1\n
|
||||
A: configuration=
|
||||
H: descriptors=12010002090001406B1D020013050302010109021900010100E0000904000001090000000705810304000C
|
||||
A: dev=189:0\n
|
||||
A: devnum=1\n
|
||||
A: devpath=0\n
|
||||
L: driver=../../../../bus/usb/drivers/usb
|
||||
L: firmware_node=../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4c/device:4d
|
||||
A: idProduct=0002\n
|
||||
A: idVendor=1d6b\n
|
||||
A: interface_authorized_default=1\n
|
||||
A: ltm_capable=no\n
|
||||
A: manufacturer=Linux 5.13.12-200.fc34.x86_64 xhci-hcd\n
|
||||
A: maxchild=16\n
|
||||
A: power/active_duration=837797629\n
|
||||
A: power/autosuspend=0\n
|
||||
A: power/autosuspend_delay_ms=0\n
|
||||
A: power/connected_duration=837797629\n
|
||||
A: power/control=auto\n
|
||||
A: power/level=auto\n
|
||||
A: power/runtime_active_time=837797626\n
|
||||
A: power/runtime_status=active\n
|
||||
A: power/runtime_suspended_time=0\n
|
||||
A: power/wakeup=disabled\n
|
||||
A: power/wakeup_abort_count=\n
|
||||
A: power/wakeup_active=\n
|
||||
A: power/wakeup_active_count=\n
|
||||
A: power/wakeup_count=\n
|
||||
A: power/wakeup_expire_count=\n
|
||||
A: power/wakeup_last_time_ms=\n
|
||||
A: power/wakeup_max_time_ms=\n
|
||||
A: power/wakeup_total_time_ms=\n
|
||||
A: product=xHCI Host Controller\n
|
||||
A: quirks=0x0\n
|
||||
A: removable=unknown\n
|
||||
A: rx_lanes=1\n
|
||||
A: serial=0000:00:14.0\n
|
||||
A: speed=480\n
|
||||
A: tx_lanes=1\n
|
||||
A: urbnum=1498\n
|
||||
A: version= 2.00\n
|
||||
|
||||
P: /devices/pci0000:00/0000:00:14.0
|
||||
E: DRIVER=xhci_hcd
|
||||
E: PCI_CLASS=C0330
|
||||
E: PCI_ID=8086:A36D
|
||||
E: PCI_SUBSYS_ID=17AA:312A
|
||||
E: PCI_SLOT_NAME=0000:00:14.0
|
||||
E: MODALIAS=pci:v00008086d0000A36Dsv000017AAsd0000312Abc0Csc03i30
|
||||
E: SUBSYSTEM=pci
|
||||
E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller
|
||||
E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller
|
||||
E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI
|
||||
E: ID_VENDOR_FROM_DATABASE=Intel Corporation
|
||||
E: ID_MODEL_FROM_DATABASE=Cannon Lake PCH USB 3.1 xHCI Host Controller
|
||||
A: ari_enabled=0\n
|
||||
A: broken_parity_status=0\n
|
||||
A: class=0x0c0330\n
|
||||
H: config=86806DA3060490021030030C00008000040032B1000000000000000000000000000000000000000000000000AA172A31000000007000000000000000FF010000FD0134808FC6FF8300000000000000007F6DDC0F000000009A1CF40100000000316000000000000000000000000000000180C2C1080000000000000000000000059087009802E0FE0000000000000000090014F01000400100000000C10A080000080E00001800008F400200000100006000000008020000010000000400000090000000001800000005000000000000000300000C0000004000000080000000030000000000000000000000000000000000000000000000B50F110112000000
|
||||
A: consistent_dma_mask_bits=64\n
|
||||
A: d3cold_allowed=1\n
|
||||
A: dbc=disabled\n
|
||||
A: device=0xa36d\n
|
||||
A: dma_mask_bits=64\n
|
||||
L: driver=../../../bus/pci/drivers/xhci_hcd
|
||||
A: driver_override=(null)\n
|
||||
A: enable=1\n
|
||||
L: firmware_node=../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4c
|
||||
A: index=3\n
|
||||
A: irq=125\n
|
||||
A: label=Onboard - Other\n
|
||||
A: local_cpulist=0-5\n
|
||||
A: local_cpus=3f\n
|
||||
A: modalias=pci:v00008086d0000A36Dsv000017AAsd0000312Abc0Csc03i30\n
|
||||
A: msi_bus=1\n
|
||||
A: msi_irqs/125=msi\n
|
||||
A: numa_node=-1\n
|
||||
A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 15 20 2112 20\nxHCI ring segments 46 76 4096 76\nbuffer-2048 0 32 2048 16\nbuffer-512 0 32 512 4\nbuffer-128 3 32 128 1\nbuffer-32 0 128 32 1\n
|
||||
A: power/control=on\n
|
||||
A: power/runtime_active_time=837797789\n
|
||||
A: power/runtime_status=active\n
|
||||
A: power/runtime_suspended_time=0\n
|
||||
A: power/wakeup=enabled\n
|
||||
A: power/wakeup_abort_count=0\n
|
||||
A: power/wakeup_active=0\n
|
||||
A: power/wakeup_active_count=67\n
|
||||
A: power/wakeup_count=0\n
|
||||
A: power/wakeup_expire_count=67\n
|
||||
A: power/wakeup_last_time_ms=835747082\n
|
||||
A: power/wakeup_max_time_ms=108\n
|
||||
A: power/wakeup_total_time_ms=6974\n
|
||||
A: power_state=D0\n
|
||||
A: resource=0x00000000b1320000 0x00000000b132ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n
|
||||
A: revision=0x10\n
|
||||
A: subsystem_device=0x312a\n
|
||||
A: subsystem_vendor=0x17aa\n
|
||||
A: vendor=0x8086\n
|
||||
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 106 KiB |
|
@ -1,220 +0,0 @@
|
|||
P: /devices/pci0000:00/0000:00:14.0/usb1/1-10
|
||||
N: bus/usb/001/047=12010002000000405E04CA000001010203010902200001010080820904000002FFFFFF000705810340000807058202400000
|
||||
E: DEVNAME=/dev/bus/usb/001/047
|
||||
E: DEVTYPE=usb_device
|
||||
E: DRIVER=usb
|
||||
E: PRODUCT=45e/ca/100
|
||||
E: TYPE=0/0/0
|
||||
E: BUSNUM=001
|
||||
E: DEVNUM=047
|
||||
E: MAJOR=189
|
||||
E: MINOR=46
|
||||
E: SUBSYSTEM=usb
|
||||
E: ID_VENDOR=Microsoft
|
||||
E: ID_VENDOR_ENC=Microsoft
|
||||
E: ID_VENDOR_ID=045e
|
||||
E: ID_MODEL=Microsoft®_Fingerprint_Reader
|
||||
E: ID_MODEL_ENC=Microsoft®\x20Fingerprint\x20Reader
|
||||
E: ID_MODEL_ID=00ca
|
||||
E: ID_REVISION=0100
|
||||
E: ID_SERIAL=Microsoft_Microsoft®_Fingerprint_Reader__BE815DAD-15E4-0745-AA30-41DEBCAC5913_
|
||||
E: ID_SERIAL_SHORT=_BE815DAD-15E4-0745-AA30-41DEBCAC5913_
|
||||
E: ID_BUS=usb
|
||||
E: ID_USB_INTERFACES=:ffffff:
|
||||
E: ID_VENDOR_FROM_DATABASE=Microsoft Corp.
|
||||
E: ID_AUTOSUSPEND=1
|
||||
E: ID_MODEL_FROM_DATABASE=Fingerprint Reader
|
||||
E: ID_PATH=pci-0000:00:14.0-usb-0:10
|
||||
E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_10
|
||||
E: LIBFPRINT_DRIVER=Digital Persona U.are.U 4000/4000B/4500
|
||||
E: ID_FOR_SEAT=usb-pci-0000_00_14_0-usb-0_10
|
||||
E: TAGS=:seat:
|
||||
E: CURRENT_TAGS=:seat:
|
||||
A: authorized=1\n
|
||||
A: avoid_reset_quirk=0\n
|
||||
A: bConfigurationValue=1\n
|
||||
A: bDeviceClass=00\n
|
||||
A: bDeviceProtocol=00\n
|
||||
A: bDeviceSubClass=00\n
|
||||
A: bMaxPacketSize0=64\n
|
||||
A: bMaxPower=260mA\n
|
||||
A: bNumConfigurations=1\n
|
||||
A: bNumInterfaces= 1\n
|
||||
A: bcdDevice=0100\n
|
||||
A: bmAttributes=80\n
|
||||
A: busnum=1\n
|
||||
A: configuration=
|
||||
H: descriptors=12010002000000405E04CA000001010203010902200001010080820904000002FFFFFF000705810340000807058202400000
|
||||
A: dev=189:46\n
|
||||
A: devnum=47\n
|
||||
A: devpath=10\n
|
||||
L: driver=../../../../../bus/usb/drivers/usb
|
||||
L: firmware_node=../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4c/device:4d/device:57
|
||||
A: idProduct=00ca\n
|
||||
A: idVendor=045e\n
|
||||
A: ltm_capable=no\n
|
||||
A: manufacturer=Microsoft\n
|
||||
A: maxchild=0\n
|
||||
L: port=../1-0:1.0/usb1-port10
|
||||
A: power/active_duration=31642\n
|
||||
A: power/autosuspend=2\n
|
||||
A: power/autosuspend_delay_ms=2000\n
|
||||
A: power/connected_duration=1177852\n
|
||||
A: power/control=auto\n
|
||||
A: power/level=auto\n
|
||||
A: power/persist=0\n
|
||||
A: power/runtime_active_time=31877\n
|
||||
A: power/runtime_status=active\n
|
||||
A: power/runtime_suspended_time=1145731\n
|
||||
A: product=Microsoft\302\256 Fingerprint Reader\n
|
||||
A: quirks=0x0\n
|
||||
A: removable=removable\n
|
||||
A: rx_lanes=1\n
|
||||
A: serial={BE815DAD-15E4-0745-AA30-41DEBCAC5913}\n
|
||||
A: speed=12\n
|
||||
A: tx_lanes=1\n
|
||||
A: urbnum=183\n
|
||||
A: version= 2.00\n
|
||||
|
||||
P: /devices/pci0000:00/0000:00:14.0/usb1
|
||||
N: bus/usb/001/001=12010002090001406B1D020013050302010109021900010100E0000904000001090000000705810304000C
|
||||
E: DEVNAME=/dev/bus/usb/001/001
|
||||
E: DEVTYPE=usb_device
|
||||
E: DRIVER=usb
|
||||
E: PRODUCT=1d6b/2/513
|
||||
E: TYPE=9/0/1
|
||||
E: BUSNUM=001
|
||||
E: DEVNUM=001
|
||||
E: MAJOR=189
|
||||
E: MINOR=0
|
||||
E: SUBSYSTEM=usb
|
||||
E: ID_VENDOR=Linux_5.13.12-200.fc34.x86_64_xhci-hcd
|
||||
E: ID_VENDOR_ENC=Linux\x205.13.12-200.fc34.x86_64\x20xhci-hcd
|
||||
E: ID_VENDOR_ID=1d6b
|
||||
E: ID_MODEL=xHCI_Host_Controller
|
||||
E: ID_MODEL_ENC=xHCI\x20Host\x20Controller
|
||||
E: ID_MODEL_ID=0002
|
||||
E: ID_REVISION=0513
|
||||
E: ID_SERIAL=Linux_5.13.12-200.fc34.x86_64_xhci-hcd_xHCI_Host_Controller_0000:00:14.0
|
||||
E: ID_SERIAL_SHORT=0000:00:14.0
|
||||
E: ID_BUS=usb
|
||||
E: ID_USB_INTERFACES=:090000:
|
||||
E: ID_VENDOR_FROM_DATABASE=Linux Foundation
|
||||
E: ID_AUTOSUSPEND=1
|
||||
E: ID_MODEL_FROM_DATABASE=2.0 root hub
|
||||
E: ID_PATH=pci-0000:00:14.0
|
||||
E: ID_PATH_TAG=pci-0000_00_14_0
|
||||
E: ID_FOR_SEAT=usb-pci-0000_00_14_0
|
||||
E: TAGS=:seat:
|
||||
E: CURRENT_TAGS=:seat:
|
||||
A: authorized=1\n
|
||||
A: authorized_default=1\n
|
||||
A: avoid_reset_quirk=0\n
|
||||
A: bConfigurationValue=1\n
|
||||
A: bDeviceClass=09\n
|
||||
A: bDeviceProtocol=01\n
|
||||
A: bDeviceSubClass=00\n
|
||||
A: bMaxPacketSize0=64\n
|
||||
A: bMaxPower=0mA\n
|
||||
A: bNumConfigurations=1\n
|
||||
A: bNumInterfaces= 1\n
|
||||
A: bcdDevice=0513\n
|
||||
A: bmAttributes=e0\n
|
||||
A: busnum=1\n
|
||||
A: configuration=
|
||||
H: descriptors=12010002090001406B1D020013050302010109021900010100E0000904000001090000000705810304000C
|
||||
A: dev=189:0\n
|
||||
A: devnum=1\n
|
||||
A: devpath=0\n
|
||||
L: driver=../../../../bus/usb/drivers/usb
|
||||
L: firmware_node=../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4c/device:4d
|
||||
A: idProduct=0002\n
|
||||
A: idVendor=1d6b\n
|
||||
A: interface_authorized_default=1\n
|
||||
A: ltm_capable=no\n
|
||||
A: manufacturer=Linux 5.13.12-200.fc34.x86_64 xhci-hcd\n
|
||||
A: maxchild=16\n
|
||||
A: power/active_duration=775798957\n
|
||||
A: power/autosuspend=0\n
|
||||
A: power/autosuspend_delay_ms=0\n
|
||||
A: power/connected_duration=775798957\n
|
||||
A: power/control=auto\n
|
||||
A: power/level=auto\n
|
||||
A: power/runtime_active_time=775798954\n
|
||||
A: power/runtime_status=active\n
|
||||
A: power/runtime_suspended_time=0\n
|
||||
A: power/wakeup=disabled\n
|
||||
A: power/wakeup_abort_count=\n
|
||||
A: power/wakeup_active=\n
|
||||
A: power/wakeup_active_count=\n
|
||||
A: power/wakeup_count=\n
|
||||
A: power/wakeup_expire_count=\n
|
||||
A: power/wakeup_last_time_ms=\n
|
||||
A: power/wakeup_max_time_ms=\n
|
||||
A: power/wakeup_total_time_ms=\n
|
||||
A: product=xHCI Host Controller\n
|
||||
A: quirks=0x0\n
|
||||
A: removable=unknown\n
|
||||
A: rx_lanes=1\n
|
||||
A: serial=0000:00:14.0\n
|
||||
A: speed=480\n
|
||||
A: tx_lanes=1\n
|
||||
A: urbnum=1381\n
|
||||
A: version= 2.00\n
|
||||
|
||||
P: /devices/pci0000:00/0000:00:14.0
|
||||
E: DRIVER=xhci_hcd
|
||||
E: PCI_CLASS=C0330
|
||||
E: PCI_ID=8086:A36D
|
||||
E: PCI_SUBSYS_ID=17AA:312A
|
||||
E: PCI_SLOT_NAME=0000:00:14.0
|
||||
E: MODALIAS=pci:v00008086d0000A36Dsv000017AAsd0000312Abc0Csc03i30
|
||||
E: SUBSYSTEM=pci
|
||||
E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller
|
||||
E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller
|
||||
E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI
|
||||
E: ID_VENDOR_FROM_DATABASE=Intel Corporation
|
||||
E: ID_MODEL_FROM_DATABASE=Cannon Lake PCH USB 3.1 xHCI Host Controller
|
||||
A: ari_enabled=0\n
|
||||
A: broken_parity_status=0\n
|
||||
A: class=0x0c0330\n
|
||||
H: config=86806DA3060490021030030C00008000040032B1000000000000000000000000000000000000000000000000AA172A31000000007000000000000000FF010000FD0134808FC6FF8300000000000000007F6DDC0F00000000970B083900000000316000000000000000000000000000000180C2C1080000000000000000000000059087009802E0FE0000000000000000090014F01000400100000000C10A080000080E00001800008F400200000100006000000008020000010000000400000090000000001800000005000000000000000300000C0000004000000080000000030000000000000000000000000000000000000000000000B50F110112000000
|
||||
A: consistent_dma_mask_bits=64\n
|
||||
A: d3cold_allowed=1\n
|
||||
A: dbc=disabled\n
|
||||
A: device=0xa36d\n
|
||||
A: dma_mask_bits=64\n
|
||||
L: driver=../../../bus/pci/drivers/xhci_hcd
|
||||
A: driver_override=(null)\n
|
||||
A: enable=1\n
|
||||
L: firmware_node=../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4c
|
||||
A: index=3\n
|
||||
A: irq=125\n
|
||||
A: label=Onboard - Other\n
|
||||
A: local_cpulist=0-5\n
|
||||
A: local_cpus=3f\n
|
||||
A: modalias=pci:v00008086d0000A36Dsv000017AAsd0000312Abc0Csc03i30\n
|
||||
A: msi_bus=1\n
|
||||
A: msi_irqs/125=msi\n
|
||||
A: numa_node=-1\n
|
||||
A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 15 20 2112 20\nxHCI ring segments 46 76 4096 76\nbuffer-2048 0 32 2048 16\nbuffer-512 0 32 512 4\nbuffer-128 3 32 128 1\nbuffer-32 0 128 32 1\n
|
||||
A: power/control=on\n
|
||||
A: power/runtime_active_time=775799103\n
|
||||
A: power/runtime_status=active\n
|
||||
A: power/runtime_suspended_time=0\n
|
||||
A: power/wakeup=enabled\n
|
||||
A: power/wakeup_abort_count=0\n
|
||||
A: power/wakeup_active=0\n
|
||||
A: power/wakeup_active_count=61\n
|
||||
A: power/wakeup_count=0\n
|
||||
A: power/wakeup_expire_count=61\n
|
||||
A: power/wakeup_last_time_ms=773160682\n
|
||||
A: power/wakeup_max_time_ms=108\n
|
||||
A: power/wakeup_total_time_ms=6358\n
|
||||
A: power_state=D0\n
|
||||
A: resource=0x00000000b1320000 0x00000000b132ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n
|
||||
A: revision=0x10\n
|
||||
A: subsystem_device=0x312a\n
|
||||
A: subsystem_vendor=0x17aa\n
|
||||
A: vendor=0x8086\n
|
||||
|
Loading…
Reference in a new issue