Compare commits

..

1 commit

Author SHA1 Message Date
50cc336455 TEMP elanmoc2 2021-09-21 01:08:50 +02:00
23 changed files with 1083 additions and 698 deletions

View file

@ -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

View file

@ -26,4 +26,3 @@
uncrustify
valgrind
clang-analyzer
diffutils

View file

@ -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

View file

@ -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);

View 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);
}

View 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}
};

View file

@ -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, },

View file

@ -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 */
};

View file

@ -360,7 +360,7 @@ 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,
fpi_usb_transfer_fill_bulk (transfer,
EP_INTR,
IRQ_LENGTH);
fpi_usb_transfer_submit (transfer, 0, self->irq_cancellable, irq_handler, NULL);

View file

@ -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)

View file

@ -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");
}
}
static int

View file

@ -125,6 +125,8 @@ driver_sources = {
[ 'drivers/elan.c' ],
'elanmoc' :
[ 'drivers/elanmoc/elanmoc.c' ],
'elanmoc2' :
[ 'drivers/elanmoc2/elanmoc2.c' ],
'elanspi' :
[ 'drivers/elanspi.c' ],
'nb1010' :

View file

@ -118,6 +118,7 @@ default_drivers = [
'synaptics',
'elan',
'elanmoc',
'elanmoc2',
'uru4000',
'upektc',
'upeksonly',

View file

@ -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.

View file

@ -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")

View file

@ -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 = [

View file

@ -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

View file

@ -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

View file

@ -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