Files
libfprint-fpc1020/libfprint/drivers/focaltech_moc/focaltech_moc.c
Marco Trevisan (Treviño) a9c6621119 focaltech_moc: add new pid:0xA27A
2025-12-06 01:39:32 +00:00

1881 lines
56 KiB
C

/*
* Copyright (C) 2022 FocalTech Electronics 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
*/
#include "focaltech_moc.h"
#include <ctype.h>
#define FP_COMPONENT "focaltech_moc"
#include "drivers_api.h"
G_DEFINE_TYPE (FpiDeviceFocaltechMoc, fpi_device_focaltech_moc, FP_TYPE_DEVICE)
static const FpIdEntry id_table[] = {
{ .vid = 0x2808, .pid = 0x9e48, },
{ .vid = 0x2808, .pid = 0xd979, },
{ .vid = 0x2808, .pid = 0xa27a, },
{ .vid = 0x2808, .pid = 0xa959, },
{ .vid = 0x2808, .pid = 0xa99a, },
{ .vid = 0x2808, .pid = 0xa57a, },
{ .vid = 0x2808, .pid = 0xa78a, },
{ .vid = 0x2808, .pid = 0x1579, },
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
};
typedef void (*SynCmdMsgCallback) (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error);
typedef struct
{
SynCmdMsgCallback callback;
} CommandData;
typedef struct
{
uint8_t h;
uint8_t l;
} FpCmdLen;
typedef struct
{
uint8_t magic;
FpCmdLen len;
} FpCmdHeader;
typedef struct
{
FpCmdHeader header;
uint8_t code;
uint8_t payload[0];
} FpCmd;
typedef struct
{
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
uint8_t b0;
uint8_t b1;
#else
uint8_t b1;
uint8_t b0;
#endif
} u16_bytes_t;
typedef union
{
u16_bytes_t s;
uint16_t v;
} u_u16_bytes_t;
static inline uint16_t
get_u16_from_u8_lh (uint8_t l, uint8_t h)
{
u_u16_bytes_t u_u16_bytes;
u_u16_bytes.v = 0;
u_u16_bytes.s.b0 = l;
u_u16_bytes.s.b1 = h;
return u_u16_bytes.v;
}
static inline uint8_t
get_u8_l_from_u16 (uint16_t v)
{
u_u16_bytes_t u_u16_bytes;
u_u16_bytes.v = v;
return u_u16_bytes.s.b0;
}
static inline uint8_t
get_u8_h_from_u16 (uint16_t v)
{
u_u16_bytes_t u_u16_bytes;
u_u16_bytes.v = v;
return u_u16_bytes.s.b1;
}
static uint8_t
fp_cmd_bcc (uint8_t *data, uint16_t len)
{
int i;
uint8_t bcc = 0;
for (i = 0; i < len; i++)
bcc ^= data[i];
return bcc;
}
static uint8_t *
focaltech_moc_compose_cmd (uint8_t cmd, const uint8_t *data, uint16_t len)
{
g_autofree uint8_t *cmd_buf = NULL;
FpCmd *fp_cmd = NULL;
uint8_t *bcc = NULL;
uint16_t header_len = len + sizeof (*bcc);
cmd_buf = g_new0 (uint8_t, sizeof (FpCmd) + header_len);
fp_cmd = (FpCmd *) cmd_buf;
fp_cmd->header.magic = 0x02;
fp_cmd->header.len.l = get_u8_l_from_u16 (header_len);
fp_cmd->header.len.h = get_u8_h_from_u16 (header_len);
fp_cmd->code = cmd;
if (data != NULL)
memcpy (fp_cmd->payload, data, len);
bcc = fp_cmd->payload + len;
*bcc = fp_cmd_bcc ((uint8_t *) &fp_cmd->header.len, bcc - (uint8_t *) &fp_cmd->header.len);
return g_steal_pointer (&cmd_buf);
}
static int
focaltech_moc_check_cmd (uint8_t *response_buf, uint16_t len)
{
int ret = -1;
FpCmd *fp_cmd = NULL;
uint8_t *bcc = NULL;
uint16_t header_len;
uint16_t data_len;
fp_cmd = (FpCmd *) response_buf;
if (len < sizeof (FpCmd) + sizeof (*bcc))
return ret;
if (fp_cmd->header.magic != 0x02)
return ret;
header_len = get_u16_from_u8_lh (fp_cmd->header.len.l, fp_cmd->header.len.h);
if (header_len < sizeof (*bcc))
return ret;
if ((sizeof (FpCmd) + header_len) > len)
return ret;
data_len = header_len - sizeof (*bcc);
bcc = fp_cmd->payload + data_len;
if (fp_cmd_bcc ((uint8_t *) &fp_cmd->header.len,
bcc - (uint8_t *) &fp_cmd->header.len) != *bcc)
return ret;
ret = 0;
return ret;
}
static void
fp_cmd_receive_cb (FpiUsbTransfer *transfer,
FpDevice *device,
gpointer userdata,
GError *error)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
CommandData *data = userdata;
int ssm_state = 0;
if (error)
{
fpi_ssm_mark_failed (transfer->ssm, g_steal_pointer (&error));
return;
}
if (data == NULL)
{
fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
return;
}
ssm_state = fpi_ssm_get_cur_state (transfer->ssm);
/* skip zero length package */
if (transfer->actual_length == 0)
{
fpi_ssm_jump_to_state (transfer->ssm, ssm_state);
return;
}
if (focaltech_moc_check_cmd (transfer->buffer, transfer->actual_length) != 0)
{
fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
return;
}
if (data->callback)
data->callback (self, transfer->buffer, transfer->actual_length, NULL);
fpi_ssm_mark_completed (transfer->ssm);
}
typedef enum {
FOCALTECH_CMD_SEND = 0,
FOCALTECH_CMD_GET,
FOCALTECH_CMD_NUM_STATES,
} FocaltechCmdState;
static void
fp_cmd_run_state (FpiSsm *ssm,
FpDevice *device)
{
FpiUsbTransfer *transfer;
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
switch (fpi_ssm_get_cur_state (ssm))
{
case FOCALTECH_CMD_SEND:
if (self->cmd_transfer)
{
self->cmd_transfer->ssm = ssm;
fpi_usb_transfer_submit (g_steal_pointer (&self->cmd_transfer),
FOCALTECH_MOC_CMD_TIMEOUT,
NULL,
fpi_ssm_usb_transfer_cb,
NULL);
}
else
{
fpi_ssm_next_state (ssm);
}
break;
case FOCALTECH_CMD_GET:
if (self->cmd_len_in == 0)
{
CommandData *data = fpi_ssm_get_data (ssm);
if (data->callback)
data->callback (self, NULL, 0, 0);
fpi_ssm_mark_completed (ssm);
return;
}
transfer = fpi_usb_transfer_new (device);
transfer->ssm = ssm;
fpi_usb_transfer_fill_bulk (transfer, self->bulk_in_ep, self->cmd_len_in);
fpi_usb_transfer_submit (transfer,
self->cmd_cancelable ? 0 : FOCALTECH_MOC_CMD_TIMEOUT,
self->cmd_cancelable ? fpi_device_get_cancellable (device) : NULL,
fp_cmd_receive_cb,
fpi_ssm_get_data (ssm));
break;
}
}
static void
fp_cmd_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error)
{
g_autoptr(GError) local_error = error;
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
CommandData *data = fpi_ssm_get_data (ssm);
self->cmd_ssm = NULL;
if (local_error && data->callback)
data->callback (self, NULL, 0, g_steal_pointer (&local_error));
}
static void
fp_cmd_ssm_done_data_free (CommandData *data)
{
g_free (data);
}
static void
focaltech_moc_get_cmd (FpDevice *device, guint8 *buffer_out,
gsize length_out, gsize length_in,
gboolean can_be_cancelled,
SynCmdMsgCallback callback)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
g_autoptr(FpiUsbTransfer) transfer = NULL;
CommandData *data = g_new0 (CommandData, 1);
transfer = fpi_usb_transfer_new (device);
transfer->short_is_error = TRUE;
fpi_usb_transfer_fill_bulk_full (transfer, self->bulk_out_ep, buffer_out,
length_out, g_free);
data->callback = callback;
self->cmd_transfer = g_steal_pointer (&transfer);
self->cmd_len_in = length_in + 1;
self->cmd_cancelable = can_be_cancelled;
self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self),
fp_cmd_run_state,
FOCALTECH_CMD_NUM_STATES);
fpi_ssm_set_data (self->cmd_ssm, data, (GDestroyNotify) fp_cmd_ssm_done_data_free);
fpi_ssm_start (self->cmd_ssm, fp_cmd_ssm_done);
}
struct UserId
{
uint8_t uid[32];
};
static void
fprint_set_uid (FpPrint *print, uint8_t *uid, size_t size)
{
GVariant *var_uid;
GVariant *var_data;
var_uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, uid, size, 1);
var_data = g_variant_new ("(@ay)", var_uid);
fpi_print_set_type (print, FPI_PRINT_RAW);
fpi_print_set_device_stored (print, TRUE);
g_object_set (print, "fpi-data", var_data, NULL);
}
enum enroll_states {
ENROLL_RSP_RETRY,
ENROLL_RSP_ENROLL_REPORT,
ENROLL_RSP_ENROLL_OK,
ENROLL_RSP_ENROLL_CANCEL_REPORT,
};
static void
enroll_status_report (FpiDeviceFocaltechMoc *self, int enroll_status_id,
int data, GError *error)
{
FpDevice *device = FP_DEVICE (self);
switch (enroll_status_id)
{
case ENROLL_RSP_RETRY:
{
fpi_device_enroll_progress (device, self->num_frames, NULL,
fpi_device_retry_new (FP_DEVICE_RETRY_CENTER_FINGER));
break;
}
case ENROLL_RSP_ENROLL_REPORT:
{
fpi_device_enroll_progress (device, self->num_frames, NULL, NULL);
break;
}
case ENROLL_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;
}
case ENROLL_RSP_ENROLL_CANCEL_REPORT:
{
fpi_device_enroll_complete (device, NULL,
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"Enrollment failed (%d) (ENROLL_RSP_ENROLL_CANCEL_REPORT)",
data));
}
}
}
static void
task_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
self->num_frames = 0;
self->task_ssm = NULL;
if (error)
fpi_device_action_error (device, g_steal_pointer (&error));
}
static const char *
get_g_usb_device_direction_des (GUsbDeviceDirection dir)
{
switch (dir)
{
case G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST:
return "G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST";
case G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE:
return "G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE";
default:
return "unknown";
}
}
static int
usb_claim_interface_probe (FpDevice *device, int claim, GError **error)
{
g_autoptr(GPtrArray) interfaces = NULL;
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
int ret = -1;
int i;
interfaces = g_usb_device_get_interfaces (fpi_device_get_usb_device (device), error);
for (i = 0; i < interfaces->len; i++)
{
GUsbInterface *cur_iface = g_ptr_array_index (interfaces, i);
g_autoptr(GPtrArray) endpoints = g_usb_interface_get_endpoints (cur_iface);
fp_dbg ("class:%x, subclass:%x, protocol:%x",
g_usb_interface_get_class (cur_iface),
g_usb_interface_get_subclass (cur_iface),
g_usb_interface_get_protocol (cur_iface));
if (claim == 1)
{
int j;
for (j = 0; j < endpoints->len; j++)
{
GUsbEndpoint *endpoint = g_ptr_array_index (endpoints, j);
GBytes *bytes = g_usb_endpoint_get_extra (endpoint);
fp_dbg ("bytes size:%ld", g_bytes_get_size (bytes));
fp_dbg ("kind:%x, max packet size:%d, poll interval:%d, refresh:%x, "
"sync address:%x, address:%x, number:%d, direction:%s",
g_usb_endpoint_get_kind (endpoint),
g_usb_endpoint_get_maximum_packet_size (endpoint),
g_usb_endpoint_get_polling_interval (endpoint),
g_usb_endpoint_get_refresh (endpoint),
g_usb_endpoint_get_synch_address (endpoint),
g_usb_endpoint_get_address (endpoint),
g_usb_endpoint_get_number (endpoint),
get_g_usb_device_direction_des (g_usb_endpoint_get_direction (endpoint)));
if (g_usb_endpoint_get_direction (endpoint) == G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST)
self->bulk_in_ep = g_usb_endpoint_get_address (endpoint);
else
self->bulk_out_ep = g_usb_endpoint_get_address (endpoint);
}
if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device),
g_usb_interface_get_number (cur_iface),
0, error))
return ret;
}
else if (!g_usb_device_release_interface (fpi_device_get_usb_device (device),
g_usb_interface_get_number (cur_iface),
0, error))
{
return ret;
}
}
ret = 0;
return ret;
}
static void
task_ssm_init_done (FpiSsm *ssm, FpDevice *device, GError *error)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
if (error)
usb_claim_interface_probe (device, 0, &error);
fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error));
}
struct EnrollTimes
{
uint8_t enroll_times;
};
static void
focaltech_moc_get_enroll_times (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
struct EnrollTimes *enroll_times = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
enroll_times = (struct EnrollTimes *) (fp_cmd + 1);
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
else
{
fp_dbg ("focaltechmoc enroll_times: %d", enroll_times->enroll_times + 1);
fpi_device_set_nr_enroll_stages (FP_DEVICE (self), enroll_times->enroll_times + 1);
fpi_ssm_next_state (self->task_ssm);
}
}
static void
focaltech_moc_release_finger (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
else
{
fpi_ssm_next_state (self->task_ssm);
}
}
enum dev_init_states {
DEV_INIT_GET_ENROLL_TIMES,
DEV_INIT_RELEASE_FINGER,
DEV_INIT_STATES,
};
static void
dev_init_handler (FpiSsm *ssm, FpDevice *device)
{
guint8 *cmd_buf = NULL;
uint16_t cmd_len = 0;
uint16_t resp_len = 0;
switch (fpi_ssm_get_cur_state (ssm))
{
case DEV_INIT_GET_ENROLL_TIMES:
cmd_len = 0;
resp_len = sizeof (struct EnrollTimes);
cmd_buf = focaltech_moc_compose_cmd (0xa5, NULL, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_get_enroll_times);
break;
case DEV_INIT_RELEASE_FINGER:
{
uint8_t d1 = 0x78;
cmd_len = sizeof (d1);
resp_len = 0;
cmd_buf = focaltech_moc_compose_cmd (0x82, &d1, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_release_finger);
break;
}
}
}
static void
focaltech_moc_open (FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
GError *error = NULL;
if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error))
{
fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error));
return;
}
if (usb_claim_interface_probe (device, 1, &error) != 0)
{
fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error));
return;
}
self->task_ssm = fpi_ssm_new (FP_DEVICE (self), dev_init_handler, DEV_INIT_STATES);
fpi_ssm_start (self->task_ssm, task_ssm_init_done);
}
static void
task_ssm_exit_done (FpiSsm *ssm, FpDevice *device, GError *error)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
if (!error)
{
GError *local_error = NULL;
if (usb_claim_interface_probe (device, 0, &local_error) < 0)
g_propagate_error (&error, g_steal_pointer (&local_error));
}
fpi_device_close_complete (FP_DEVICE (self), error);
self->task_ssm = NULL;
}
enum dev_exit_states {
DEV_EXIT_START,
DEV_EXIT_STATES,
};
static void
dev_exit_handler (FpiSsm *ssm, FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
switch (fpi_ssm_get_cur_state (ssm))
{
case DEV_EXIT_START:
fpi_ssm_next_state (self->task_ssm);
break;
default:
g_assert_not_reached ();
}
}
static void
focaltech_moc_close (FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
fp_info ("Focaltechmoc dev_exit");
self->task_ssm = fpi_ssm_new (FP_DEVICE (self), dev_exit_handler, DEV_EXIT_STATES);
fpi_ssm_start (self->task_ssm, task_ssm_exit_done);
}
enum identify_states {
MOC_IDENTIFY_RELEASE_FINGER,
MOC_IDENTIFY_WAIT_FINGER,
MOC_IDENTIFY_WAIT_FINGER_DELAY,
MOC_IDENTIFY_CAPTURE,
MOC_IDENTIFY_MATCH,
MOC_IDENTIFY_NUM_STATES,
};
static void
focaltech_moc_identify_wait_finger_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
uint8_t *finger_status = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
finger_status = (uint8_t *) (fp_cmd + 1);
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
else
{
if (*finger_status == 0x01)
fpi_ssm_jump_to_state (self->task_ssm, MOC_IDENTIFY_CAPTURE);
else
fpi_ssm_jump_to_state (self->task_ssm, MOC_IDENTIFY_WAIT_FINGER_DELAY);
}
}
static void
focaltech_moc_identify_wait_finger_delay (FpDevice *device,
void *user_data)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
fpi_ssm_jump_to_state (self->task_ssm, MOC_IDENTIFY_WAIT_FINGER);
}
enum FprintError {
ERROR_NONE,
ERROR_QUALITY,
ERROR_SHORT,
ERROR_LEFT,
ERROR_RIGHT,
ERROR_NONFINGER,
ERROR_NOMOVE,
ERROR_OTHER,
};
struct CaptureResult
{
uint8_t error;
uint8_t remain;
};
static void
focaltech_moc_identify_capture_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
struct CaptureResult *capture_result = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
capture_result = (struct CaptureResult *) (fp_cmd + 1);
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
else
{
if (capture_result->error == ERROR_NONE)
{
fpi_ssm_next_state (self->task_ssm);
}
else
{
if (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_VERIFY)
{
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_ERROR, NULL, error);
fpi_device_verify_complete (FP_DEVICE (self), NULL);
}
else
{
fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, error);
fpi_device_identify_complete (FP_DEVICE (self), NULL);
}
fpi_ssm_mark_failed (self->task_ssm, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
}
}
}
static void
identify_status_report (FpiDeviceFocaltechMoc *self, FpPrint *print, GError *error)
{
FpDevice *device = FP_DEVICE (self);
if (print == NULL)
{
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY)
{
fpi_device_identify_report (device, NULL, NULL, NULL);
fpi_device_identify_complete (device, NULL);
}
else
{
fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL);
fpi_device_verify_complete (device, NULL);
}
}
else
{
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY)
{
GPtrArray *prints;
gboolean found = FALSE;
guint index;
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);
fpi_device_identify_complete (device, NULL);
}
else
{
FpPrint *verify_print = NULL;
fpi_device_get_verify_data (device, &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);
fpi_device_verify_complete (device, NULL);
}
}
}
static void
focaltech_moc_identify_match_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
struct UserId *user_id = NULL;
FpPrint *print = NULL;
fp_cmd = (FpCmd *) buffer_in;
user_id = (struct UserId *) (fp_cmd + 1);
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
if (fp_cmd->code == 0x04)
{
print = fp_print_new (FP_DEVICE (self));
fprint_set_uid (print, user_id->uid, sizeof (user_id->uid));
}
identify_status_report (self, print, error);
fpi_ssm_next_state (self->task_ssm);
}
static void
focaltech_identify_run_state (FpiSsm *ssm, FpDevice *device)
{
guint8 *cmd_buf = NULL;
uint16_t cmd_len = 0;
uint16_t resp_len = 0;
switch (fpi_ssm_get_cur_state (ssm))
{
case MOC_IDENTIFY_RELEASE_FINGER:
{
uint8_t d1 = 0x78;
cmd_len = sizeof (d1);
resp_len = 0;
cmd_buf = focaltech_moc_compose_cmd (0x82, &d1, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_release_finger);
break;
}
case MOC_IDENTIFY_WAIT_FINGER:
{
uint8_t data = 0x02;
cmd_len = sizeof (uint8_t);
resp_len = sizeof (uint8_t);
cmd_buf = focaltech_moc_compose_cmd (0x80, &data, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_identify_wait_finger_cb);
break;
}
case MOC_IDENTIFY_WAIT_FINGER_DELAY:
fpi_device_add_timeout (device, 50,
focaltech_moc_identify_wait_finger_delay,
NULL, NULL);
break;
case MOC_IDENTIFY_CAPTURE:
cmd_len = 0;
resp_len = sizeof (struct CaptureResult);
cmd_buf = focaltech_moc_compose_cmd (0xa6, NULL, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_identify_capture_cb);
break;
case MOC_IDENTIFY_MATCH:
cmd_len = 0;
resp_len = sizeof (struct UserId);
cmd_buf = focaltech_moc_compose_cmd (0xaa, NULL, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_identify_match_cb);
break;
}
}
static void
focaltech_moc_identify (FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
self->task_ssm = fpi_ssm_new (device,
focaltech_identify_run_state,
MOC_IDENTIFY_NUM_STATES);
fpi_ssm_start (self->task_ssm, task_ssm_done);
}
enum moc_enroll_states {
MOC_ENROLL_GET_ENROLLED_INFO,
MOC_ENROLL_GET_ENROLLED_LIST,
MOC_ENROLL_RELEASE_FINGER,
MOC_ENROLL_START_ENROLL,
MOC_ENROLL_WAIT_FINGER,
MOC_ENROLL_WAIT_FINGER_DELAY,
MOC_ENROLL_ENROLL_CAPTURE,
MOC_ENROLL_SET_ENROLLED_INFO,
MOC_ENROLL_COMMIT_RESULT,
MOC_ENROLL_NUM_STATES,
};
struct EnrolledInfoItem
{
uint8_t uid[FOCALTECH_MOC_UID_PREFIX_LENGTH];
uint8_t user_id[FOCALTECH_MOC_USER_ID_LENGTH];
};
struct UserDes
{
uint8_t finger;
char username[FOCALTECH_MOC_USER_ID_LENGTH];
};
struct EnrolledInfo
{
uint8_t actived[FOCALTECH_MOC_MAX_FINGERS];
struct EnrolledInfoItem items[FOCALTECH_MOC_MAX_FINGERS];
struct UserId user_id[FOCALTECH_MOC_MAX_FINGERS];
struct UserDes user_des[FOCALTECH_MOC_MAX_FINGERS];
};
typedef struct
{
GPtrArray *list_result;
struct EnrolledInfo *enrolled_info;
} FpActionData;
struct EnrolledInfoSetData
{
uint8_t data;
struct EnrolledInfoItem items[FOCALTECH_MOC_MAX_FINGERS];
};
static void
fp_action_ssm_done_data_free (FpActionData *data)
{
g_clear_pointer (&data->list_result, g_ptr_array_unref);
g_clear_pointer (&data->enrolled_info, g_free);
g_free (data);
}
static void
focaltech_moc_get_enrolled_info_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
struct EnrolledInfoItem *items = NULL;
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
items = (struct EnrolledInfoItem *) (fp_cmd + 1);
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
else
{
memcpy (&data->enrolled_info->items[0], items,
FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem));
fpi_ssm_next_state (self->task_ssm);
}
}
struct UidList
{
uint8_t actived[FOCALTECH_MOC_MAX_FINGERS];
struct UserId uid[FOCALTECH_MOC_MAX_FINGERS];
};
static int
focaltech_moc_get_enrolled_info_item (FpiDeviceFocaltechMoc *self,
uint8_t *uid,
struct EnrolledInfoItem **pitem, int *index)
{
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
int ret = -1;
int i;
for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++)
{
struct EnrolledInfoItem *item = &data->enrolled_info->items[i];
if (memcmp (item->uid, uid, FOCALTECH_MOC_UID_PREFIX_LENGTH) == 0)
{
data->enrolled_info->actived[i] = 1;
*pitem = item;
*index = i;
ret = 0;
break;
}
}
return ret;
}
static void
focaltech_moc_get_enrolled_list_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
struct UidList *uid_list = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
uid_list = (struct UidList *) (fp_cmd + 1);
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
else
{
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
int i;
for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++)
{
if (uid_list->actived[i] != 0)
{
struct UserId *user_id = &uid_list->uid[i];
FpPrint *print = fp_print_new (FP_DEVICE (self));
struct EnrolledInfoItem *item = NULL;
int index;
fp_info ("focaltechmoc add slot: %d", i);
fprint_set_uid (print, user_id->uid, sizeof (user_id->uid));
if (focaltech_moc_get_enrolled_info_item (self, user_id->uid, &item, &index) == 0)
{
g_autofree gchar *userid_safe = NULL;
const gchar *username;
userid_safe = g_strndup ((const char *) &item->user_id, FOCALTECH_MOC_USER_ID_LENGTH);
fp_dbg ("%s", userid_safe);
fpi_print_fill_from_user_id (print, userid_safe);
memcpy (data->enrolled_info->user_id[index].uid, user_id->uid, 32);
data->enrolled_info->user_des[index].finger = fp_print_get_finger (print);
username = fp_print_get_username (print);
if (username != NULL)
strncpy (data->enrolled_info->user_des[index].username, username, 64);
}
g_ptr_array_add (data->list_result, g_object_ref_sink (print));
}
}
for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++)
{
struct EnrolledInfoItem *item = &data->enrolled_info->items[i];
if (data->enrolled_info->actived[i] == 0)
memset (item, 0, sizeof (struct EnrolledInfoItem));
}
fpi_ssm_next_state (self->task_ssm);
}
}
static void
focaltech_moc_enroll_wait_finger_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
uint8_t *finger_status = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
finger_status = (uint8_t *) (fp_cmd + 1);
if (fp_cmd->code != 0x04)
{
fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER_DELAY);
}
else
{
if (*finger_status == 0x01)
fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_ENROLL_CAPTURE);
else
fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER_DELAY);
}
}
static void
focaltech_moc_enroll_wait_finger_delay (FpDevice *device,
void *user_data
)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER);
}
static void
focaltech_moc_start_enroll_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
struct UserId *user_id = NULL;
FpPrint *print = NULL;
struct EnrolledInfoItem *item = NULL;
int index;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
user_id = (struct UserId *) (fp_cmd + 1);
if (fp_cmd->code != 0x04)
{
if (fp_cmd->code == 0x05)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_FULL,
"device data full!!"));
}
else
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
return;
}
if (focaltech_moc_get_enrolled_info_item (self, user_id->uid, &item, &index) == 0)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"uid error!!"));
}
else
{
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
g_autofree gchar *userid_safe = NULL;
gsize userid_len;
uint8_t found = 0;
int i;
struct EnrolledInfoItem *free_item = NULL;
for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++)
{
item = &data->enrolled_info->items[i];
if (data->enrolled_info->actived[i] == 0)
{
found = 1;
free_item = item;
break;
}
}
if (found == 0)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"no uid slot!!"));
}
else
{
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
fprint_set_uid (print, user_id->uid, sizeof (user_id->uid));
userid_safe = fpi_print_generate_user_id (print);
userid_len = strlen (userid_safe);
userid_len = MIN (FOCALTECH_MOC_USER_ID_LENGTH, userid_len);
fp_info ("focaltechmoc user id: %s", userid_safe);
memcpy (free_item->uid, user_id->uid, FOCALTECH_MOC_UID_PREFIX_LENGTH);
memcpy (free_item->user_id, userid_safe, userid_len);
fpi_ssm_next_state (self->task_ssm);
}
}
}
static void
focaltech_moc_enroll_capture_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
struct CaptureResult *capture_result = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
capture_result = (struct CaptureResult *) (fp_cmd + 1);
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
else
{
if (capture_result->error == ERROR_NONE)
{
self->num_frames += 1;
enroll_status_report (self, ENROLL_RSP_ENROLL_REPORT, self->num_frames, NULL);
fp_info ("focaltechmoc remain: %d", capture_result->remain);
}
else
{
enroll_status_report (self, ENROLL_RSP_RETRY, self->num_frames, NULL);
}
if (self->num_frames == fp_device_get_nr_enroll_stages (FP_DEVICE (self)))
fpi_ssm_next_state (self->task_ssm);
else
fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER);
}
}
static void
focaltech_moc_set_enrolled_info_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
return;
}
fpi_ssm_next_state (self->task_ssm);
}
static void
focaltech_moc_commit_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
else
{
fp_info ("focaltech_moc_commit_cb success");
enroll_status_report (self, ENROLL_RSP_ENROLL_OK, self->num_frames, NULL);
fpi_ssm_next_state (self->task_ssm);
}
}
static void
focaltech_enroll_run_state (FpiSsm *ssm, FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
guint8 *cmd_buf = NULL;
uint16_t cmd_len = 0;
uint16_t resp_len = 0;
switch (fpi_ssm_get_cur_state (ssm))
{
case MOC_ENROLL_GET_ENROLLED_INFO:
{
uint8_t data = 0x00;
cmd_len = sizeof (uint8_t);
resp_len = sizeof (struct EnrolledInfoItem) * FOCALTECH_MOC_MAX_FINGERS;
cmd_buf = focaltech_moc_compose_cmd (0xaf, &data, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_get_enrolled_info_cb);
break;
}
case MOC_ENROLL_GET_ENROLLED_LIST:
{
cmd_len = 0;
resp_len = sizeof (struct UidList);
cmd_buf = focaltech_moc_compose_cmd (0xab, NULL, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_get_enrolled_list_cb);
break;
}
case MOC_ENROLL_RELEASE_FINGER:
{
uint8_t d1 = 0x78;
cmd_len = sizeof (d1);
resp_len = 0;
cmd_buf = focaltech_moc_compose_cmd (0x82, &d1, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_release_finger);
break;
}
case MOC_ENROLL_START_ENROLL:
cmd_len = 0;
resp_len = sizeof (struct UserId);
cmd_buf = focaltech_moc_compose_cmd (0xa9, NULL, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_start_enroll_cb);
break;
case MOC_ENROLL_WAIT_FINGER:
{
uint8_t data = 0x02;
cmd_len = sizeof (uint8_t);
resp_len = sizeof (uint8_t);
cmd_buf = focaltech_moc_compose_cmd (0x80, &data, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_enroll_wait_finger_cb);
break;
}
case MOC_ENROLL_WAIT_FINGER_DELAY:
fpi_device_add_timeout (device, 50,
focaltech_moc_enroll_wait_finger_delay,
NULL, NULL);
break;
case MOC_ENROLL_ENROLL_CAPTURE:
cmd_len = 0;
resp_len = sizeof (struct CaptureResult);
cmd_buf = focaltech_moc_compose_cmd (0xa6, NULL, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_enroll_capture_cb);
break;
case MOC_ENROLL_SET_ENROLLED_INFO:
{
g_autofree struct EnrolledInfoSetData *set_data = NULL;
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
cmd_len = sizeof (struct EnrolledInfoSetData);
resp_len = 0;
set_data = (struct EnrolledInfoSetData *) g_malloc0 (cmd_len);
set_data->data = 0x01;
memcpy (&set_data->items[0], &data->enrolled_info->items[0],
FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem));
cmd_buf = focaltech_moc_compose_cmd (0xaf, (const uint8_t *) set_data, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_set_enrolled_info_cb);
break;
}
case MOC_ENROLL_COMMIT_RESULT:
{
FpPrint *print = NULL;
g_autoptr(GVariant) data = NULL;
g_autoptr(GVariant) user_id_var = NULL;
const guint8 *user_id;
gsize user_id_len = 0;
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
g_object_get (print, "fpi-data", &data, NULL);
if (!g_variant_check_format_string (data, "(@ay)", FALSE))
{
fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
return;
}
g_variant_get (data,
"(@ay)",
&user_id_var);
user_id = g_variant_get_fixed_array (user_id_var, &user_id_len, 1);
cmd_len = user_id_len;
resp_len = 0;
cmd_buf = focaltech_moc_compose_cmd (0xa3, user_id, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_commit_cb);
break;
}
}
}
static void
focaltech_moc_enroll (FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
FpActionData *data = g_new0 (FpActionData, 1);
data->enrolled_info = g_new0 (struct EnrolledInfo, 1);
data->list_result = g_ptr_array_new_with_free_func (g_object_unref);
self->task_ssm = fpi_ssm_new (FP_DEVICE (self),
focaltech_enroll_run_state,
MOC_ENROLL_NUM_STATES);
fpi_ssm_set_data (self->task_ssm, data, (GDestroyNotify) fp_action_ssm_done_data_free);
fpi_ssm_start (self->task_ssm, task_ssm_done);
}
static void
focaltech_moc_delete_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
if (error)
{
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
fp_cmd = (FpCmd *) buffer_in;
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Can't get response!!"));
}
else
{
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
int ssm_state;
if (self->delete_slot != -1)
{
fp_dbg ("delete slot %d", self->delete_slot);
data->enrolled_info->actived[self->delete_slot] = 0;
memset (&data->enrolled_info->items[self->delete_slot], 0, sizeof (struct EnrolledInfoItem));
memset (&data->enrolled_info->user_id[self->delete_slot], 0, sizeof (struct UserId));
memset (&data->enrolled_info->user_des[self->delete_slot], 0, sizeof (struct UserDes));
}
ssm_state = fpi_ssm_get_cur_state (self->task_ssm);
fpi_ssm_jump_to_state (self->task_ssm, ssm_state);
}
}
enum delete_states {
MOC_DELETE_GET_ENROLLED_INFO,
MOC_DELETE_GET_ENROLLED_LIST,
MOC_DELETE_SET_ENROLLED_INFO,
MOC_DELETE_BY_UID,
MOC_DELETE_BY_USER_INFO,
MOC_DELETE_NUM_STATES,
};
static void
focaltech_delete_run_state (FpiSsm *ssm, FpDevice *device)
{
guint8 *cmd_buf = NULL;
uint16_t cmd_len = 0;
uint16_t resp_len = 0;
switch (fpi_ssm_get_cur_state (ssm))
{
case MOC_DELETE_GET_ENROLLED_INFO:
{
uint8_t data = 0x00;
cmd_len = sizeof (uint8_t);
resp_len = sizeof (struct EnrolledInfoItem) * FOCALTECH_MOC_MAX_FINGERS;
cmd_buf = focaltech_moc_compose_cmd (0xaf, &data, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_get_enrolled_info_cb);
break;
}
case MOC_DELETE_GET_ENROLLED_LIST:
{
cmd_len = 0;
resp_len = sizeof (struct UidList) + sizeof (struct UserId) * FOCALTECH_MOC_MAX_FINGERS;
cmd_buf = focaltech_moc_compose_cmd (0xab, NULL, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_get_enrolled_list_cb);
break;
}
case MOC_DELETE_SET_ENROLLED_INFO:
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
g_autofree struct EnrolledInfoSetData *set_data = NULL;
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
cmd_len = sizeof (struct EnrolledInfoSetData);
resp_len = 0;
set_data = (struct EnrolledInfoSetData *) g_malloc0 (cmd_len);
set_data->data = 0x01;
memcpy (&set_data->items[0], &data->enrolled_info->items[0], FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem));
cmd_buf = focaltech_moc_compose_cmd (0xaf, (const uint8_t *) set_data, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_set_enrolled_info_cb);
break;
}
case MOC_DELETE_BY_UID:
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
FpPrint *print = NULL;
g_autoptr(GVariant) data = NULL;
g_autoptr(GVariant) user_id_var = NULL;
const guint8 *user_id;
gsize user_id_len = 0;
struct EnrolledInfoItem *item = NULL;
int index;
self->delete_slot = -1;
fpi_device_get_delete_data (device, &print);
g_object_get (print, "fpi-data", &data, NULL);
if (!g_variant_check_format_string (data, "(@ay)", FALSE))
{
fpi_device_delete_complete (device,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
return;
}
g_variant_get (data, "(@ay)", &user_id_var);
user_id = g_variant_get_fixed_array (user_id_var, &user_id_len, 1);
if (focaltech_moc_get_enrolled_info_item (self, (uint8_t *) user_id, &item, &index) == 0)
self->delete_slot = index;
if (self->delete_slot != -1)
{
cmd_len = sizeof (struct UserId);
resp_len = 0;
cmd_buf = focaltech_moc_compose_cmd (0xa8, user_id, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_delete_cb);
}
else
{
fpi_ssm_next_state (self->task_ssm);
}
break;
}
case MOC_DELETE_BY_USER_INFO:
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
FpPrint *print = NULL;
const guint8 *user_id;
const gchar *username;
uint8_t finger;
int i;
self->delete_slot = -1;
fpi_device_get_delete_data (device, &print);
username = fp_print_get_username (print);
finger = fp_print_get_finger (print);
for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++)
{
struct UserDes *user_des = &data->enrolled_info->user_des[i];
if (username == NULL)
continue;
if (strncmp (user_des->username, username, FOCALTECH_MOC_USER_ID_LENGTH) != 0)
continue;
if (finger != user_des->finger)
continue;
self->delete_slot = i;
}
if (self->delete_slot != -1)
{
user_id = (const guint8 *) &data->enrolled_info->user_id[self->delete_slot].uid[0];
cmd_len = sizeof (struct UserId);
resp_len = 0;
cmd_buf = focaltech_moc_compose_cmd (0xa8, user_id, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_delete_cb);
}
else
{
fpi_device_delete_complete (FP_DEVICE (self), NULL);
fpi_ssm_next_state (self->task_ssm);
}
break;
}
}
}
static void
focaltech_moc_delete_print (FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
FpActionData *data = g_new0 (FpActionData, 1);
data->enrolled_info = g_new0 (struct EnrolledInfo, 1);
data->list_result = g_ptr_array_new_with_free_func (g_object_unref);
self->task_ssm = fpi_ssm_new (device,
focaltech_delete_run_state,
MOC_DELETE_NUM_STATES);
fpi_ssm_set_data (self->task_ssm, data, (GDestroyNotify) fp_action_ssm_done_data_free);
fpi_ssm_start (self->task_ssm, task_ssm_done);
}
enum moc_list_states {
MOC_LIST_GET_ENROLLED_INFO,
MOC_LIST_GET_ENROLLED_LIST,
MOC_LIST_REPORT,
MOC_LIST_NUM_STATES,
};
static void
focaltech_list_run_state (FpiSsm *ssm, FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
guint8 *cmd_buf = NULL;
uint16_t cmd_len = 0;
uint16_t resp_len = 0;
switch (fpi_ssm_get_cur_state (ssm))
{
case MOC_LIST_GET_ENROLLED_INFO:
{
uint8_t data = 0x00;
cmd_len = sizeof (uint8_t);
resp_len = sizeof (struct EnrolledInfoItem) * FOCALTECH_MOC_MAX_FINGERS;
cmd_buf = focaltech_moc_compose_cmd (0xaf, &data, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_get_enrolled_info_cb);
break;
}
case MOC_LIST_GET_ENROLLED_LIST:
{
cmd_len = 0;
resp_len = sizeof (struct UidList) + sizeof (struct UserId) * FOCALTECH_MOC_MAX_FINGERS;
cmd_buf = focaltech_moc_compose_cmd (0xab, NULL, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
1,
focaltech_moc_get_enrolled_list_cb);
break;
}
case MOC_LIST_REPORT:
{
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
fpi_device_list_complete (FP_DEVICE (self), g_steal_pointer (&data->list_result), NULL);
fpi_ssm_next_state (self->task_ssm);
break;
}
}
}
static void
focaltech_moc_list (FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
FpActionData *data = g_new0 (FpActionData, 1);
data->enrolled_info = g_new0 (struct EnrolledInfo, 1);
data->list_result = g_ptr_array_new_with_free_func (g_object_unref);
self->task_ssm = fpi_ssm_new (device,
focaltech_list_run_state,
MOC_LIST_NUM_STATES);
fpi_ssm_set_data (self->task_ssm, data, (GDestroyNotify) fp_action_ssm_done_data_free);
fpi_ssm_start (self->task_ssm, task_ssm_done);
}
static void
fpi_device_focaltech_moc_init (FpiDeviceFocaltechMoc *self)
{
G_DEBUG_HERE ();
}
static void
fpi_device_focaltech_moc_class_init (FpiDeviceFocaltechMocClass *klass)
{
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
dev_class->id = FP_COMPONENT;
dev_class->full_name = FOCALTECH_MOC_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 = FOCALTECH_MOC_MAX_FINGERS;
dev_class->temp_hot_seconds = -1;
dev_class->open = focaltech_moc_open;
dev_class->close = focaltech_moc_close;
dev_class->verify = focaltech_moc_identify;
dev_class->enroll = focaltech_moc_enroll;
dev_class->identify = focaltech_moc_identify;
dev_class->delete = focaltech_moc_delete_print;
dev_class->list = focaltech_moc_list;
fpi_device_class_auto_initialize_features (dev_class);
}