mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2026-05-11 08:28:08 +02:00
Initial commit of crfpmoc driver
This commit is contained in:
parent
d79f157282
commit
11c704d462
12 changed files with 1131 additions and 8 deletions
627
libfprint/drivers/crfpmoc/crfpmoc.c
Normal file
627
libfprint/drivers/crfpmoc/crfpmoc.c
Normal file
|
|
@ -0,0 +1,627 @@
|
|||
/*
|
||||
* ChromeOS Fingerprint driver for libfprint
|
||||
*
|
||||
* Copyright (C) 2024 Abhinav Baid <abhinavbaid@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "crfpmoc"
|
||||
|
||||
#include <sys/fcntl.h>
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "crfpmoc_storage.h"
|
||||
#include "crfpmoc.h"
|
||||
|
||||
struct _FpiDeviceCrfpMoc
|
||||
{
|
||||
FpDevice parent;
|
||||
FpiSsm *task_ssm;
|
||||
GCancellable *interrupt_cancellable;
|
||||
int fd;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FpiDeviceCrfpMoc, fpi_device_crfpmoc, FP_TYPE_DEVICE)
|
||||
|
||||
typedef struct crfpmoc_enroll_print
|
||||
{
|
||||
FpPrint *print;
|
||||
int stage;
|
||||
} EnrollPrint;
|
||||
|
||||
static const FpIdEntry crfpmoc_id_table[] = {
|
||||
{.udev_types = FPI_DEVICE_UDEV_SUBTYPE_MISC, .misc_name = "cros_fp"},
|
||||
{.udev_types = 0}
|
||||
};
|
||||
|
||||
static const gchar *const crfpmoc_meanings[] = {
|
||||
"SUCCESS",
|
||||
"INVALID_COMMAND",
|
||||
"ERROR",
|
||||
"INVALID_PARAM",
|
||||
"ACCESS_DENIED",
|
||||
"INVALID_RESPONSE",
|
||||
"INVALID_VERSION",
|
||||
"INVALID_CHECKSUM",
|
||||
"IN_PROGRESS",
|
||||
"UNAVAILABLE",
|
||||
"TIMEOUT",
|
||||
"OVERFLOW",
|
||||
"INVALID_HEADER",
|
||||
"REQUEST_TRUNCATED",
|
||||
"RESPONSE_TOO_BIG",
|
||||
"BUS_ERROR",
|
||||
"BUSY",
|
||||
"INVALID_HEADER_VERSION",
|
||||
"INVALID_HEADER_CRC",
|
||||
"INVALID_DATA_CRC",
|
||||
"DUP_UNAVAILABLE",
|
||||
};
|
||||
|
||||
static const gchar *crfpmoc_strresult (int i)
|
||||
{
|
||||
int crfpmoc_meanings_len = sizeof (crfpmoc_meanings) / sizeof (crfpmoc_meanings[0]);
|
||||
if (i < 0 || i >= crfpmoc_meanings_len)
|
||||
return "<unknown>";
|
||||
return crfpmoc_meanings[i];
|
||||
}
|
||||
|
||||
static int
|
||||
crfpmoc_ec_command (FpiDeviceCrfpMoc *self, int command, int version, const void *outdata, int outsize, void *indata, int insize, const gchar **error_msg)
|
||||
{
|
||||
struct crfpmoc_cros_ec_command_v2 *s_cmd;
|
||||
int r;
|
||||
|
||||
g_assert (outsize == 0 || outdata != NULL);
|
||||
g_assert (insize == 0 || indata != NULL);
|
||||
|
||||
s_cmd = g_malloc (sizeof (struct crfpmoc_cros_ec_command_v2) + MAX (outsize, insize));
|
||||
if (s_cmd == NULL)
|
||||
{
|
||||
if (error_msg != NULL)
|
||||
{
|
||||
*error_msg = crfpmoc_strresult (EC_RES_ERROR);
|
||||
}
|
||||
return -EC_RES_ERROR;
|
||||
}
|
||||
|
||||
s_cmd->command = command;
|
||||
s_cmd->version = version;
|
||||
s_cmd->result = 0xff;
|
||||
s_cmd->outsize = outsize;
|
||||
s_cmd->insize = insize;
|
||||
memcpy (s_cmd->data, outdata, outsize);
|
||||
|
||||
r = ioctl (self->fd, CRFPMOC_CROS_EC_DEV_IOCXCMD_V2, s_cmd);
|
||||
if (r < 0)
|
||||
{
|
||||
fp_warn ("ioctl %d, errno %d (%s), EC result %d (%s)", r, errno, strerror (errno), s_cmd->result, crfpmoc_strresult (s_cmd->result));
|
||||
if (errno == EAGAIN && s_cmd->result == EC_RES_IN_PROGRESS)
|
||||
{
|
||||
s_cmd->command = CRFPMOC_EC_CMD_RESEND_RESPONSE;
|
||||
r = ioctl (self->fd, CRFPMOC_CROS_EC_DEV_IOCXCMD_V2, s_cmd);
|
||||
if (r < 0)
|
||||
{
|
||||
fp_warn ("ioctl %d, errno %d (%s), EC result %d (%s)", r, errno, strerror (errno), s_cmd->result, crfpmoc_strresult (s_cmd->result));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (r >= 0)
|
||||
{
|
||||
memcpy (indata, s_cmd->data, MIN (r, insize));
|
||||
if (s_cmd->result != EC_RES_SUCCESS)
|
||||
{
|
||||
fp_warn ("EC result %d (%s)", s_cmd->result, crfpmoc_strresult (s_cmd->result));
|
||||
r = -CRFPMOC_EECRESULT - s_cmd->result;
|
||||
}
|
||||
}
|
||||
g_free (s_cmd);
|
||||
|
||||
if (error_msg != NULL)
|
||||
{
|
||||
*error_msg = crfpmoc_strresult (s_cmd->result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
crfpmoc_cmd_fp_mode (FpiDeviceCrfpMoc *self, guint32 inmode, guint32 *outmode, const gchar **error_msg)
|
||||
{
|
||||
struct crfpmoc_ec_params_fp_mode p;
|
||||
struct crfpmoc_ec_response_fp_mode r;
|
||||
int rv;
|
||||
|
||||
p.mode = inmode;
|
||||
rv = crfpmoc_ec_command (self, CRFPMOC_EC_CMD_FP_MODE, 0, &p, sizeof (p), &r, sizeof (r), error_msg);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
fp_dbg ("FP mode: (0x%x)", r.mode);
|
||||
if (outmode != NULL)
|
||||
{
|
||||
*outmode = r.mode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
crfpmoc_cmd_fp_info (FpiDeviceCrfpMoc *self, guint16 *enrolled_fingers, const gchar **error_msg)
|
||||
{
|
||||
struct crfpmoc_ec_response_fp_info r;
|
||||
int rv;
|
||||
|
||||
rv = crfpmoc_ec_command (self, CRFPMOC_EC_CMD_FP_INFO, 1, NULL, 0, &r, sizeof (r), error_msg);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
fp_dbg ("Fingerprint sensor: vendor %x product %x model %x version %x", r.vendor_id, r.product_id, r.model_id, r.version);
|
||||
fp_dbg ("Image: size %dx%d %d bpp", r.width, r.height, r.bpp);
|
||||
fp_dbg ("Templates: version %d size %d count %d/%d dirty bitmap %x", r.template_version, r.template_size, r.template_valid, r.template_max, r.template_dirty);
|
||||
|
||||
*enrolled_fingers = r.template_valid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
crfpmoc_cmd_fp_stats (FpiDeviceCrfpMoc *self, gint8 *finger, const gchar **error_msg)
|
||||
{
|
||||
struct crfpmoc_ec_response_fp_stats r;
|
||||
int rv;
|
||||
|
||||
rv = crfpmoc_ec_command (self, CRFPMOC_EC_CMD_FP_STATS, 0, NULL, 0, &r, sizeof (r), error_msg);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
if (r.timestamps_invalid & CRFPMOC_FPSTATS_MATCHING_INV)
|
||||
{
|
||||
fp_dbg ("Last matching time: Invalid");
|
||||
*finger = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fp_dbg ("Last matching time: %d us (finger: %d)", r.matching_time_us, r.template_matched);
|
||||
*finger = r.template_matched;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_task_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error)
|
||||
{
|
||||
fp_dbg ("Task SSM done");
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
|
||||
g_assert (!self->task_ssm || self->task_ssm == ssm);
|
||||
self->task_ssm = NULL;
|
||||
|
||||
if (error)
|
||||
fpi_device_action_error (device, error);
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_open (FpDevice *device)
|
||||
{
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
const char *file = fpi_device_get_udev_data (FP_DEVICE (device), FPI_DEVICE_UDEV_SUBTYPE_MISC);
|
||||
GError *err = NULL;
|
||||
|
||||
fp_dbg ("Opening device %s", file);
|
||||
|
||||
self->interrupt_cancellable = g_cancellable_new ();
|
||||
|
||||
int fd = open (file, O_RDWR);
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
g_set_error (&err, G_IO_ERROR, g_io_error_from_errno (errno), "unable to open misc device");
|
||||
fpi_device_open_complete (device, err);
|
||||
return;
|
||||
}
|
||||
|
||||
self->fd = fd;
|
||||
|
||||
fpi_device_open_complete (device, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_cancel (FpDevice *device)
|
||||
{
|
||||
fp_dbg ("Cancel");
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
|
||||
crfpmoc_cmd_fp_mode (self, 0, NULL, NULL);
|
||||
|
||||
g_cancellable_cancel (self->interrupt_cancellable);
|
||||
g_clear_object (&self->interrupt_cancellable);
|
||||
self->interrupt_cancellable = g_cancellable_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_suspend (FpDevice *device)
|
||||
{
|
||||
fp_dbg ("Suspend");
|
||||
|
||||
crfpmoc_cancel (device);
|
||||
g_cancellable_cancel (fpi_device_get_cancellable (device));
|
||||
fpi_device_suspend_complete (device, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_close (FpDevice *device)
|
||||
{
|
||||
fp_dbg ("Closing device");
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
|
||||
crfpmoc_cancel (device);
|
||||
g_clear_object (&self->interrupt_cancellable);
|
||||
|
||||
if (self->fd >= 0)
|
||||
{
|
||||
close (self->fd);
|
||||
self->fd = -1;
|
||||
}
|
||||
fpi_device_close_complete (device, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_enroll_run_state (FpiSsm *ssm, FpDevice *device)
|
||||
{
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
EnrollPrint *enroll_print = fpi_ssm_get_data (ssm);
|
||||
g_autofree gchar *user_id = NULL;
|
||||
int r;
|
||||
guint32 mode;
|
||||
guint16 enrolled_fingers = 0;
|
||||
const gchar *error_msg;
|
||||
|
||||
switch (fpi_ssm_get_cur_state (ssm))
|
||||
{
|
||||
case ENROLL_SENSOR_ENROLL:
|
||||
r = crfpmoc_cmd_fp_mode (self, CRFPMOC_FP_MODE_ENROLL_IMAGE | CRFPMOC_FP_MODE_ENROLL_SESSION, &mode, &error_msg);
|
||||
if (r < 0)
|
||||
{
|
||||
fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "%s", error_msg));
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_ssm_next_state (ssm);
|
||||
}
|
||||
break;
|
||||
|
||||
case ENROLL_WAIT_FINGER:
|
||||
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NEEDED);
|
||||
fpi_ssm_next_state_delayed (ssm, CRFPMOC_FINGER_SCAN_DELAY);
|
||||
break;
|
||||
|
||||
case ENROLL_SENSOR_CHECK:
|
||||
r = crfpmoc_cmd_fp_mode (self, CRFPMOC_FP_MODE_DONT_CHANGE, &mode, &error_msg);
|
||||
if (r < 0)
|
||||
{
|
||||
fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "%s", error_msg));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode & CRFPMOC_FP_MODE_ENROLL_SESSION)
|
||||
{
|
||||
if (mode & CRFPMOC_FP_MODE_ENROLL_IMAGE)
|
||||
{
|
||||
fpi_ssm_jump_to_state (ssm, ENROLL_WAIT_FINGER);
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
enroll_print->stage++;
|
||||
fp_info ("Partial capture successful (%d/%d).", enroll_print->stage, CRFPMOC_NR_ENROLL_STAGES);
|
||||
fpi_device_enroll_progress (device, enroll_print->stage, enroll_print->print, NULL);
|
||||
|
||||
fpi_ssm_jump_to_state (ssm, ENROLL_SENSOR_ENROLL);
|
||||
}
|
||||
}
|
||||
else if (mode == 0)
|
||||
{
|
||||
fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
fpi_ssm_next_state (ssm);
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
fpi_device_enroll_progress (device, enroll_print->stage, NULL, fpi_device_retry_new_msg (FP_DEVICE_RETRY_GENERAL, "FP mode: (0x%x)", mode));
|
||||
|
||||
fpi_ssm_jump_to_state (ssm, ENROLL_SENSOR_ENROLL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ENROLL_COMMIT:
|
||||
crfpmoc_cmd_fp_info (self, &enrolled_fingers, &error_msg);
|
||||
fp_dbg ("Number of enrolled fingers is: %d", enrolled_fingers);
|
||||
|
||||
user_id = fpi_print_generate_user_id (enroll_print->print);
|
||||
fp_dbg ("New fingerprint ID: %s", user_id);
|
||||
|
||||
fpi_print_set_type (enroll_print->print, FPI_PRINT_RAW);
|
||||
fpi_print_set_device_stored (enroll_print->print, TRUE);
|
||||
|
||||
g_object_set (enroll_print->print, "description", user_id, NULL);
|
||||
|
||||
print_data_save (enroll_print->print, enrolled_fingers - 1);
|
||||
|
||||
fp_info ("Enrollment was successful!");
|
||||
fpi_device_enroll_complete (device, g_object_ref (enroll_print->print), NULL);
|
||||
|
||||
fpi_ssm_mark_completed (ssm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_enroll (FpDevice *device)
|
||||
{
|
||||
fp_dbg ("Enroll");
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
EnrollPrint *enroll_print = g_new0 (EnrollPrint, 1);
|
||||
|
||||
fpi_device_get_enroll_data (device, &enroll_print->print);
|
||||
enroll_print->stage = 0;
|
||||
|
||||
g_assert (self->task_ssm == NULL);
|
||||
self->task_ssm = fpi_ssm_new (device, crfpmoc_enroll_run_state, ENROLL_STATES);
|
||||
fpi_ssm_set_data (self->task_ssm, g_steal_pointer (&enroll_print), g_free);
|
||||
fpi_ssm_start (self->task_ssm, crfpmoc_task_ssm_done);
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_verify_run_state (FpiSsm *ssm, FpDevice *device)
|
||||
{
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
FpPrint *print = NULL;
|
||||
FpPrint *verify_print = NULL;
|
||||
GPtrArray *prints;
|
||||
gboolean found = FALSE;
|
||||
guint index;
|
||||
int r;
|
||||
guint32 mode;
|
||||
gint8 finger = -1;
|
||||
const gchar *error_msg;
|
||||
|
||||
switch (fpi_ssm_get_cur_state (ssm))
|
||||
{
|
||||
case VERIFY_SENSOR_MATCH:
|
||||
r = crfpmoc_cmd_fp_mode (self, CRFPMOC_FP_MODE_MATCH, &mode, &error_msg);
|
||||
if (r < 0)
|
||||
{
|
||||
fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "%s", error_msg));
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_ssm_next_state (ssm);
|
||||
}
|
||||
break;
|
||||
|
||||
case VERIFY_WAIT_FINGER:
|
||||
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NEEDED);
|
||||
fpi_ssm_next_state_delayed (ssm, CRFPMOC_FINGER_SCAN_DELAY);
|
||||
break;
|
||||
|
||||
case VERIFY_SENSOR_CHECK:
|
||||
r = crfpmoc_cmd_fp_mode (self, CRFPMOC_FP_MODE_DONT_CHANGE, &mode, &error_msg);
|
||||
if (r < 0)
|
||||
{
|
||||
fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "%s", error_msg));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode & CRFPMOC_FP_MODE_MATCH)
|
||||
{
|
||||
fpi_ssm_jump_to_state (ssm, VERIFY_WAIT_FINGER);
|
||||
}
|
||||
else if (mode == 0)
|
||||
{
|
||||
fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
r = crfpmoc_cmd_fp_stats (self, &finger, &error_msg);
|
||||
if (r < 0)
|
||||
{
|
||||
fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "%s", error_msg));
|
||||
}
|
||||
else
|
||||
{
|
||||
int is_identify = fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY;
|
||||
if (finger == -1)
|
||||
{
|
||||
fp_info ("Print was not identified by the device");
|
||||
|
||||
if (is_identify)
|
||||
{
|
||||
fpi_device_identify_report (device, NULL, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print = print_data_load (device, finger);
|
||||
|
||||
fp_info ("Identify successful for: %s", fp_print_get_description (print));
|
||||
|
||||
if (is_identify)
|
||||
{
|
||||
fpi_device_get_identify_data (device, &prints);
|
||||
found = g_ptr_array_find_with_equal_func (prints,
|
||||
print,
|
||||
(GEqualFunc) fp_print_equal,
|
||||
&index);
|
||||
|
||||
if (found)
|
||||
fpi_device_identify_report (device, g_ptr_array_index (prints, index), print, NULL);
|
||||
else
|
||||
fpi_device_identify_report (device, NULL, print, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_device_get_verify_data (device, &verify_print);
|
||||
fp_info ("Verifying against: %s", fp_print_get_description (verify_print));
|
||||
|
||||
if (fp_print_equal (verify_print, print))
|
||||
fpi_device_verify_report (device, FPI_MATCH_SUCCESS, print, NULL);
|
||||
else
|
||||
fpi_device_verify_report (device, FPI_MATCH_FAIL, print, NULL);
|
||||
}
|
||||
}
|
||||
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
|
||||
{
|
||||
fpi_device_verify_complete (device, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_device_identify_complete (device, NULL);
|
||||
}
|
||||
fpi_ssm_mark_completed (ssm);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
|
||||
{
|
||||
fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, fpi_device_retry_new_msg (FP_DEVICE_RETRY_GENERAL, "FP mode: (0x%x)", mode));
|
||||
fpi_device_verify_complete (device, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_device_identify_report (device, NULL, NULL, fpi_device_retry_new_msg (FP_DEVICE_RETRY_GENERAL, "FP mode: (0x%x)", mode));
|
||||
fpi_device_identify_complete (device, NULL);
|
||||
}
|
||||
fpi_ssm_mark_completed (ssm);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_identify_verify (FpDevice *device)
|
||||
{
|
||||
fp_dbg ("Identify or Verify");
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
|
||||
g_assert (self->task_ssm == NULL);
|
||||
self->task_ssm = fpi_ssm_new (device, crfpmoc_verify_run_state, VERIFY_STATES);
|
||||
fpi_ssm_start (self->task_ssm, crfpmoc_task_ssm_done);
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_clear_storage_run_state (FpiSsm *ssm, FpDevice *device)
|
||||
{
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
int r;
|
||||
guint32 mode;
|
||||
const gchar *error_msg;
|
||||
|
||||
switch (fpi_ssm_get_cur_state (ssm))
|
||||
{
|
||||
case CLEAR_STORAGE_SENSOR_RESET:
|
||||
r = crfpmoc_cmd_fp_mode (self, CRFPMOC_FP_MODE_RESET_SENSOR, &mode, &error_msg);
|
||||
if (r < 0)
|
||||
{
|
||||
fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "%s", error_msg));
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_device_clear_storage_complete (device, NULL);
|
||||
fpi_ssm_mark_completed (ssm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_clear_storage (FpDevice *device)
|
||||
{
|
||||
fp_dbg ("Clear storage");
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
|
||||
g_assert (self->task_ssm == NULL);
|
||||
self->task_ssm = fpi_ssm_new (device, crfpmoc_clear_storage_run_state, CLEAR_STORAGE_STATES);
|
||||
fpi_ssm_start (self->task_ssm, crfpmoc_task_ssm_done);
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_list_run_state (FpiSsm *ssm, FpDevice *device)
|
||||
{
|
||||
g_autoptr(GPtrArray) enrolled_prints = NULL;
|
||||
|
||||
switch (fpi_ssm_get_cur_state (ssm))
|
||||
{
|
||||
case LIST_RETURN_ENROLLED_PRINTS:
|
||||
enrolled_prints = gallery_data_load (device);
|
||||
fpi_device_list_complete (device, g_steal_pointer (&enrolled_prints), NULL);
|
||||
fpi_ssm_mark_completed (ssm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
crfpmoc_list (FpDevice *device)
|
||||
{
|
||||
fp_dbg ("List");
|
||||
FpiDeviceCrfpMoc *self = FPI_DEVICE_CRFPMOC (device);
|
||||
|
||||
g_assert (self->task_ssm == NULL);
|
||||
self->task_ssm = fpi_ssm_new (device, crfpmoc_list_run_state, LIST_STATES);
|
||||
fpi_ssm_start (self->task_ssm, crfpmoc_task_ssm_done);
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_crfpmoc_init (FpiDeviceCrfpMoc *self)
|
||||
{
|
||||
G_DEBUG_HERE ();
|
||||
self->fd = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_crfpmoc_class_init (FpiDeviceCrfpMocClass *klass)
|
||||
{
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
||||
|
||||
dev_class->id = FP_COMPONENT;
|
||||
dev_class->full_name = CRFPMOC_DRIVER_FULLNAME;
|
||||
|
||||
dev_class->type = FP_DEVICE_TYPE_UDEV;
|
||||
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
|
||||
dev_class->id_table = crfpmoc_id_table;
|
||||
dev_class->nr_enroll_stages = CRFPMOC_NR_ENROLL_STAGES;
|
||||
dev_class->temp_hot_seconds = 0;
|
||||
|
||||
dev_class->open = crfpmoc_open;
|
||||
dev_class->cancel = crfpmoc_cancel;
|
||||
dev_class->suspend = crfpmoc_suspend;
|
||||
dev_class->close = crfpmoc_close;
|
||||
dev_class->enroll = crfpmoc_enroll;
|
||||
dev_class->identify = crfpmoc_identify_verify;
|
||||
dev_class->verify = crfpmoc_identify_verify;
|
||||
dev_class->clear_storage = crfpmoc_clear_storage;
|
||||
dev_class->list = crfpmoc_list;
|
||||
|
||||
fpi_device_class_auto_initialize_features (dev_class);
|
||||
}
|
||||
183
libfprint/drivers/crfpmoc/crfpmoc.h
Normal file
183
libfprint/drivers/crfpmoc/crfpmoc.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* ChromeOS Fingerprint driver for libfprint
|
||||
*
|
||||
* Copyright (C) 2024 Abhinav Baid <abhinavbaid@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <config.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifndef HAVE_UDEV
|
||||
#error "crfpmoc requires udev"
|
||||
#endif
|
||||
|
||||
#include "fpi-device.h"
|
||||
#include "fpi-ssm.h"
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FpiDeviceCrfpMoc, fpi_device_crfpmoc, FPI, DEVICE_CRFPMOC, FpDevice)
|
||||
|
||||
#define CRFPMOC_DRIVER_FULLNAME "ChromeOS Fingerprint Match-on-Chip"
|
||||
|
||||
#define CRFPMOC_NR_ENROLL_STAGES 5
|
||||
|
||||
#define CRFPMOC_FINGER_SCAN_DELAY 1000
|
||||
|
||||
/* crfpmoc_ec_command return value for non-success result from EC */
|
||||
#define CRFPMOC_EECRESULT 1000
|
||||
|
||||
/* Resend last response (not supported on LPC). */
|
||||
#define CRFPMOC_EC_CMD_RESEND_RESPONSE 0x00DB
|
||||
/* Configure the Fingerprint MCU behavior */
|
||||
#define CRFPMOC_EC_CMD_FP_MODE 0x0402
|
||||
#define CRFPMOC_EC_CMD_FP_INFO 0x0403
|
||||
#define CRFPMOC_EC_CMD_FP_STATS 0x0407
|
||||
|
||||
/* Finger enrollment session on-going */
|
||||
#define CRFPMOC_FP_MODE_ENROLL_SESSION (1U << 4)
|
||||
/* Enroll the current finger image */
|
||||
#define CRFPMOC_FP_MODE_ENROLL_IMAGE (1U << 5)
|
||||
/* Try to match the current finger image */
|
||||
#define CRFPMOC_FP_MODE_MATCH (1U << 6)
|
||||
/* Reset and re-initialize the sensor. */
|
||||
#define CRFPMOC_FP_MODE_RESET_SENSOR (1U << 7)
|
||||
/* special value: don't change anything just read back current mode */
|
||||
#define CRFPMOC_FP_MODE_DONT_CHANGE (1U << 31)
|
||||
|
||||
#define CRFPMOC_FPSTATS_MATCHING_INV (1U << 1)
|
||||
|
||||
struct crfpmoc_ec_params_fp_mode {
|
||||
guint32 mode; /* as defined by CRFPMOC_FP_MODE_ constants */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct crfpmoc_ec_response_fp_mode {
|
||||
guint32 mode; /* as defined by CRFPMOC_FP_MODE_ constants */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct crfpmoc_ec_response_fp_stats {
|
||||
guint32 capture_time_us;
|
||||
guint32 matching_time_us;
|
||||
guint32 overall_time_us;
|
||||
struct {
|
||||
guint32 lo;
|
||||
guint32 hi;
|
||||
} overall_t0;
|
||||
guint8 timestamps_invalid;
|
||||
gint8 template_matched;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct crfpmoc_ec_response_fp_info {
|
||||
/* Sensor identification */
|
||||
guint32 vendor_id;
|
||||
guint32 product_id;
|
||||
guint32 model_id;
|
||||
guint32 version;
|
||||
/* Image frame characteristics */
|
||||
guint32 frame_size;
|
||||
guint32 pixel_format;
|
||||
guint16 width;
|
||||
guint16 height;
|
||||
guint16 bpp;
|
||||
guint16 errors;
|
||||
/* Template/finger current information */
|
||||
guint32 template_size; /* max template size in bytes */
|
||||
guint16 template_max; /* maximum number of fingers/templates */
|
||||
guint16 template_valid; /* number of valid fingers/templates */
|
||||
guint32 template_dirty; /* bitmap of templates with MCU side changes */
|
||||
guint32 template_version; /* version of the template format */
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* @version: Command version number (often 0)
|
||||
* @command: Command to send (CRFPMOC_EC_CMD_...)
|
||||
* @outsize: Outgoing length in bytes
|
||||
* @insize: Max number of bytes to accept from EC
|
||||
* @result: EC's response to the command (separate from communication failure)
|
||||
* @data: Where to put the incoming data from EC and outgoing data to EC
|
||||
*/
|
||||
struct crfpmoc_cros_ec_command_v2 {
|
||||
guint32 version;
|
||||
guint32 command;
|
||||
guint32 outsize;
|
||||
guint32 insize;
|
||||
guint32 result;
|
||||
guint8 data[0];
|
||||
};
|
||||
|
||||
#define CRFPMOC_CROS_EC_DEV_IOC_V2 0xEC
|
||||
#define CRFPMOC_CROS_EC_DEV_IOCXCMD_V2 \
|
||||
_IOWR(CRFPMOC_CROS_EC_DEV_IOC_V2, 0, struct crfpmoc_cros_ec_command_v2)
|
||||
|
||||
/*
|
||||
* Host command response codes (16-bit).
|
||||
*/
|
||||
enum crfpmoc_ec_status {
|
||||
EC_RES_SUCCESS = 0,
|
||||
EC_RES_INVALID_COMMAND = 1,
|
||||
EC_RES_ERROR = 2,
|
||||
EC_RES_INVALID_PARAM = 3,
|
||||
EC_RES_ACCESS_DENIED = 4,
|
||||
EC_RES_INVALID_RESPONSE = 5,
|
||||
EC_RES_INVALID_VERSION = 6,
|
||||
EC_RES_INVALID_CHECKSUM = 7,
|
||||
EC_RES_IN_PROGRESS = 8, /* Accepted, command in progress */
|
||||
EC_RES_UNAVAILABLE = 9, /* No response available */
|
||||
EC_RES_TIMEOUT = 10, /* We got a timeout */
|
||||
EC_RES_OVERFLOW = 11, /* Table / data overflow */
|
||||
EC_RES_INVALID_HEADER = 12, /* Header contains invalid data */
|
||||
EC_RES_REQUEST_TRUNCATED = 13, /* Didn't get the entire request */
|
||||
EC_RES_RESPONSE_TOO_BIG = 14, /* Response was too big to handle */
|
||||
EC_RES_BUS_ERROR = 15, /* Communications bus error */
|
||||
EC_RES_BUSY = 16, /* Up but too busy. Should retry */
|
||||
EC_RES_INVALID_HEADER_VERSION = 17, /* Header version invalid */
|
||||
EC_RES_INVALID_HEADER_CRC = 18, /* Header CRC invalid */
|
||||
EC_RES_INVALID_DATA_CRC = 19, /* Data CRC invalid */
|
||||
EC_RES_DUP_UNAVAILABLE = 20, /* Can't resend response */
|
||||
|
||||
EC_RES_COUNT,
|
||||
|
||||
EC_RES_MAX = UINT16_MAX, /**< Force enum to be 16 bits */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* SSM task states and various status enums */
|
||||
|
||||
typedef enum {
|
||||
ENROLL_SENSOR_ENROLL,
|
||||
ENROLL_WAIT_FINGER,
|
||||
ENROLL_SENSOR_CHECK,
|
||||
ENROLL_COMMIT,
|
||||
ENROLL_STATES,
|
||||
} EnrollStates;
|
||||
|
||||
typedef enum {
|
||||
VERIFY_SENSOR_MATCH,
|
||||
VERIFY_WAIT_FINGER,
|
||||
VERIFY_SENSOR_CHECK,
|
||||
VERIFY_STATES,
|
||||
} VerifyStates;
|
||||
|
||||
typedef enum {
|
||||
CLEAR_STORAGE_SENSOR_RESET,
|
||||
CLEAR_STORAGE_STATES,
|
||||
} ClearStorageStates;
|
||||
|
||||
typedef enum {
|
||||
LIST_RETURN_ENROLLED_PRINTS,
|
||||
LIST_STATES,
|
||||
} ListStates;
|
||||
202
libfprint/drivers/crfpmoc/crfpmoc_storage.c
Normal file
202
libfprint/drivers/crfpmoc/crfpmoc_storage.c
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* ChromeOS Fingerprint driver storage utils for libfprint
|
||||
*
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
* Copyright (C) 2019-2020 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "crfpmoc_storage.h"
|
||||
|
||||
#define STORAGE_FILE "crfpmoc.variant"
|
||||
|
||||
static char *
|
||||
get_print_data_descriptor (FpPrint *print, FpDevice *dev, gint8 finger)
|
||||
{
|
||||
const char *driver;
|
||||
const char *dev_id;
|
||||
|
||||
if (print)
|
||||
{
|
||||
driver = fp_print_get_driver (print);
|
||||
dev_id = fp_print_get_device_id (print);
|
||||
}
|
||||
else
|
||||
{
|
||||
driver = fp_device_get_driver (dev);
|
||||
dev_id = fp_device_get_device_id (dev);
|
||||
}
|
||||
|
||||
return g_strdup_printf ("%s/%s/%d", driver, dev_id, finger);
|
||||
}
|
||||
|
||||
static GVariantDict *
|
||||
load_data (void)
|
||||
{
|
||||
GVariantDict *res;
|
||||
GVariant *var;
|
||||
gchar *contents = NULL;
|
||||
gsize length = 0;
|
||||
|
||||
if (!g_file_get_contents (STORAGE_FILE, &contents, &length, NULL))
|
||||
{
|
||||
g_warning ("Error loading storage, assuming it is empty");
|
||||
return g_variant_dict_new (NULL);
|
||||
}
|
||||
|
||||
var = g_variant_new_from_data (G_VARIANT_TYPE_VARDICT, contents, length, FALSE, g_free, contents);
|
||||
|
||||
res = g_variant_dict_new (var);
|
||||
g_variant_unref (var);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
save_data (GVariant *data)
|
||||
{
|
||||
const gchar *contents = NULL;
|
||||
gsize length;
|
||||
|
||||
length = g_variant_get_size (data);
|
||||
contents = (gchar *) g_variant_get_data (data);
|
||||
|
||||
if (!g_file_set_contents (STORAGE_FILE, contents, length, NULL))
|
||||
{
|
||||
g_warning ("Error saving storage!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_variant_ref_sink (data);
|
||||
g_variant_unref (data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FpPrint *
|
||||
load_print_from_data (GVariant *data)
|
||||
{
|
||||
const guchar *stored_data = NULL;
|
||||
gsize stored_len;
|
||||
FpPrint *print;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
stored_data = (const guchar *) g_variant_get_fixed_array (data, &stored_len, 1);
|
||||
print = fp_print_deserialize (stored_data, stored_len, &error);
|
||||
if (error)
|
||||
g_warning ("Error deserializing data: %s", error->message);
|
||||
return print;
|
||||
}
|
||||
|
||||
int
|
||||
print_data_save (FpPrint *print, gint8 finger)
|
||||
{
|
||||
g_debug ("Saving finger: %d", finger);
|
||||
GVariant *print_id_var = NULL;
|
||||
GVariant *fpi_data = NULL;
|
||||
|
||||
g_autofree gchar *descr = get_print_data_descriptor (print, NULL, finger);
|
||||
print_id_var = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
|
||||
descr,
|
||||
strlen (descr),
|
||||
sizeof (guchar));
|
||||
fpi_data = g_variant_new ("(@ay)", print_id_var);
|
||||
g_object_set (print, "fpi-data", fpi_data, NULL);
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(GVariantDict) dict = NULL;
|
||||
g_autofree guchar *data = NULL;
|
||||
GVariant *val;
|
||||
gsize size;
|
||||
int res;
|
||||
|
||||
dict = load_data ();
|
||||
|
||||
fp_print_serialize (print, &data, &size, &error);
|
||||
if (error)
|
||||
{
|
||||
g_warning ("Error serializing data: %s", error->message);
|
||||
return -1;
|
||||
}
|
||||
val = g_variant_new_fixed_array (G_VARIANT_TYPE ("y"), data, size, 1);
|
||||
g_variant_dict_insert_value (dict, descr, val);
|
||||
|
||||
res = save_data (g_variant_dict_end (dict));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
FpPrint *
|
||||
print_data_load (FpDevice *dev, gint8 finger)
|
||||
{
|
||||
g_autofree gchar *descr = get_print_data_descriptor (NULL, dev, finger);
|
||||
|
||||
g_autoptr(GVariant) val = NULL;
|
||||
g_autoptr(GVariantDict) dict = NULL;
|
||||
|
||||
dict = load_data ();
|
||||
val = g_variant_dict_lookup_value (dict, descr, G_VARIANT_TYPE ("ay"));
|
||||
|
||||
if (val)
|
||||
return load_print_from_data (val);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
gallery_data_load (FpDevice *dev)
|
||||
{
|
||||
g_autoptr(GVariantDict) dict = NULL;
|
||||
g_autoptr(GVariant) dict_variant = NULL;
|
||||
g_autofree char *dev_prefix = NULL;
|
||||
GPtrArray *gallery;
|
||||
const char *driver;
|
||||
const char *dev_id;
|
||||
GVariantIter iter;
|
||||
GVariant *value;
|
||||
gchar *key;
|
||||
|
||||
gallery = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
dict = load_data ();
|
||||
dict_variant = g_variant_dict_end (dict);
|
||||
driver = fp_device_get_driver (dev);
|
||||
dev_id = fp_device_get_device_id (dev);
|
||||
dev_prefix = g_strdup_printf ("%s/%s/", driver, dev_id);
|
||||
|
||||
g_variant_iter_init (&iter, dict_variant);
|
||||
while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
|
||||
{
|
||||
FpPrint *print;
|
||||
const guchar *stored_data;
|
||||
g_autoptr(GError) error = NULL;
|
||||
gsize stored_len;
|
||||
|
||||
if (!g_str_has_prefix (key, dev_prefix))
|
||||
continue;
|
||||
|
||||
stored_data = (const guchar *) g_variant_get_fixed_array (value, &stored_len, 1);
|
||||
print = fp_print_deserialize (stored_data, stored_len, &error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_warning ("Error deserializing data: %s", error->message);
|
||||
continue;
|
||||
}
|
||||
|
||||
g_ptr_array_add (gallery, print);
|
||||
}
|
||||
|
||||
return gallery;
|
||||
}
|
||||
26
libfprint/drivers/crfpmoc/crfpmoc_storage.h
Normal file
26
libfprint/drivers/crfpmoc/crfpmoc_storage.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* ChromeOS Fingerprint driver storage utils for libfprint
|
||||
*
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
* Copyright (C) 2019-2020 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <fprint.h>
|
||||
|
||||
int print_data_save (FpPrint *print, gint8 finger);
|
||||
FpPrint *print_data_load (FpDevice *dev, gint8 finger);
|
||||
GPtrArray *gallery_data_load (FpDevice *dev);
|
||||
|
|
@ -479,6 +479,7 @@ fp_context_enumerate (FpContext *context)
|
|||
|
||||
g_autoptr(GList) spidev_devices = g_udev_client_query_by_subsystem (udev_client, "spidev");
|
||||
g_autoptr(GList) hidraw_devices = g_udev_client_query_by_subsystem (udev_client, "hidraw");
|
||||
g_autoptr(GList) misc_devices = g_udev_client_query_by_subsystem (udev_client, "misc");
|
||||
|
||||
/* for each potential driver, try to match all requested resources. */
|
||||
for (i = 0; i < priv->drivers->len; i++)
|
||||
|
|
@ -492,7 +493,7 @@ fp_context_enumerate (FpContext *context)
|
|||
|
||||
for (entry = cls->id_table; entry->udev_types; entry++)
|
||||
{
|
||||
GList *matched_spidev = NULL, *matched_hidraw = NULL;
|
||||
GList *matched_spidev = NULL, *matched_hidraw = NULL, *matched_misc = NULL;
|
||||
|
||||
if (entry->udev_types & FPI_DEVICE_UDEV_SUBTYPE_SPIDEV)
|
||||
{
|
||||
|
|
@ -530,6 +531,20 @@ fp_context_enumerate (FpContext *context)
|
|||
if (matched_hidraw == NULL)
|
||||
continue;
|
||||
}
|
||||
if (entry->udev_types & FPI_DEVICE_UDEV_SUBTYPE_MISC)
|
||||
{
|
||||
for (matched_misc = misc_devices; matched_misc; matched_misc = matched_misc->next)
|
||||
{
|
||||
const gchar * sysfs = g_udev_device_get_sysfs_path (matched_misc->data);
|
||||
if (!sysfs)
|
||||
continue;
|
||||
if (strstr (sysfs, entry->misc_name))
|
||||
break;
|
||||
}
|
||||
/* If match was not found exit */
|
||||
if (matched_misc == NULL)
|
||||
continue;
|
||||
}
|
||||
priv->pending_devices++;
|
||||
g_async_initable_new_async (driver,
|
||||
G_PRIORITY_LOW,
|
||||
|
|
@ -539,6 +554,7 @@ fp_context_enumerate (FpContext *context)
|
|||
"fpi-driver-data", entry->driver_data,
|
||||
"fpi-udev-data-spidev", (matched_spidev ? g_udev_device_get_device_file (matched_spidev->data) : NULL),
|
||||
"fpi-udev-data-hidraw", (matched_hidraw ? g_udev_device_get_device_file (matched_hidraw->data) : NULL),
|
||||
"fpi-udev-data-misc", (matched_misc ? g_udev_device_get_device_file (matched_misc->data) : NULL),
|
||||
NULL);
|
||||
/* remove entries from list to avoid conflicts */
|
||||
if (matched_spidev)
|
||||
|
|
@ -551,12 +567,18 @@ fp_context_enumerate (FpContext *context)
|
|||
g_object_unref (matched_hidraw->data);
|
||||
hidraw_devices = g_list_delete_link (hidraw_devices, matched_hidraw);
|
||||
}
|
||||
if (matched_misc)
|
||||
{
|
||||
g_object_unref (matched_misc->data);
|
||||
misc_devices = g_list_delete_link (misc_devices, matched_misc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* free all unused elemnts in both lists */
|
||||
g_list_foreach (spidev_devices, (GFunc) g_object_unref, NULL);
|
||||
g_list_foreach (hidraw_devices, (GFunc) g_object_unref, NULL);
|
||||
g_list_foreach (misc_devices, (GFunc) g_object_unref, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ typedef struct
|
|||
{
|
||||
gchar *spidev_path;
|
||||
gchar *hidraw_path;
|
||||
gchar *misc_path;
|
||||
} udev_data;
|
||||
|
||||
gboolean is_removed;
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ enum {
|
|||
PROP_FPI_USB_DEVICE,
|
||||
PROP_FPI_UDEV_DATA_SPIDEV,
|
||||
PROP_FPI_UDEV_DATA_HIDRAW,
|
||||
PROP_FPI_UDEV_DATA_MISC,
|
||||
PROP_FPI_DRIVER_DATA,
|
||||
N_PROPS
|
||||
};
|
||||
|
|
@ -304,6 +305,13 @@ fp_device_get_property (GObject *object,
|
|||
g_value_set_string (value, NULL);
|
||||
break;
|
||||
|
||||
case PROP_FPI_UDEV_DATA_MISC:
|
||||
if (cls->type == FP_DEVICE_TYPE_UDEV)
|
||||
g_value_set_string (value, g_strdup (priv->udev_data.misc_path));
|
||||
else
|
||||
g_value_set_string (value, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
|
|
@ -350,6 +358,13 @@ fp_device_set_property (GObject *object,
|
|||
g_assert (g_value_get_string (value) == NULL);
|
||||
break;
|
||||
|
||||
case PROP_FPI_UDEV_DATA_MISC:
|
||||
if (cls->type == FP_DEVICE_TYPE_UDEV)
|
||||
priv->udev_data.misc_path = g_value_dup_string (value);
|
||||
else
|
||||
g_assert (g_value_get_string (value) == NULL);
|
||||
break;
|
||||
|
||||
case PROP_FPI_DRIVER_DATA:
|
||||
priv->driver_data = g_value_get_uint64 (value);
|
||||
break;
|
||||
|
|
@ -580,6 +595,19 @@ fp_device_class_init (FpDeviceClass *klass)
|
|||
"Private: The path to /dev/hidrawN",
|
||||
NULL,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
/**
|
||||
* FpDevice::fpi-udev-data-misc: (skip)
|
||||
*
|
||||
* This property is only for internal purposes.
|
||||
*
|
||||
* Stability: private
|
||||
*/
|
||||
properties[PROP_FPI_UDEV_DATA_MISC] =
|
||||
g_param_spec_string ("fpi-udev-data-misc",
|
||||
"Udev data: misc path",
|
||||
"Private: The path to /dev/{misc_name}",
|
||||
NULL,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
/**
|
||||
* FpDevice::fpi-driver-data: (skip)
|
||||
|
|
|
|||
|
|
@ -439,6 +439,9 @@ fpi_device_get_udev_data (FpDevice *device, FpiDeviceUdevSubtypeFlags subtype)
|
|||
case FPI_DEVICE_UDEV_SUBTYPE_SPIDEV:
|
||||
return priv->udev_data.spidev_path;
|
||||
|
||||
case FPI_DEVICE_UDEV_SUBTYPE_MISC:
|
||||
return priv->udev_data.misc_path;
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -28,12 +28,14 @@
|
|||
* FpiDeviceUdevSubtypeFlags:
|
||||
* @FPI_DEVICE_UDEV_SUBTYPE_SPIDEV: The device requires an spidev node
|
||||
* @FPI_DEVICE_UDEV_SUBTYPE_HIDRAW: The device requires a hidraw node
|
||||
* @FPI_DEVICE_UDEV_SUBTYPE_MISC: The device requires a misc node
|
||||
*
|
||||
* Bitfield of required hardware resources for a udev-backed device.
|
||||
*/
|
||||
typedef enum {
|
||||
FPI_DEVICE_UDEV_SUBTYPE_SPIDEV = 1 << 0,
|
||||
FPI_DEVICE_UDEV_SUBTYPE_HIDRAW = 1 << 1,
|
||||
FPI_DEVICE_UDEV_SUBTYPE_MISC = 1 << 2,
|
||||
} FpiDeviceUdevSubtypeFlags;
|
||||
|
||||
/**
|
||||
|
|
@ -64,6 +66,7 @@ struct _FpIdEntry
|
|||
guint pid;
|
||||
guint vid;
|
||||
} hid_id;
|
||||
const gchar *misc_name;
|
||||
};
|
||||
};
|
||||
guint64 driver_data;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
GHashTable *printed = NULL;
|
||||
|
||||
static void
|
||||
insert_drivers (GList **usb_list, GList **spi_list)
|
||||
insert_drivers (GList **usb_list, GList **spi_list, GList **misc_list)
|
||||
{
|
||||
g_autoptr(GArray) drivers = fpi_get_driver_types ();
|
||||
gint i;
|
||||
|
|
@ -68,12 +68,20 @@ insert_drivers (GList **usb_list, GList **spi_list)
|
|||
for (entry = cls->id_table; entry->udev_types; entry++)
|
||||
{
|
||||
char *key;
|
||||
int is_misc_device = entry->udev_types & FPI_DEVICE_UDEV_SUBTYPE_MISC;
|
||||
|
||||
/* Need SPI device */
|
||||
if ((entry->udev_types & FPI_DEVICE_UDEV_SUBTYPE_SPIDEV) == 0)
|
||||
continue;
|
||||
if (is_misc_device)
|
||||
{
|
||||
key = g_strdup_printf ("misc:%s", entry->misc_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Need SPI device */
|
||||
if ((entry->udev_types & FPI_DEVICE_UDEV_SUBTYPE_SPIDEV) == 0)
|
||||
continue;
|
||||
|
||||
key = g_strdup_printf ("SPI:%s:%04x:%04x", entry->spi_acpi_id, entry->hid_id.vid, entry->hid_id.pid);
|
||||
key = g_strdup_printf ("SPI:%s:%04x:%04x", entry->spi_acpi_id, entry->hid_id.vid, entry->hid_id.pid);
|
||||
}
|
||||
|
||||
if (g_hash_table_lookup (printed, key) != NULL)
|
||||
{
|
||||
|
|
@ -83,7 +91,10 @@ insert_drivers (GList **usb_list, GList **spi_list)
|
|||
|
||||
g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
|
||||
|
||||
if (entry->udev_types & FPI_DEVICE_UDEV_SUBTYPE_HIDRAW)
|
||||
if (is_misc_device)
|
||||
*misc_list = g_list_prepend (*misc_list,
|
||||
g_strdup_printf ("%s | %s\n", entry->misc_name, cls->full_name));
|
||||
else if (entry->udev_types & FPI_DEVICE_UDEV_SUBTYPE_HIDRAW)
|
||||
*spi_list = g_list_prepend (*spi_list,
|
||||
g_strdup_printf ("%s | %04x:%04x | %s\n", entry->spi_acpi_id, entry->hid_id.vid, entry->hid_id.pid, cls->full_name));
|
||||
else
|
||||
|
|
@ -104,6 +115,7 @@ main (int argc, char **argv)
|
|||
{
|
||||
GList *usb_list = NULL;
|
||||
GList *spi_list = NULL;
|
||||
GList *misc_list = NULL;
|
||||
GList *l;
|
||||
|
||||
setlocale (LC_ALL, "");
|
||||
|
|
@ -120,7 +132,7 @@ main (int argc, char **argv)
|
|||
g_print ("This is a list of supported devices in libfprint's development version. Those drivers might not all be available in the stable, released version. If in doubt, contact your distribution or systems integrator for details.\n");
|
||||
g_print ("\n");
|
||||
|
||||
insert_drivers (&usb_list, &spi_list);
|
||||
insert_drivers (&usb_list, &spi_list, &misc_list);
|
||||
|
||||
g_print ("## USB devices\n");
|
||||
g_print ("\n");
|
||||
|
|
@ -148,6 +160,18 @@ main (int argc, char **argv)
|
|||
|
||||
g_list_free_full (g_steal_pointer (&spi_list), g_free);
|
||||
|
||||
g_print ("## Misc devices\n");
|
||||
g_print ("\n");
|
||||
g_print ("Name | Driver\n");
|
||||
g_print ("------------ | ------------\n");
|
||||
|
||||
misc_list = g_list_sort (misc_list, (GCompareFunc) g_strcmp0);
|
||||
for (l = misc_list; l != NULL; l = l->next)
|
||||
g_print ("%s", (char *) l->data);
|
||||
g_print ("\n");
|
||||
|
||||
g_list_free_full (g_steal_pointer (&misc_list), g_free);
|
||||
|
||||
|
||||
g_hash_table_destroy (printed);
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,8 @@ driver_sources = {
|
|||
[ 'drivers/realtek/realtek.c' ],
|
||||
'focaltech_moc' :
|
||||
[ 'drivers/focaltech_moc/focaltech_moc.c' ],
|
||||
'crfpmoc' :
|
||||
[ 'drivers/crfpmoc/crfpmoc.c', 'drivers/crfpmoc/crfpmoc_storage.c' ],
|
||||
}
|
||||
|
||||
helper_sources = {
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ default_drivers = [
|
|||
'fpcmoc',
|
||||
'realtek',
|
||||
'focaltech_moc',
|
||||
'crfpmoc',
|
||||
]
|
||||
|
||||
spi_drivers = [
|
||||
|
|
@ -168,6 +169,7 @@ endian_independent_drivers = virtual_drivers + [
|
|||
'elanmoc',
|
||||
'etes603',
|
||||
'focaltech_moc',
|
||||
'crfpmoc',
|
||||
'nb1010',
|
||||
'realtek',
|
||||
'synaptics',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue