8d545a0b95
Allows drivers to define more fine grained features for devices, not strictly depending on assumptions we can make depending on the implemented vfuncs. We keep this per class but could be in theory moved to each instance. In any case, added an utility function to initialize it in the way we can ensure that we've a consistent way for setting them across all the devices.
1411 lines
43 KiB
C
1411 lines
43 KiB
C
/*
|
|
* Copyright (C) 2019 Synaptics Inc
|
|
*
|
|
* 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 "synaptics"
|
|
|
|
#include "drivers_api.h"
|
|
|
|
#include "fpi-byte-reader.h"
|
|
|
|
#include "synaptics.h"
|
|
#include "bmkt_message.h"
|
|
|
|
G_DEFINE_TYPE (FpiDeviceSynaptics, fpi_device_synaptics, FP_TYPE_DEVICE)
|
|
|
|
static void init_identify_msg (FpDevice *device);
|
|
static void compose_and_send_identify_msg (FpDevice *device);
|
|
|
|
static const FpIdEntry id_table[] = {
|
|
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00BD, },
|
|
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00E9, },
|
|
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00DF, },
|
|
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00F9, },
|
|
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00FC, },
|
|
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00C2, },
|
|
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00C9, },
|
|
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0100, },
|
|
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
|
|
};
|
|
|
|
|
|
static void
|
|
cmd_receive_cb (FpiUsbTransfer *transfer,
|
|
FpDevice *device,
|
|
gpointer user_data,
|
|
GError *error)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
SynCmdMsgCallback callback = user_data;
|
|
int res;
|
|
bmkt_msg_resp_t msg_resp;
|
|
bmkt_response_t resp;
|
|
|
|
if (error)
|
|
{
|
|
/* NOTE: assumes timeout should never happen for receiving. */
|
|
fpi_ssm_mark_failed (transfer->ssm, error);
|
|
return;
|
|
}
|
|
|
|
res = bmkt_parse_message_header (&transfer->buffer[SENSOR_FW_REPLY_HEADER_LEN],
|
|
transfer->actual_length - SENSOR_FW_REPLY_HEADER_LEN,
|
|
&msg_resp);
|
|
if (res != BMKT_SUCCESS)
|
|
{
|
|
g_warning ("Corrupted message received");
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
|
|
return;
|
|
}
|
|
|
|
/* Special case events */
|
|
if (msg_resp.msg_id == BMKT_EVT_FINGER_REPORT)
|
|
{
|
|
if (msg_resp.payload_len != 1)
|
|
{
|
|
g_warning ("Corrupted finger report received");
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
|
|
return;
|
|
}
|
|
|
|
if (msg_resp.payload[0] == 0x01)
|
|
{
|
|
self->finger_on_sensor = TRUE;
|
|
fpi_device_report_finger_status_changes (device,
|
|
FP_FINGER_STATUS_PRESENT,
|
|
FP_FINGER_STATUS_NONE);
|
|
}
|
|
else
|
|
{
|
|
self->finger_on_sensor = FALSE;
|
|
fpi_device_report_finger_status_changes (device,
|
|
FP_FINGER_STATUS_NONE,
|
|
FP_FINGER_STATUS_PRESENT);
|
|
|
|
if (self->cmd_complete_on_removal)
|
|
{
|
|
fpi_ssm_mark_completed (transfer->ssm);
|
|
return;
|
|
}
|
|
}
|
|
|
|
fp_dbg ("Finger is now %s the sensor", self->finger_on_sensor ? "on" : "off");
|
|
|
|
/* XXX: Call callback!?! */
|
|
}
|
|
|
|
res = bmkt_parse_message_payload (&msg_resp, &resp);
|
|
if (res != BMKT_SUCCESS)
|
|
{
|
|
g_warning ("Could not parse message payload: %i", res);
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
|
|
return;
|
|
}
|
|
|
|
/* Special cancellation handling */
|
|
if (resp.response_id == BMKT_RSP_CANCEL_OP_OK || resp.response_id == BMKT_RSP_CANCEL_OP_FAIL)
|
|
{
|
|
if (resp.response_id == BMKT_RSP_CANCEL_OP_OK)
|
|
{
|
|
fp_dbg ("Received cancellation success response");
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
g_error_new_literal (G_IO_ERROR,
|
|
G_IO_ERROR_CANCELLED,
|
|
"Device reported cancellation of operation"));
|
|
}
|
|
else
|
|
{
|
|
fp_dbg ("Cancellation failed, this should not happen");
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (msg_resp.seq_num == 0)
|
|
{
|
|
/* XXX: Should we really abort the command on general error?
|
|
* The original code did not! */
|
|
if (msg_resp.msg_id == BMKT_RSP_GENERAL_ERROR)
|
|
{
|
|
guint16 err;
|
|
|
|
/* XXX: It is weird that this is big endian. */
|
|
err = FP_READ_UINT16_BE (msg_resp.payload);
|
|
|
|
fp_warn ("Received General Error %d from the sensor", (guint) err);
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Received general error %u from device",
|
|
(guint) err));
|
|
//fpi_ssm_jump_to_state (transfer->ssm, fpi_ssm_get_cur_state (transfer->ssm));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
fp_dbg ("Received message with 0 sequence number 0x%02x, ignoring!",
|
|
msg_resp.msg_id);
|
|
fpi_ssm_next_state (transfer->ssm);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* We should only ever have one command running, and the sequence num needs
|
|
* to match. */
|
|
if (msg_resp.seq_num != self->cmd_seq_num)
|
|
{
|
|
fp_warn ("Got unexpected sequence number from device, %d instead of %d",
|
|
msg_resp.seq_num,
|
|
self->cmd_seq_num);
|
|
}
|
|
|
|
if (callback)
|
|
callback (self, &resp, NULL);
|
|
|
|
/* Callback may have queued a follow up command, then we need
|
|
* to restart the SSM. If not, we'll finish/wait for interrupt
|
|
* depending on resp.complete. */
|
|
if (self->cmd_pending_transfer)
|
|
fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_SEND_PENDING);
|
|
else if (!resp.complete || self->cmd_complete_on_removal)
|
|
fpi_ssm_next_state (transfer->ssm); /* SYNAPTICS_CMD_WAIT_INTERRUPT */
|
|
else
|
|
fpi_ssm_mark_completed (transfer->ssm);
|
|
}
|
|
|
|
static void
|
|
cmd_interrupt_cb (FpiUsbTransfer *transfer,
|
|
FpDevice *device,
|
|
gpointer user_data,
|
|
GError *error)
|
|
{
|
|
g_debug ("interrupt transfer done");
|
|
if (error)
|
|
{
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
{
|
|
g_error_free (error);
|
|
fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_GET_RESP);
|
|
return;
|
|
}
|
|
|
|
fpi_ssm_mark_failed (transfer->ssm, error);
|
|
return;
|
|
}
|
|
|
|
if (transfer->buffer[0] & USB_ASYNC_MESSAGE_PENDING)
|
|
{
|
|
fpi_ssm_next_state (transfer->ssm);
|
|
}
|
|
else
|
|
{
|
|
fpi_usb_transfer_submit (fpi_usb_transfer_ref (transfer),
|
|
0,
|
|
NULL,
|
|
cmd_interrupt_cb,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
synaptics_cmd_run_state (FpiSsm *ssm,
|
|
FpDevice *dev)
|
|
{
|
|
FpiUsbTransfer *transfer;
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (dev);
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case SYNAPTICS_CMD_SEND_PENDING:
|
|
if (self->cmd_pending_transfer)
|
|
{
|
|
self->cmd_pending_transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (self->cmd_pending_transfer,
|
|
1000,
|
|
NULL,
|
|
fpi_ssm_usb_transfer_cb,
|
|
NULL);
|
|
self->cmd_pending_transfer = NULL;
|
|
}
|
|
else
|
|
{
|
|
fpi_ssm_next_state (ssm);
|
|
}
|
|
break;
|
|
|
|
case SYNAPTICS_CMD_GET_RESP:
|
|
transfer = fpi_usb_transfer_new (dev);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_fill_bulk (transfer, USB_EP_REPLY, MAX_TRANSFER_LEN);
|
|
fpi_usb_transfer_submit (transfer,
|
|
5000,
|
|
NULL,
|
|
cmd_receive_cb,
|
|
fpi_ssm_get_data (ssm));
|
|
|
|
break;
|
|
|
|
case SYNAPTICS_CMD_WAIT_INTERRUPT:
|
|
transfer = fpi_usb_transfer_new (dev);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_fill_interrupt (transfer, USB_EP_INTERRUPT, USB_INTERRUPT_DATA_SIZE);
|
|
fpi_usb_transfer_submit (transfer,
|
|
0,
|
|
self->interrupt_cancellable,
|
|
cmd_interrupt_cb,
|
|
NULL);
|
|
|
|
break;
|
|
|
|
case SYNAPTICS_CMD_SEND_ASYNC:
|
|
transfer = fpi_usb_transfer_new (dev);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_fill_bulk (transfer, USB_EP_REQUEST, SENSOR_FW_CMD_HEADER_LEN);
|
|
transfer->buffer[0] = SENSOR_CMD_ASYNCMSG_READ;
|
|
fpi_usb_transfer_submit (transfer,
|
|
1000,
|
|
NULL,
|
|
fpi_ssm_usb_transfer_cb,
|
|
NULL);
|
|
|
|
break;
|
|
|
|
case SYNAPTICS_CMD_RESTART:
|
|
fpi_ssm_jump_to_state (ssm, SYNAPTICS_CMD_SEND_PENDING);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
cmd_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (dev);
|
|
SynCmdMsgCallback callback = fpi_ssm_get_data (ssm);
|
|
|
|
self->cmd_ssm = NULL;
|
|
|
|
/* Notify about the SSM failure from here instead. */
|
|
if (error || self->cmd_complete_on_removal)
|
|
callback (self, NULL, error);
|
|
|
|
self->cmd_complete_on_removal = FALSE;
|
|
}
|
|
|
|
static void
|
|
cmd_forget_cb (FpiUsbTransfer *transfer,
|
|
FpDevice *device,
|
|
gpointer user_data,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
g_warning ("Async command sending failed: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
else
|
|
{
|
|
g_debug ("Async command sent successfully");
|
|
}
|
|
}
|
|
|
|
static void
|
|
synaptics_sensor_cmd (FpiDeviceSynaptics *self,
|
|
gint seq_num,
|
|
guint8 msg_id,
|
|
const guint8 * payload,
|
|
gssize payload_len,
|
|
SynCmdMsgCallback callback)
|
|
{
|
|
FpiUsbTransfer *transfer;
|
|
guint8 real_seq_num;
|
|
gint msg_len;
|
|
gint res;
|
|
|
|
/* callback may be NULL in two cases:
|
|
* - seq_num == -1
|
|
* - a state machine is already running, continued command */
|
|
g_assert (payload || payload_len == 0);
|
|
|
|
/* seq_num of 0 means a normal command, -1 means the current commands
|
|
* sequence number should not be updated (i.e. second async command which
|
|
* may only be a cancellation currently). */
|
|
if (seq_num <= 0)
|
|
{
|
|
self->last_seq_num = MAX (1, (self->last_seq_num + 1) & 0xff);
|
|
real_seq_num = self->last_seq_num;
|
|
if (seq_num == 0)
|
|
self->cmd_seq_num = self->last_seq_num;
|
|
}
|
|
else
|
|
{
|
|
real_seq_num = seq_num;
|
|
self->last_seq_num = real_seq_num;
|
|
}
|
|
g_debug ("sequence number is %d", real_seq_num);
|
|
|
|
/* We calculate the exact length here (we could also just create a larger
|
|
* buffer instead and check the result of bmkt_compose_message. */
|
|
msg_len = BMKT_MESSAGE_HEADER_LEN + payload_len;
|
|
|
|
/* Send out the command */
|
|
transfer = fpi_usb_transfer_new (FP_DEVICE (self));
|
|
transfer->short_is_error = TRUE;
|
|
fpi_usb_transfer_fill_bulk (transfer,
|
|
USB_EP_REQUEST,
|
|
msg_len + SENSOR_FW_CMD_HEADER_LEN);
|
|
|
|
/* MIS sensors send ACE commands encapsulated in FW commands*/
|
|
transfer->buffer[0] = SENSOR_CMD_ACE_COMMAND;
|
|
res = bmkt_compose_message (&transfer->buffer[1],
|
|
&msg_len, msg_id,
|
|
real_seq_num,
|
|
payload_len,
|
|
payload);
|
|
g_assert (res == BMKT_SUCCESS);
|
|
g_assert (msg_len + SENSOR_FW_CMD_HEADER_LEN == transfer->length);
|
|
|
|
/* Special case for async command sending (should only be used for
|
|
* cancellation). */
|
|
if (seq_num == -1)
|
|
{
|
|
g_assert (callback == NULL);
|
|
|
|
/* We just send and forget here. */
|
|
fpi_usb_transfer_submit (transfer, 1000, NULL, cmd_forget_cb, NULL);
|
|
}
|
|
else
|
|
{
|
|
/* Command should be send using the state machine. */
|
|
g_assert (self->cmd_pending_transfer == NULL);
|
|
|
|
self->cmd_pending_transfer = g_steal_pointer (&transfer);
|
|
|
|
if (self->cmd_ssm)
|
|
{
|
|
/* Continued command, we already have an SSM with a callback.
|
|
* There is nothing to do in this case, the command will be
|
|
* sent automatically. */
|
|
g_assert (callback == NULL);
|
|
}
|
|
else
|
|
{
|
|
/* Start of a new command, create the state machine. */
|
|
g_assert (callback != NULL);
|
|
|
|
self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self),
|
|
synaptics_cmd_run_state,
|
|
SYNAPTICS_CMD_NUM_STATES);
|
|
fpi_ssm_set_data (self->cmd_ssm, callback, NULL);
|
|
|
|
fpi_ssm_start (self->cmd_ssm, cmd_ssm_done);
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
parse_print_data (GVariant *data,
|
|
guint8 *finger,
|
|
const guint8 **user_id,
|
|
gsize *user_id_len)
|
|
{
|
|
g_autoptr(GVariant) user_id_var = NULL;
|
|
|
|
g_return_val_if_fail (data != NULL, FALSE);
|
|
g_return_val_if_fail (finger != NULL, FALSE);
|
|
g_return_val_if_fail (user_id != NULL, FALSE);
|
|
g_return_val_if_fail (user_id_len != NULL, FALSE);
|
|
|
|
*user_id = NULL;
|
|
*user_id_len = 0;
|
|
|
|
if (!g_variant_check_format_string (data, "(y@ay)", FALSE))
|
|
return FALSE;
|
|
|
|
g_variant_get (data,
|
|
"(y@ay)",
|
|
finger,
|
|
&user_id_var);
|
|
|
|
*user_id = g_variant_get_fixed_array (user_id_var, user_id_len, 1);
|
|
|
|
if (*user_id_len == 0 || *user_id_len > BMKT_MAX_USER_ID_LEN)
|
|
return FALSE;
|
|
|
|
if (*user_id_len <= 0 || *user_id[0] == ' ')
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static FpPrint *
|
|
create_print (FpiDeviceSynaptics *self,
|
|
guint8 *user_id,
|
|
guint8 finger_id)
|
|
{
|
|
FpPrint *print;
|
|
g_autofree gchar *user_id_safe;
|
|
GVariant *data = NULL;
|
|
GVariant *uid = NULL;
|
|
|
|
user_id_safe = g_strndup ((char *) user_id, BMKT_MAX_USER_ID_LEN);
|
|
|
|
print = fp_print_new (FP_DEVICE (self));
|
|
uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
|
|
user_id_safe,
|
|
strlen (user_id_safe),
|
|
1);
|
|
data = g_variant_new ("(y@ay)",
|
|
finger_id,
|
|
uid);
|
|
|
|
fpi_print_set_type (print, FPI_PRINT_RAW);
|
|
fpi_print_set_device_stored (print, TRUE);
|
|
g_object_set (print, "fpi-data", data, NULL);
|
|
g_object_set (print, "description", user_id_safe, NULL);
|
|
|
|
fpi_print_fill_from_user_id (print, user_id_safe);
|
|
|
|
return print;
|
|
}
|
|
|
|
static void
|
|
list_msg_cb (FpiDeviceSynaptics *self,
|
|
bmkt_response_t *resp,
|
|
GError *error)
|
|
{
|
|
bmkt_enroll_templates_resp_t *get_enroll_templates_resp;
|
|
|
|
if (error)
|
|
{
|
|
g_clear_pointer (&self->list_result, g_ptr_array_unref);
|
|
fpi_device_list_complete (FP_DEVICE (self), NULL, error);
|
|
return;
|
|
}
|
|
|
|
get_enroll_templates_resp = &resp->response.enroll_templates_resp;
|
|
|
|
switch (resp->response_id)
|
|
{
|
|
case BMKT_RSP_QUERY_FAIL:
|
|
if (resp->result == BMKT_FP_DATABASE_EMPTY)
|
|
{
|
|
fp_info ("Database is empty");
|
|
|
|
fpi_device_list_complete (FP_DEVICE (self),
|
|
g_steal_pointer (&self->list_result),
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
fp_info ("Failed to query enrolled users: %d", resp->result);
|
|
g_clear_pointer (&self->list_result, g_ptr_array_unref);
|
|
fpi_device_list_complete (FP_DEVICE (self),
|
|
NULL,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
|
|
"Failed to query enrolled users: %d",
|
|
resp->result));
|
|
}
|
|
break;
|
|
|
|
case BMKT_RSP_QUERY_RESPONSE_COMPLETE:
|
|
fp_info ("Query complete!");
|
|
|
|
fpi_device_list_complete (FP_DEVICE (self),
|
|
g_steal_pointer (&self->list_result),
|
|
NULL);
|
|
|
|
break;
|
|
|
|
case BMKT_RSP_TEMPLATE_RECORDS_REPORT:
|
|
|
|
for (int n = 0; n < BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH; n++)
|
|
{
|
|
FpPrint *print;
|
|
|
|
if (get_enroll_templates_resp->templates[n].user_id_len == 0)
|
|
continue;
|
|
|
|
fp_info ("![query %d of %d] template %d: status=0x%x, userId=%s, fingerId=%d",
|
|
get_enroll_templates_resp->query_sequence,
|
|
get_enroll_templates_resp->total_query_messages,
|
|
n,
|
|
get_enroll_templates_resp->templates[n].template_status,
|
|
get_enroll_templates_resp->templates[n].user_id,
|
|
get_enroll_templates_resp->templates[n].finger_id);
|
|
|
|
print = create_print (self,
|
|
get_enroll_templates_resp->templates[n].user_id,
|
|
get_enroll_templates_resp->templates[n].finger_id);
|
|
|
|
g_ptr_array_add (self->list_result, g_object_ref_sink (print));
|
|
}
|
|
|
|
synaptics_sensor_cmd (self,
|
|
self->cmd_seq_num,
|
|
BMKT_CMD_GET_NEXT_QUERY_RESPONSE,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
list (FpDevice *device)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
|
|
G_DEBUG_HERE ();
|
|
|
|
self->list_result = g_ptr_array_new_with_free_func (g_object_unref);
|
|
synaptics_sensor_cmd (self, 0, BMKT_CMD_GET_TEMPLATE_RECORDS, NULL, 0, list_msg_cb);
|
|
}
|
|
|
|
static void
|
|
verify_complete_after_finger_removal (FpiDeviceSynaptics *self)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
|
|
if (self->finger_on_sensor)
|
|
{
|
|
fp_dbg ("delaying verify report until after finger removal!");
|
|
self->cmd_complete_on_removal = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fpi_device_verify_complete (device, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
verify_msg_cb (FpiDeviceSynaptics *self,
|
|
bmkt_response_t *resp,
|
|
GError *error)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
bmkt_verify_resp_t *verify_resp;
|
|
|
|
if (error)
|
|
{
|
|
fpi_device_verify_complete (device, error);
|
|
return;
|
|
}
|
|
|
|
if (resp == NULL && self->cmd_complete_on_removal)
|
|
{
|
|
fpi_device_verify_complete (device, NULL);
|
|
return;
|
|
}
|
|
|
|
g_assert (resp != NULL);
|
|
|
|
verify_resp = &resp->response.verify_resp;
|
|
|
|
switch (resp->response_id)
|
|
{
|
|
case BMKT_RSP_VERIFY_READY:
|
|
fpi_device_report_finger_status_changes (device,
|
|
FP_FINGER_STATUS_NEEDED,
|
|
FP_FINGER_STATUS_NONE);
|
|
fp_info ("Place Finger on the Sensor!");
|
|
break;
|
|
|
|
case BMKT_RSP_CAPTURE_COMPLETE:
|
|
fp_info ("Fingerprint image capture complete!");
|
|
break;
|
|
|
|
case BMKT_RSP_VERIFY_FAIL:
|
|
if (resp->result == BMKT_SENSOR_STIMULUS_ERROR)
|
|
{
|
|
fp_info ("Match error occurred");
|
|
fpi_device_verify_report (device, FPI_MATCH_ERROR, NULL,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
|
|
verify_complete_after_finger_removal (self);
|
|
}
|
|
else if (resp->result == BMKT_FP_NO_MATCH)
|
|
{
|
|
fp_info ("Print didn't match");
|
|
fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL);
|
|
verify_complete_after_finger_removal (self);
|
|
}
|
|
else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS)
|
|
{
|
|
fp_info ("Print is not in database");
|
|
fpi_device_verify_complete (device,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
|
|
}
|
|
else
|
|
{
|
|
fp_warn ("Verify has failed: %d", resp->result);
|
|
fpi_device_verify_complete (device,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Unexpected result from device %d",
|
|
resp->result));
|
|
}
|
|
break;
|
|
|
|
case BMKT_RSP_VERIFY_OK:
|
|
fp_info ("Verify was successful! for user: %s finger: %d score: %f",
|
|
verify_resp->user_id, verify_resp->finger_id, verify_resp->match_result);
|
|
fpi_device_verify_report (device, FPI_MATCH_SUCCESS, NULL, NULL);
|
|
verify_complete_after_finger_removal (self);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
verify (FpDevice *device)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
FpPrint *print = NULL;
|
|
|
|
g_autoptr(GVariant) data = NULL;
|
|
guint8 finger;
|
|
const guint8 *user_id;
|
|
gsize user_id_len = 0;
|
|
|
|
fpi_device_get_verify_data (device, &print);
|
|
|
|
g_object_get (print, "fpi-data", &data, NULL);
|
|
g_debug ("data is %p", data);
|
|
if (!parse_print_data (data, &finger, &user_id, &user_id_len))
|
|
{
|
|
fpi_device_verify_complete (device,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
|
|
return;
|
|
}
|
|
|
|
G_DEBUG_HERE ();
|
|
|
|
synaptics_sensor_cmd (self, 0, BMKT_CMD_VERIFY_USER, user_id, user_id_len, verify_msg_cb);
|
|
}
|
|
|
|
static void
|
|
identify_complete_after_finger_removal (FpiDeviceSynaptics *self)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
|
|
if (self->finger_on_sensor)
|
|
{
|
|
fp_dbg ("delaying identify report until after finger removal!");
|
|
self->cmd_complete_on_removal = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fpi_device_identify_complete (device, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
identify_msg_cb (FpiDeviceSynaptics *self,
|
|
bmkt_response_t *resp,
|
|
GError *error)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
|
|
if (error)
|
|
{
|
|
fpi_device_identify_complete (device, error);
|
|
return;
|
|
}
|
|
|
|
if (resp == NULL && self->cmd_complete_on_removal)
|
|
{
|
|
fpi_device_identify_complete (device, NULL);
|
|
return;
|
|
}
|
|
|
|
g_assert (resp != NULL);
|
|
|
|
switch (resp->response_id)
|
|
{
|
|
case BMKT_RSP_ID_READY:
|
|
fp_info ("Place Finger on the Sensor!");
|
|
break;
|
|
|
|
case BMKT_RSP_SEND_NEXT_USER_ID:
|
|
{
|
|
compose_and_send_identify_msg (device);
|
|
break;
|
|
}
|
|
|
|
case BMKT_RSP_ID_FAIL:
|
|
if (resp->result == BMKT_SENSOR_STIMULUS_ERROR)
|
|
{
|
|
fp_info ("Match error occurred");
|
|
fpi_device_identify_report (device, NULL, NULL,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
|
|
identify_complete_after_finger_removal (self);
|
|
}
|
|
else if (resp->result == BMKT_FP_NO_MATCH)
|
|
{
|
|
fp_info ("Print didn't match");
|
|
fpi_device_identify_report (device, NULL, NULL, NULL);
|
|
identify_complete_after_finger_removal (self);
|
|
}
|
|
else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS)
|
|
{
|
|
fp_info ("Print is not in database");
|
|
fpi_device_identify_complete (device,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
|
|
}
|
|
else
|
|
{
|
|
fp_warn ("identify has failed: %d", resp->result);
|
|
fpi_device_identify_complete (device,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Unexpected result from device %d",
|
|
resp->result));
|
|
}
|
|
break;
|
|
|
|
case BMKT_RSP_ID_OK:
|
|
{
|
|
FpPrint *print = NULL;
|
|
GPtrArray *prints = NULL;
|
|
g_autoptr(GVariant) data = NULL;
|
|
gboolean found = FALSE;
|
|
guint index;
|
|
|
|
print = create_print (self,
|
|
resp->response.id_resp.user_id,
|
|
resp->response.id_resp.finger_id);
|
|
|
|
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);
|
|
|
|
identify_complete_after_finger_removal (self);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
identify (FpDevice *device)
|
|
{
|
|
init_identify_msg (device);
|
|
compose_and_send_identify_msg (device);
|
|
}
|
|
|
|
static void
|
|
init_identify_msg (FpDevice *device)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
|
|
self->id_idx = 0;
|
|
}
|
|
|
|
static void
|
|
compose_and_send_identify_msg (FpDevice *device)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
FpPrint *print = NULL;
|
|
GPtrArray *prints = NULL;
|
|
|
|
g_autoptr(GVariant) data = NULL;
|
|
guint8 finger;
|
|
const guint8 *user_id;
|
|
gsize user_id_len = 0;
|
|
g_autofree guint8 *payload = NULL;
|
|
guint8 payload_len = 0;
|
|
guint8 payloadOffset = 0;
|
|
|
|
fpi_device_get_identify_data (device, &prints);
|
|
if (prints->len > UINT8_MAX)
|
|
{
|
|
fpi_device_identify_complete (device,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
|
|
return;
|
|
}
|
|
if(self->id_idx >= prints->len)
|
|
{
|
|
fp_warn ("Device asked for more prints than we are providing.");
|
|
fpi_device_identify_complete (device,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Unexpected index"));
|
|
return;
|
|
}
|
|
print = g_ptr_array_index (prints, self->id_idx);
|
|
g_object_get (print, "fpi-data", &data, NULL);
|
|
g_debug ("data is %p", data);
|
|
if (!parse_print_data (data, &finger, &user_id, &user_id_len))
|
|
{
|
|
fpi_device_identify_complete (device,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
|
|
return;
|
|
}
|
|
if(self->id_idx == 0)
|
|
{
|
|
/*
|
|
* Construct payload.
|
|
* 1st byte is total number of IDs in list.
|
|
* 2nd byte is number of IDs in list.
|
|
* 1 byte for each ID length, maximum id length is 100.
|
|
* user_id_len bytes of each ID
|
|
*/
|
|
payload_len = 2 + 1 + user_id_len;
|
|
payload = g_malloc0 (payload_len);
|
|
payload[payloadOffset] = prints->len;
|
|
payloadOffset += 1;
|
|
payload[payloadOffset] = 1; /* send one id per message */
|
|
payloadOffset += 1;
|
|
payload[payloadOffset] = user_id_len;
|
|
payloadOffset += 1;
|
|
memcpy (&payload[payloadOffset], user_id, user_id_len);
|
|
payloadOffset += user_id_len;
|
|
|
|
G_DEBUG_HERE ();
|
|
|
|
synaptics_sensor_cmd (self, 0, BMKT_CMD_ID_USER_IN_ORDER, payload, payloadOffset, identify_msg_cb);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* 1st byte is the number of IDs
|
|
* 1 byte for each ID length
|
|
* id_length bytes for each ID
|
|
*/
|
|
payload_len = 1 + 1 + user_id_len;
|
|
payload = g_malloc0 (payload_len);
|
|
payload[payloadOffset] = 1; /* send one id per message */
|
|
payloadOffset += 1;
|
|
payload[payloadOffset] = user_id_len;
|
|
payloadOffset += 1;
|
|
memcpy (&payload[payloadOffset], user_id, user_id_len);
|
|
payloadOffset += user_id_len;
|
|
synaptics_sensor_cmd (self, self->cmd_seq_num, BMKT_CMD_ID_NEXT_USER, payload, payloadOffset, NULL);
|
|
}
|
|
self->id_idx++;
|
|
}
|
|
static void
|
|
enroll_msg_cb (FpiDeviceSynaptics *self,
|
|
bmkt_response_t *resp,
|
|
GError *error)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
bmkt_enroll_resp_t *enroll_resp;
|
|
|
|
if (error)
|
|
{
|
|
fpi_device_enroll_complete (device, NULL, error);
|
|
return;
|
|
}
|
|
|
|
enroll_resp = &resp->response.enroll_resp;
|
|
|
|
switch (resp->response_id)
|
|
{
|
|
case BMKT_RSP_ENROLL_READY:
|
|
{
|
|
self->enroll_stage = 0;
|
|
fpi_device_report_finger_status_changes (device,
|
|
FP_FINGER_STATUS_NEEDED,
|
|
FP_FINGER_STATUS_NONE);
|
|
fp_info ("Place Finger on the Sensor!");
|
|
break;
|
|
}
|
|
|
|
case BMKT_RSP_CAPTURE_COMPLETE:
|
|
{
|
|
fp_info ("Fingerprint image capture complete!");
|
|
break;
|
|
}
|
|
|
|
case BMKT_RSP_ENROLL_REPORT:
|
|
{
|
|
gint done_stages;
|
|
fp_info ("Enrollment is %d %% ", enroll_resp->progress);
|
|
|
|
done_stages = (enroll_resp->progress * ENROLL_SAMPLES + 99) / 100;
|
|
if (enroll_resp->progress < 100)
|
|
done_stages = MIN (done_stages, ENROLL_SAMPLES - 1);
|
|
|
|
/* Emit a retry error if there has been no discernible
|
|
* progress. Some firmware revisions report more required
|
|
* touches. */
|
|
if (self->enroll_stage == done_stages)
|
|
{
|
|
fpi_device_enroll_progress (device,
|
|
done_stages,
|
|
NULL,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
|
|
}
|
|
|
|
while (self->enroll_stage < done_stages)
|
|
{
|
|
self->enroll_stage += 1;
|
|
fpi_device_enroll_progress (device, self->enroll_stage, NULL, NULL);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BMKT_RSP_ENROLL_PAUSED:
|
|
{
|
|
fp_info ("Enrollment has been paused!");
|
|
break;
|
|
}
|
|
|
|
case BMKT_RSP_ENROLL_RESUMED:
|
|
{
|
|
fp_info ("Enrollment has been resumed!");
|
|
break;
|
|
}
|
|
|
|
case BMKT_RSP_ENROLL_FAIL:
|
|
{
|
|
fp_info ("Enrollment has failed!: %d", resp->result);
|
|
if (resp->result == BMKT_FP_DATABASE_FULL)
|
|
{
|
|
fpi_device_enroll_complete (device,
|
|
NULL,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_FULL));
|
|
}
|
|
else
|
|
{
|
|
fpi_device_enroll_complete (device,
|
|
NULL,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
|
|
"Enrollment failed (%d)",
|
|
resp->result));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BMKT_RSP_ENROLL_OK:
|
|
{
|
|
FpPrint *print = NULL;
|
|
|
|
fp_info ("Enrollment was successful!");
|
|
|
|
fpi_device_get_enroll_data (device, &print);
|
|
|
|
fpi_device_enroll_complete (device, g_object_ref (print), NULL);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
enroll (FpDevice *device)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
FpPrint *print = NULL;
|
|
GVariant *data = NULL;
|
|
GVariant *uid = NULL;
|
|
guint finger;
|
|
g_autofree gchar *user_id = NULL;
|
|
gssize user_id_len;
|
|
g_autofree guint8 *payload = NULL;
|
|
|
|
fpi_device_get_enroll_data (device, &print);
|
|
|
|
G_DEBUG_HERE ();
|
|
|
|
user_id = fpi_print_generate_user_id (print);
|
|
|
|
user_id_len = strlen (user_id);
|
|
user_id_len = MIN (BMKT_MAX_USER_ID_LEN, user_id_len);
|
|
|
|
/* We currently always use finger 1 from the devices point of view */
|
|
finger = 1;
|
|
|
|
uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
|
|
user_id,
|
|
user_id_len,
|
|
1);
|
|
data = g_variant_new ("(y@ay)",
|
|
finger,
|
|
uid);
|
|
|
|
fpi_print_set_type (print, FPI_PRINT_RAW);
|
|
fpi_print_set_device_stored (print, TRUE);
|
|
g_object_set (print, "fpi-data", data, NULL);
|
|
g_object_set (print, "description", user_id, NULL);
|
|
|
|
g_debug ("user_id: %s, finger: %d", user_id, finger);
|
|
|
|
payload = g_malloc0 (user_id_len + 2);
|
|
|
|
/* Backup options are not supported for Prometheus */
|
|
payload[0] = 0;
|
|
payload[1] = finger;
|
|
memcpy (payload + 2, user_id, user_id_len);
|
|
|
|
synaptics_sensor_cmd (self, 0, BMKT_CMD_ENROLL_USER, payload, user_id_len + 2, enroll_msg_cb);
|
|
}
|
|
|
|
static void
|
|
delete_msg_cb (FpiDeviceSynaptics *self,
|
|
bmkt_response_t *resp,
|
|
GError *error)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
bmkt_del_user_resp_t *del_user_resp;
|
|
|
|
if (error)
|
|
{
|
|
fpi_device_delete_complete (device, error);
|
|
return;
|
|
}
|
|
|
|
del_user_resp = &resp->response.del_user_resp;
|
|
|
|
switch (resp->response_id)
|
|
{
|
|
case BMKT_RSP_DELETE_PROGRESS:
|
|
fp_info ("Deleting Enrolled Users is %d%% complete",
|
|
del_user_resp->progress);
|
|
break;
|
|
|
|
case BMKT_RSP_DEL_USER_FP_FAIL:
|
|
fp_info ("Failed to delete enrolled user: %d", resp->result);
|
|
if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS)
|
|
fpi_device_delete_complete (device,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
|
|
else
|
|
fpi_device_delete_complete (device,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
|
|
break;
|
|
|
|
case BMKT_RSP_DEL_USER_FP_OK:
|
|
fp_info ("Successfully deleted enrolled user");
|
|
fpi_device_delete_complete (device, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
delete_print (FpDevice *device)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
FpPrint *print = NULL;
|
|
|
|
g_autoptr(GVariant) data = NULL;
|
|
guint8 finger;
|
|
const guint8 *user_id;
|
|
gsize user_id_len = 0;
|
|
g_autofree guint8 *payload = NULL;
|
|
|
|
fpi_device_get_delete_data (device, &print);
|
|
|
|
g_object_get (print, "fpi-data", &data, NULL);
|
|
g_debug ("data is %p", data);
|
|
if (!parse_print_data (data, &finger, &user_id, &user_id_len))
|
|
{
|
|
fpi_device_delete_complete (device,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
|
|
return;
|
|
}
|
|
|
|
G_DEBUG_HERE ();
|
|
|
|
payload = g_malloc0 (1 + user_id_len);
|
|
payload[0] = finger;
|
|
memcpy (payload + 1, user_id, user_id_len);
|
|
|
|
synaptics_sensor_cmd (self, 0, BMKT_CMD_DEL_USER_FP, payload, user_id_len + 1, delete_msg_cb);
|
|
}
|
|
|
|
static void
|
|
prob_msg_cb (FpiDeviceSynaptics *self,
|
|
bmkt_response_t *resp,
|
|
GError *error)
|
|
{
|
|
GUsbDevice *usb_dev = NULL;
|
|
g_autofree gchar *serial = NULL;
|
|
|
|
usb_dev = fpi_device_get_usb_device (FP_DEVICE (self));
|
|
|
|
if (error)
|
|
{
|
|
g_usb_device_close (usb_dev, NULL);
|
|
fpi_device_probe_complete (FP_DEVICE (self), NULL, NULL,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "unsupported firmware version"));
|
|
return;
|
|
}
|
|
|
|
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
|
|
serial = g_strdup ("emulated-device");
|
|
else
|
|
serial = g_usb_device_get_string_descriptor (usb_dev,
|
|
g_usb_device_get_serial_number_index (usb_dev),
|
|
&error);
|
|
|
|
/* BMKT_OPERATION_DENIED is returned if the sensor is already initialized */
|
|
if (resp->result == BMKT_SUCCESS || resp->result == BMKT_OPERATION_DENIED)
|
|
{
|
|
g_usb_device_close (usb_dev, NULL);
|
|
fpi_device_probe_complete (FP_DEVICE (self), serial, NULL, error);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Probe fingerprint sensor failed with %d!", resp->result);
|
|
g_usb_device_close (usb_dev, NULL);
|
|
fpi_device_probe_complete (FP_DEVICE (self), serial, NULL, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
|
|
}
|
|
}
|
|
|
|
static void
|
|
dev_probe (FpDevice *device)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
GUsbDevice *usb_dev;
|
|
|
|
g_autoptr(FpiUsbTransfer) transfer = NULL;
|
|
FpiByteReader reader;
|
|
GError *error = NULL;
|
|
guint16 status;
|
|
const guint8 *data;
|
|
gboolean read_ok = TRUE;
|
|
g_autofree gchar *serial = NULL;
|
|
gboolean retry = TRUE;
|
|
|
|
G_DEBUG_HERE ();
|
|
|
|
/* Claim usb interface */
|
|
usb_dev = fpi_device_get_usb_device (device);
|
|
if (!g_usb_device_open (usb_dev, &error))
|
|
{
|
|
fpi_device_probe_complete (device, NULL, NULL, error);
|
|
return;
|
|
}
|
|
|
|
if (!g_usb_device_claim_interface (usb_dev, 0, 0, &error))
|
|
goto err_close;
|
|
|
|
while(1)
|
|
{
|
|
/* TODO: Do not do this synchronous. */
|
|
transfer = fpi_usb_transfer_new (device);
|
|
fpi_usb_transfer_fill_bulk (transfer, USB_EP_REQUEST, SENSOR_FW_CMD_HEADER_LEN);
|
|
transfer->short_is_error = TRUE;
|
|
transfer->buffer[0] = SENSOR_CMD_GET_VERSION;
|
|
if (!fpi_usb_transfer_submit_sync (transfer, 1000, &error))
|
|
goto err_close;
|
|
|
|
g_clear_pointer (&transfer, fpi_usb_transfer_unref);
|
|
transfer = fpi_usb_transfer_new (device);
|
|
fpi_usb_transfer_fill_bulk (transfer, USB_EP_REPLY, 40);
|
|
if (!fpi_usb_transfer_submit_sync (transfer, 1000, &error))
|
|
goto err_close;
|
|
|
|
fpi_byte_reader_init (&reader, transfer->buffer, transfer->actual_length);
|
|
|
|
if (!fpi_byte_reader_get_uint16_le (&reader, &status))
|
|
{
|
|
g_warning ("Transfer in response to version query was too short");
|
|
error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO);
|
|
goto err_close;
|
|
}
|
|
if (status != 0)
|
|
{
|
|
g_warning ("Device responded with error: %d retry: %d", status, retry);
|
|
if(retry)
|
|
{
|
|
retry = FALSE;
|
|
continue;
|
|
}
|
|
error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO);
|
|
goto err_close;
|
|
}
|
|
break;
|
|
}
|
|
read_ok &= fpi_byte_reader_get_uint32_le (&reader, &self->mis_version.build_time);
|
|
read_ok &= fpi_byte_reader_get_uint32_le (&reader, &self->mis_version.build_num);
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.version_major);
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.version_minor);
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.target);
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.product);
|
|
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.silicon_rev);
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.formal_release);
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.platform);
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.patch);
|
|
if (fpi_byte_reader_get_data (&reader, sizeof (self->mis_version.serial_number), &data))
|
|
memcpy (self->mis_version.serial_number, data, sizeof (self->mis_version.serial_number));
|
|
else
|
|
read_ok = FALSE;
|
|
read_ok &= fpi_byte_reader_get_uint16_le (&reader, &self->mis_version.security);
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.iface);
|
|
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.device_type);
|
|
|
|
if (!read_ok)
|
|
{
|
|
g_warning ("Transfer in response to version query was too short");
|
|
error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO);
|
|
goto err_close;
|
|
}
|
|
|
|
fp_dbg ("Build Time: %d", self->mis_version.build_time);
|
|
fp_dbg ("Build Num: %d", self->mis_version.build_num);
|
|
fp_dbg ("Version: %d.%d", self->mis_version.version_major, self->mis_version.version_minor);
|
|
fp_dbg ("Target: %d", self->mis_version.target);
|
|
fp_dbg ("Product: %d", self->mis_version.product);
|
|
|
|
synaptics_sensor_cmd (self, 0, BMKT_CMD_FPS_INIT, NULL, 0, prob_msg_cb);
|
|
|
|
return;
|
|
|
|
err_close:
|
|
g_usb_device_close (usb_dev, NULL);
|
|
fpi_device_probe_complete (device, NULL, NULL, error);
|
|
}
|
|
|
|
static void
|
|
fps_init_msg_cb (FpiDeviceSynaptics *self,
|
|
bmkt_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_device_open_complete (FP_DEVICE (self), error);
|
|
return;
|
|
}
|
|
|
|
/* BMKT_OPERATION_DENIED is returned if the sensor is already initialized */
|
|
if (resp->result == BMKT_SUCCESS || resp->result == BMKT_OPERATION_DENIED)
|
|
{
|
|
fpi_device_open_complete (FP_DEVICE (self), NULL);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Initializing fingerprint sensor failed with %d!", resp->result);
|
|
fpi_device_open_complete (FP_DEVICE (self),
|
|
fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
|
|
}
|
|
}
|
|
static void
|
|
fps_deinit_cb (FpiDeviceSynaptics *self,
|
|
bmkt_response_t *resp,
|
|
GError *error)
|
|
{
|
|
/* Release usb interface */
|
|
g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (self)), 0, 0, &error);
|
|
|
|
g_clear_object (&self->interrupt_cancellable);
|
|
|
|
if (!error)
|
|
{
|
|
switch (resp->response_id)
|
|
{
|
|
case BMKT_RSP_POWER_DOWN_READY:
|
|
fp_info ("Fingerprint sensor ready to be powered down");
|
|
break;
|
|
|
|
case BMKT_RSP_POWER_DOWN_FAIL:
|
|
fp_info ("Failed to go to power down mode: %d", resp->result);
|
|
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
|
|
"Power down failed: %d", resp->result);
|
|
|
|
break;
|
|
}
|
|
}
|
|
fpi_device_close_complete (FP_DEVICE (self), error);
|
|
}
|
|
|
|
static void
|
|
dev_init (FpDevice *device)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
GError *error = NULL;
|
|
|
|
G_DEBUG_HERE ();
|
|
|
|
self->interrupt_cancellable = g_cancellable_new ();
|
|
|
|
/* Claim usb interface */
|
|
if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error))
|
|
goto error;
|
|
|
|
synaptics_sensor_cmd (self, 0, BMKT_CMD_FPS_INIT, NULL, 0, fps_init_msg_cb);
|
|
|
|
return;
|
|
|
|
error:
|
|
fpi_device_open_complete (FP_DEVICE (self), error);
|
|
}
|
|
|
|
static void
|
|
dev_exit (FpDevice *device)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
|
|
|
|
G_DEBUG_HERE ();
|
|
|
|
synaptics_sensor_cmd (self, 0, BMKT_CMD_POWER_DOWN_NOTIFY, NULL, 0, fps_deinit_cb);
|
|
}
|
|
|
|
static void
|
|
cancel (FpDevice *dev)
|
|
{
|
|
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (dev);
|
|
|
|
/* We just send out a cancel command and hope for the best. */
|
|
synaptics_sensor_cmd (self, -1, BMKT_CMD_CANCEL_OP, NULL, 0, NULL);
|
|
|
|
/* Cancel any current interrupt transfer (resulting us to go into
|
|
* response reading mode again); then create a new cancellable
|
|
* for the next transfers. */
|
|
g_cancellable_cancel (self->interrupt_cancellable);
|
|
g_clear_object (&self->interrupt_cancellable);
|
|
self->interrupt_cancellable = g_cancellable_new ();
|
|
}
|
|
|
|
static void
|
|
fpi_device_synaptics_init (FpiDeviceSynaptics *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
fpi_device_synaptics_class_init (FpiDeviceSynapticsClass *klass)
|
|
{
|
|
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
|
|
|
dev_class->id = FP_COMPONENT;
|
|
dev_class->full_name = SYNAPTICS_DRIVER_FULLNAME;
|
|
|
|
dev_class->type = FP_DEVICE_TYPE_USB;
|
|
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
|
|
dev_class->id_table = id_table;
|
|
dev_class->nr_enroll_stages = ENROLL_SAMPLES;
|
|
|
|
dev_class->open = dev_init;
|
|
dev_class->close = dev_exit;
|
|
dev_class->probe = dev_probe;
|
|
dev_class->verify = verify;
|
|
dev_class->identify = identify;
|
|
dev_class->enroll = enroll;
|
|
dev_class->delete = delete_print;
|
|
dev_class->cancel = cancel;
|
|
dev_class->list = list;
|
|
|
|
fpi_device_class_auto_initialize_features (dev_class);
|
|
}
|