mirror of
https://github.com/morgan9e/libfprint-fpc1020
synced 2026-04-15 00:44:25 +09:00
This is was leading to a potential error due to misaligned memory:
../libfprint/drivers/goodixmoc/goodix.c:167:20: runtime error: load of
misaligned address 0x00001165c989 for type 'uint32_t', which requires 4 byte
alignment
0x00001165c989: note: pointer points here
00 00 00 00 0a ac b3 09 00 00 00 00 00 00 55 53 42 00 00 00 00 00 56 42 53 00 00 00 00 00 30 30
^
#0 0x7ff3ba98d190 in fp_cmd_receive_cb ../libfprint/drivers/goodixmoc/goodix.c:167
#1 0x7ff3baa3b235 in transfer_finish_cb ../libfprint/fpi-usb-transfer.c:352
#2 0x7ff3c18ca862 in g_task_return_now ../../glib/gio/gtask.c:1363
#3 0x7ff3c18ca89c in complete_in_idle_cb ../../glib/gio/gtask.c:1377
#4 0x7ff3c228470b in g_main_dispatch ../../glib/glib/gmain.c:3373
#5 0x7ff3c22868de in g_main_context_dispatch_unlocked ../../glib/glib/gmain.c:4224
#6 0x7ff3c22868de in g_main_context_iterate_unlocked ../../glib/glib/gmain.c:4289
#7 0x7ff3c2286fef in g_main_context_iteration ../../glib/glib/gmain.c:4354
#8 0x7ff3ba8d2fe5 in fp_device_open_sync ../libfprint/fp-device.c:1874
1689 lines
50 KiB
C
1689 lines
50 KiB
C
/*
|
|
* Goodix Moc driver for libfprint
|
|
* Copyright (C) 2019 Shenzhen Goodix Technology Co., Ltd.
|
|
*
|
|
* 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 "goodixmoc"
|
|
|
|
#include "drivers_api.h"
|
|
#include "fpi-byte-reader.h"
|
|
|
|
#include "goodix_proto.h"
|
|
#include "goodix.h"
|
|
|
|
|
|
/* Default enroll stages number */
|
|
#define DEFAULT_ENROLL_SAMPLES 8
|
|
/* Usb port setting */
|
|
#define EP_IN (3 | FPI_USB_ENDPOINT_IN)
|
|
#define EP_OUT (1 | FPI_USB_ENDPOINT_OUT)
|
|
|
|
#define EP_IN_MAX_BUF_SIZE (2048)
|
|
|
|
#define MAX_USER_ID_LEN (64)
|
|
|
|
/* Command transfer timeout :ms*/
|
|
#define CMD_TIMEOUT (1000)
|
|
#define ACK_TIMEOUT (2000)
|
|
#define DATA_TIMEOUT (5000)
|
|
|
|
|
|
struct _FpiDeviceGoodixMoc
|
|
{
|
|
FpDevice parent;
|
|
FpiSsm *task_ssm;
|
|
FpiSsm *cmd_ssm;
|
|
FpiUsbTransfer *cmd_transfer;
|
|
gboolean cmd_cancelable;
|
|
pgxfp_sensor_cfg_t sensorcfg;
|
|
gint enroll_stage;
|
|
gint max_enroll_stage;
|
|
gint max_stored_prints;
|
|
GPtrArray *list_result;
|
|
guint8 template_id[TEMPLATE_ID_SIZE];
|
|
gboolean is_power_button_shield_on;
|
|
|
|
};
|
|
|
|
G_DEFINE_TYPE (FpiDeviceGoodixMoc, fpi_device_goodixmoc, FP_TYPE_DEVICE)
|
|
|
|
typedef void (*SynCmdMsgCallback) (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error);
|
|
|
|
typedef struct
|
|
{
|
|
guint8 cmd;
|
|
SynCmdMsgCallback callback;
|
|
} CommandData;
|
|
|
|
static gboolean parse_print_data (GVariant *data,
|
|
guint8 *finger,
|
|
const guint8 **tid,
|
|
gsize *tid_len,
|
|
const guint8 **user_id,
|
|
gsize *user_id_len);
|
|
|
|
static FpPrint *
|
|
fp_print_from_template (FpiDeviceGoodixMoc *self, template_format_t *template)
|
|
{
|
|
FpPrint *print;
|
|
GVariant *data;
|
|
GVariant *tid;
|
|
GVariant *uid;
|
|
g_autofree gchar *userid = NULL;
|
|
|
|
userid = g_strndup ((gchar *) template->payload.data, template->payload.size);
|
|
|
|
print = fp_print_new (FP_DEVICE (self));
|
|
|
|
tid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
|
|
template->tid,
|
|
TEMPLATE_ID_SIZE,
|
|
1);
|
|
|
|
uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
|
|
template->payload.data,
|
|
template->payload.size,
|
|
1);
|
|
|
|
data = g_variant_new ("(y@ay@ay)",
|
|
template->finger_index,
|
|
tid,
|
|
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", userid, NULL);
|
|
fpi_print_fill_from_user_id (print, userid);
|
|
|
|
return print;
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* fp_cmd_xxx Function
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
fp_cmd_receive_cb (FpiUsbTransfer *transfer,
|
|
FpDevice *device,
|
|
gpointer user_data,
|
|
GError *error)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
FpiByteReader reader = {0};
|
|
CommandData *data = user_data;
|
|
int ret = -1, ssm_state = 0;
|
|
gxfp_cmd_response_t cmd_reponse = {0, };
|
|
pack_header header;
|
|
guint32 crc32_calc = 0;
|
|
guint32 crc32 = 0;
|
|
guint16 cmd = 0;
|
|
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm, 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;
|
|
}
|
|
|
|
ret = gx_proto_parse_header (transfer->buffer, transfer->actual_length, &header);
|
|
if (ret != 0)
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Corrupted message header received"));
|
|
return;
|
|
}
|
|
|
|
reader.data = transfer->buffer;
|
|
reader.size = transfer->actual_length;
|
|
if (!fpi_byte_reader_set_pos (&reader, PACKAGE_HEADER_SIZE + header.len))
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Package crc read failed"));
|
|
}
|
|
|
|
gx_proto_crc32_calc (transfer->buffer, PACKAGE_HEADER_SIZE + header.len, (uint8_t *) &crc32_calc);
|
|
|
|
if (!fpi_byte_reader_get_uint32_le (&reader, &crc32) ||
|
|
crc32_calc != crc32)
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Package crc check failed"));
|
|
return;
|
|
}
|
|
|
|
cmd = MAKE_CMD_EX (header.cmd0, header.cmd1);
|
|
|
|
ret = gx_proto_parse_body (cmd, &transfer->buffer[PACKAGE_HEADER_SIZE], header.len, &cmd_reponse);
|
|
if (ret != 0)
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Corrupted message received"));
|
|
return;
|
|
}
|
|
/* ack */
|
|
if(header.cmd0 == RESPONSE_PACKAGE_CMD)
|
|
{
|
|
if (data->cmd != cmd_reponse.parse_msg.ack_cmd)
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Unexpected response, got 0x%x",
|
|
cmd_reponse.parse_msg.ack_cmd));
|
|
|
|
return;
|
|
}
|
|
fpi_ssm_next_state (transfer->ssm);
|
|
return;
|
|
}
|
|
/* data */
|
|
if (data->cmd != header.cmd0)
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Unexpected cmd, got 0x%x",
|
|
header.cmd0));
|
|
return;
|
|
}
|
|
if (data->callback)
|
|
data->callback (self, &cmd_reponse, NULL);
|
|
|
|
fpi_ssm_mark_completed (transfer->ssm);
|
|
}
|
|
|
|
|
|
static void
|
|
fp_cmd_run_state (FpiSsm *ssm,
|
|
FpDevice *dev)
|
|
{
|
|
FpiUsbTransfer *transfer;
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (dev);
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case FP_CMD_SEND:
|
|
if (self->cmd_transfer)
|
|
{
|
|
self->cmd_transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (g_steal_pointer (&self->cmd_transfer),
|
|
CMD_TIMEOUT,
|
|
NULL,
|
|
fpi_ssm_usb_transfer_cb,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
fpi_ssm_next_state (ssm);
|
|
}
|
|
break;
|
|
|
|
case FP_CMD_GET_ACK:
|
|
transfer = fpi_usb_transfer_new (dev);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_fill_bulk (transfer, EP_IN, EP_IN_MAX_BUF_SIZE);
|
|
fpi_usb_transfer_submit (transfer,
|
|
ACK_TIMEOUT,
|
|
NULL,
|
|
fp_cmd_receive_cb,
|
|
fpi_ssm_get_data (ssm));
|
|
|
|
break;
|
|
|
|
case FP_CMD_GET_DATA:
|
|
transfer = fpi_usb_transfer_new (dev);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_fill_bulk (transfer, EP_IN, EP_IN_MAX_BUF_SIZE);
|
|
fpi_usb_transfer_submit (transfer,
|
|
self->cmd_cancelable ? 0 : DATA_TIMEOUT,
|
|
self->cmd_cancelable ? fpi_device_get_cancellable (dev) : NULL,
|
|
fp_cmd_receive_cb,
|
|
fpi_ssm_get_data (ssm));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
fp_cmd_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (dev);
|
|
CommandData *data = fpi_ssm_get_data (ssm);
|
|
|
|
self->cmd_ssm = NULL;
|
|
/* Notify about the SSM failure from here instead. */
|
|
if (error)
|
|
{
|
|
if (data->callback)
|
|
data->callback (self, NULL, error);
|
|
else
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
static FpiUsbTransfer *
|
|
alloc_cmd_transfer (FpDevice *dev,
|
|
guint8 cmd0,
|
|
guint8 cmd1,
|
|
const guint8 *data,
|
|
guint16 data_len)
|
|
{
|
|
gint ret = -1;
|
|
|
|
g_autoptr(FpiUsbTransfer) transfer = NULL;
|
|
|
|
guint32 total_len = data_len + PACKAGE_HEADER_SIZE + PACKAGE_CRC_SIZE;
|
|
|
|
g_return_val_if_fail (data || data_len == 0, NULL);
|
|
|
|
transfer = fpi_usb_transfer_new (dev);
|
|
|
|
fpi_usb_transfer_fill_bulk (transfer, EP_OUT, total_len);
|
|
|
|
ret = gx_proto_build_package (transfer->buffer, &total_len, MAKE_CMD_EX (cmd0, cmd1), data, data_len);
|
|
|
|
g_return_val_if_fail (ret == 0, NULL);
|
|
|
|
return g_steal_pointer (&transfer);
|
|
}
|
|
|
|
static void
|
|
fp_cmd_ssm_done_data_free (CommandData *data)
|
|
{
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
goodix_sensor_cmd (FpiDeviceGoodixMoc *self,
|
|
guint8 cmd0,
|
|
guint8 cmd1,
|
|
gboolean bwait_data_delay,
|
|
const guint8 * payload,
|
|
gssize payload_len,
|
|
SynCmdMsgCallback callback)
|
|
{
|
|
|
|
g_autoptr(FpiUsbTransfer) transfer = NULL;
|
|
|
|
CommandData *data = g_new0 (CommandData, 1);
|
|
|
|
transfer = alloc_cmd_transfer (FP_DEVICE (self), cmd0, cmd1, payload, payload_len);
|
|
|
|
data->cmd = cmd0;
|
|
data->callback = callback;
|
|
|
|
self->cmd_transfer = g_steal_pointer (&transfer);
|
|
self->cmd_cancelable = bwait_data_delay;
|
|
|
|
self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self),
|
|
fp_cmd_run_state,
|
|
FP_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);
|
|
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* fp_pwr_btn_shield_cb Function
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
fp_pwr_btn_shield_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
if (resp->result >= GX_FAILED)
|
|
{
|
|
fp_dbg ("Setting power button shield failed, result: 0x%x", resp->result);
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
|
|
return;
|
|
}
|
|
if (resp->power_button_shield_resp.resp_cmd1 == MOC_CMD1_PWR_BTN_SHIELD_ON)
|
|
self->is_power_button_shield_on = true;
|
|
else
|
|
self->is_power_button_shield_on = false;
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
/******************************************************************************
|
|
*
|
|
* fp_verify_xxxx Function
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
fp_verify_capture_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
if (resp->result >= GX_FAILED)
|
|
{
|
|
fp_dbg ("Capture sample failed, result: 0x%x", resp->result);
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
|
|
return;
|
|
}
|
|
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
|
FP_FINGER_STATUS_PRESENT,
|
|
FP_FINGER_STATUS_NONE);
|
|
if (resp->capture_data_resp.img_quality == 0)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_REMOVE_FINGER));
|
|
return;
|
|
}
|
|
else if (resp->capture_data_resp.img_coverage < 35)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_CENTER_FINGER));
|
|
return;
|
|
}
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_verify_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
FpPrint *new_scan = NULL;
|
|
FpPrint *matching = NULL;
|
|
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
if (resp->verify.match)
|
|
{
|
|
new_scan = fp_print_from_template (self, &resp->verify.template);
|
|
|
|
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
|
|
{
|
|
fpi_device_get_verify_data (device, &matching);
|
|
if (!fp_print_equal (matching, new_scan))
|
|
matching = NULL;
|
|
}
|
|
else
|
|
{
|
|
GPtrArray *templates = NULL;
|
|
fpi_device_get_identify_data (device, &templates);
|
|
|
|
for (gint i = 0; i < templates->len; i++)
|
|
{
|
|
if (fp_print_equal (g_ptr_array_index (templates, i), new_scan))
|
|
{
|
|
matching = g_ptr_array_index (templates, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
|
|
fpi_device_verify_report (device, matching ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL, new_scan, error);
|
|
else
|
|
fpi_device_identify_report (device, matching, new_scan, error);
|
|
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
|
|
}
|
|
|
|
static void
|
|
fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
guint8 param[3] = { 0 };
|
|
guint8 nonce[TEMPLATE_ID_SIZE] = { 0 };
|
|
|
|
param[0] = 0x01;
|
|
param[1] = self->sensorcfg->config[10];
|
|
param[2] = self->sensorcfg->config[11];
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case FP_VERIFY_PWR_BTN_SHIELD_ON:
|
|
goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_ON,
|
|
false,
|
|
NULL,
|
|
0,
|
|
fp_pwr_btn_shield_cb);
|
|
break;
|
|
|
|
case FP_VERIFY_CAPTURE:
|
|
fpi_device_report_finger_status_changes (device,
|
|
FP_FINGER_STATUS_NEEDED,
|
|
FP_FINGER_STATUS_NONE);
|
|
goodix_sensor_cmd (self, MOC_CMD0_CAPTURE_DATA, MOC_CMD1_DEFAULT,
|
|
true,
|
|
(const guint8 *) ¶m,
|
|
G_N_ELEMENTS (param),
|
|
fp_verify_capture_cb);
|
|
break;
|
|
|
|
case FP_VERIFY_IDENTIFY:
|
|
goodix_sensor_cmd (self, MOC_CMD0_IDENTIFY, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) nonce,
|
|
TEMPLATE_ID_SIZE,
|
|
fp_verify_cb);
|
|
break;
|
|
|
|
case FP_VERIFY_PWR_BTN_SHIELD_OFF:
|
|
goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_OFF,
|
|
false,
|
|
NULL,
|
|
0,
|
|
fp_pwr_btn_shield_cb);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
fp_verify_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (dev);
|
|
|
|
fp_info ("Verify complete!");
|
|
|
|
if (error && error->domain == FP_DEVICE_RETRY)
|
|
{
|
|
if (fpi_device_get_current_action (dev) == FPI_DEVICE_ACTION_VERIFY)
|
|
fpi_device_verify_report (dev, FPI_MATCH_ERROR, NULL, g_steal_pointer (&error));
|
|
else
|
|
fpi_device_identify_report (dev, NULL, NULL, g_steal_pointer (&error));
|
|
}
|
|
|
|
if (fpi_device_get_current_action (dev) == FPI_DEVICE_ACTION_VERIFY)
|
|
fpi_device_verify_complete (dev, error);
|
|
|
|
else
|
|
fpi_device_identify_complete (dev, error);
|
|
|
|
self->task_ssm = NULL;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* fp__xxxx Function
|
|
*
|
|
*****************************************************************************/
|
|
static gboolean
|
|
encode_finger_id (
|
|
const guint8 * tid,
|
|
guint16 tid_len,
|
|
const guint8 * uid,
|
|
guint16 uid_len,
|
|
guint8 ** fid,
|
|
guint16 * fid_len
|
|
)
|
|
{
|
|
guint8 * buffer = NULL;
|
|
guint16 offset = 0;
|
|
|
|
g_return_val_if_fail (tid != NULL, FALSE);
|
|
g_return_val_if_fail (uid != NULL, FALSE);
|
|
g_return_val_if_fail (fid != NULL, FALSE);
|
|
g_return_val_if_fail (fid_len != NULL, FALSE);
|
|
|
|
*fid_len = (guint16) (70 + uid_len); // must include fingerid length
|
|
|
|
*fid = (guint8 *) g_malloc0 (*fid_len + 2);
|
|
|
|
buffer = *fid;
|
|
offset = 0;
|
|
buffer[offset++] = LOBYTE (*fid_len);
|
|
buffer[offset++] = HIBYTE (*fid_len);
|
|
|
|
buffer[offset++] = 67;
|
|
buffer[offset++] = 1;
|
|
buffer[offset++] = 1; // finger index
|
|
buffer[offset++] = 0; //
|
|
|
|
offset += 32;
|
|
|
|
memcpy (&buffer[offset], tid, MIN (tid_len, TEMPLATE_ID_SIZE));
|
|
offset += 32; // offset == 68
|
|
|
|
buffer[offset++] = uid_len;
|
|
memcpy (&buffer[offset], uid, uid_len);
|
|
offset += (guint8) uid_len;
|
|
|
|
buffer[offset++] = 0;
|
|
|
|
if (offset != (*fid_len + 2))
|
|
{
|
|
memset (buffer, 0, *fid_len);
|
|
*fid_len = 0;
|
|
|
|
fp_err ("offset != fid_len, %d != %d", offset, *fid_len);
|
|
return FALSE;
|
|
}
|
|
*fid_len += 2;
|
|
|
|
return TRUE;
|
|
}
|
|
/******************************************************************************
|
|
*
|
|
* fp_enroll_xxxx Function
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
fp_enroll_enum_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
if (resp->result != GX_SUCCESS)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
|
|
"Failed to enumerate fingers, result: 0x%x",
|
|
resp->result));
|
|
return;
|
|
}
|
|
if (resp->finger_list_resp.finger_num >= self->max_stored_prints)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_FULL));
|
|
return;
|
|
}
|
|
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_enroll_create_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
memcpy (self->template_id, resp->enroll_create.tid, TEMPLATE_ID_SIZE);
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_enroll_capture_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
/* */
|
|
if (resp->result >= GX_FAILED)
|
|
{
|
|
fp_info ("Capture sample failed, result: 0x%x", resp->result);
|
|
fpi_device_enroll_progress (FP_DEVICE (self),
|
|
self->enroll_stage,
|
|
NULL,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
|
|
fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_CAPTURE);
|
|
return;
|
|
}
|
|
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
|
FP_FINGER_STATUS_PRESENT,
|
|
FP_FINGER_STATUS_NONE);
|
|
if ((resp->capture_data_resp.img_quality < self->sensorcfg->config[4]) ||
|
|
(resp->capture_data_resp.img_coverage < self->sensorcfg->config[5]))
|
|
{
|
|
fp_info ("Capture sample poor quality(%d): %d or coverage(%d): %d",
|
|
self->sensorcfg->config[4],
|
|
resp->capture_data_resp.img_quality,
|
|
self->sensorcfg->config[5],
|
|
resp->capture_data_resp.img_coverage);
|
|
fpi_device_enroll_progress (FP_DEVICE (self),
|
|
self->enroll_stage,
|
|
NULL,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_CENTER_FINGER));
|
|
fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_CAPTURE);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
}
|
|
static void
|
|
fp_enroll_update_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
|
|
if (resp->enroll_update.img_preoverlay > self->sensorcfg->config[3])
|
|
{
|
|
fp_dbg ("Sample overlapping ratio is too High(%d): %d ",
|
|
self->sensorcfg->config[3],
|
|
resp->enroll_update.img_preoverlay);
|
|
/* here should tips move finger and try again */
|
|
fpi_device_enroll_progress (FP_DEVICE (self),
|
|
self->enroll_stage,
|
|
NULL,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_REMOVE_FINGER));
|
|
}
|
|
else if (resp->enroll_update.rollback)
|
|
{
|
|
fpi_device_enroll_progress (FP_DEVICE (self),
|
|
self->enroll_stage,
|
|
NULL,
|
|
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
|
|
}
|
|
else
|
|
{
|
|
self->enroll_stage++;
|
|
fpi_device_enroll_progress (FP_DEVICE (self), self->enroll_stage, NULL, NULL);
|
|
}
|
|
/* if enroll complete, no need to wait finger up */
|
|
if (self->enroll_stage >= self->max_enroll_stage)
|
|
{
|
|
fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_CHECK_DUPLICATE);
|
|
return;
|
|
}
|
|
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_enroll_check_duplicate_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
if (resp->check_duplicate_resp.duplicate)
|
|
{
|
|
g_autoptr(FpPrint) print = NULL;
|
|
|
|
print = g_object_ref_sink (fp_print_from_template (self, &resp->check_duplicate_resp.template));
|
|
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_DUPLICATE,
|
|
"Finger was already enrolled as '%s'",
|
|
fp_print_get_description (print)));
|
|
return;
|
|
}
|
|
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_enroll_commit_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
if (resp->result >= GX_FAILED)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Commit template failed with errcode: 0x%x", resp->result));
|
|
return;
|
|
}
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_finger_mode_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
/* if reach max timeout(5sec) finger not up, switch to finger up again */
|
|
if (resp->finger_status.status == GX_ERROR_WAIT_FINGER_UP_TIMEOUT)
|
|
{
|
|
fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_WAIT_FINGER_UP);
|
|
return;
|
|
}
|
|
else if (resp->finger_status.status != GX_SUCCESS)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Switch finger mode failed"));
|
|
return;
|
|
}
|
|
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
|
FP_FINGER_STATUS_NONE,
|
|
FP_FINGER_STATUS_PRESENT);
|
|
if (self->enroll_stage < self->max_enroll_stage)
|
|
{
|
|
fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_CAPTURE);
|
|
return;
|
|
}
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
FpPrint *print = NULL;
|
|
GVariant *data = NULL;
|
|
GVariant *uid = NULL;
|
|
GVariant *tid = NULL;
|
|
guint finger;
|
|
guint16 user_id_len;
|
|
guint16 payload_len = 0;
|
|
g_autofree gchar *user_id = NULL;
|
|
g_autofree guint8 *payload = NULL;
|
|
guint8 dummy[3] = { 0 };
|
|
|
|
dummy[1] = self->sensorcfg->config[4];
|
|
dummy[2] = self->sensorcfg->config[5];
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case FP_ENROLL_PWR_BTN_SHIELD_ON:
|
|
{
|
|
goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_ON,
|
|
false,
|
|
NULL,
|
|
0,
|
|
fp_pwr_btn_shield_cb);
|
|
}
|
|
break;
|
|
|
|
case FP_ENROLL_ENUM:
|
|
{
|
|
goodix_sensor_cmd (self, MOC_CMD0_GETFINGERLIST, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) &dummy,
|
|
1,
|
|
fp_enroll_enum_cb);
|
|
}
|
|
break;
|
|
|
|
case FP_ENROLL_CREATE:
|
|
{
|
|
goodix_sensor_cmd (self, MOC_CMD0_ENROLL_INIT, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) &dummy,
|
|
1,
|
|
fp_enroll_create_cb);
|
|
}
|
|
break;
|
|
|
|
case FP_ENROLL_CAPTURE:
|
|
fpi_device_report_finger_status_changes (device,
|
|
FP_FINGER_STATUS_NEEDED,
|
|
FP_FINGER_STATUS_NONE);
|
|
goodix_sensor_cmd (self, MOC_CMD0_CAPTURE_DATA, MOC_CMD1_DEFAULT,
|
|
true,
|
|
(const guint8 *) &dummy,
|
|
3,
|
|
fp_enroll_capture_cb);
|
|
break;
|
|
|
|
case FP_ENROLL_UPDATE:
|
|
dummy[0] = 1;
|
|
dummy[1] = self->sensorcfg->config[2];
|
|
dummy[2] = self->sensorcfg->config[3];
|
|
goodix_sensor_cmd (self, MOC_CMD0_ENROLL, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) &dummy,
|
|
3,
|
|
fp_enroll_update_cb);
|
|
break;
|
|
|
|
case FP_ENROLL_WAIT_FINGER_UP:
|
|
dummy[0] = 0;
|
|
goodix_sensor_cmd (self, MOC_CMD0_FINGER_MODE, MOC_CMD1_SET_FINGER_UP,
|
|
true,
|
|
(const guint8 *) &dummy,
|
|
1,
|
|
fp_finger_mode_cb);
|
|
break;
|
|
|
|
case FP_ENROLL_CHECK_DUPLICATE:
|
|
goodix_sensor_cmd (self, MOC_CMD0_CHECK4DUPLICATE, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) &dummy,
|
|
3,
|
|
fp_enroll_check_duplicate_cb);
|
|
break;
|
|
|
|
case FP_ENROLL_COMMIT:
|
|
{
|
|
fpi_device_get_enroll_data (device, &print);
|
|
user_id = fpi_print_generate_user_id (print);
|
|
user_id_len = strlen (user_id);
|
|
user_id_len = MIN (100, user_id_len);
|
|
finger = 1;
|
|
|
|
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
|
|
memset (self->template_id, 0, TEMPLATE_ID_SIZE);
|
|
uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
|
|
user_id,
|
|
user_id_len,
|
|
1);
|
|
|
|
tid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
|
|
self->template_id,
|
|
TEMPLATE_ID_SIZE,
|
|
1);
|
|
|
|
data = g_variant_new ("(y@ay@ay)",
|
|
finger,
|
|
tid,
|
|
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, user_id_len: %d, finger: %d", user_id, user_id_len, finger);
|
|
|
|
if(!encode_finger_id (self->template_id,
|
|
TEMPLATE_ID_SIZE,
|
|
(guint8 *) user_id,
|
|
user_id_len,
|
|
&payload,
|
|
&payload_len))
|
|
{
|
|
fpi_ssm_mark_failed (ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"encode_finger_id failed"));
|
|
return;
|
|
}
|
|
goodix_sensor_cmd (self, MOC_CMD0_COMMITENROLLMENT, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) payload,
|
|
payload_len,
|
|
fp_enroll_commit_cb);
|
|
|
|
}
|
|
break;
|
|
|
|
case FP_ENROLL_PWR_BTN_SHIELD_OFF:
|
|
{
|
|
goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_OFF,
|
|
false,
|
|
NULL,
|
|
0,
|
|
fp_pwr_btn_shield_cb);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fp_enroll_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (dev);
|
|
FpPrint *print = NULL;
|
|
|
|
if (error)
|
|
{
|
|
fpi_device_enroll_complete (dev, NULL, error);
|
|
return;
|
|
}
|
|
fp_info ("Enrollment complete!");
|
|
|
|
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
|
|
|
|
fpi_device_enroll_complete (FP_DEVICE (self), g_object_ref (print), NULL);
|
|
|
|
self->task_ssm = NULL;
|
|
}
|
|
/******************************************************************************
|
|
*
|
|
* fp_init_xxxx Function
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
fp_init_version_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
g_autofree gchar *fw_type = NULL;
|
|
g_autofree gchar *fw_version = NULL;
|
|
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
|
|
G_STATIC_ASSERT (sizeof (resp->version_info.fwtype) == 8);
|
|
G_STATIC_ASSERT (sizeof (resp->version_info.fwversion) == 8);
|
|
|
|
fw_type = g_strndup ((const char *) resp->version_info.fwtype, sizeof (resp->version_info.fwtype));
|
|
|
|
fp_info ("Firmware type: %s", fw_type);
|
|
if (g_strcmp0 (fw_type, "APP") != 0)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED,
|
|
"Please update firmware using fwupd"));
|
|
return;
|
|
}
|
|
fw_version = g_strndup ((const char *) resp->version_info.fwversion, sizeof (resp->version_info.fwversion));
|
|
fp_info ("Firmware version: %s", fw_version);
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_init_config_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
self->max_stored_prints = resp->finger_config.max_stored_prints;
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_init_cb_reset_or_complete (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fp_warn ("Template storage appears to have been corrupted! Error was: %s", error->message);
|
|
fp_warn ("A known reason for this to happen is a firmware bug triggered by another storage area being initialized.");
|
|
fpi_ssm_jump_to_state (self->task_ssm, FP_INIT_RESET_DEVICE);
|
|
}
|
|
else
|
|
{
|
|
fpi_ssm_mark_completed (self->task_ssm);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fp_init_reset_device_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fp_warn ("Reset failed: %s", error->message);
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
if ((resp->result >= GX_FAILED) && (resp->result != GX_ERROR_FINGER_ID_NOEXIST))
|
|
{
|
|
fp_warn ("Reset failed, device reported: 0x%x", resp->result);
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
|
|
"Failed clear storage, result: 0x%x",
|
|
resp->result));
|
|
return;
|
|
}
|
|
|
|
fp_warn ("Reset completed");
|
|
fpi_ssm_mark_completed (self->task_ssm);
|
|
}
|
|
|
|
static void
|
|
fp_init_sm_run_state (FpiSsm *ssm, FpDevice *device)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
guint8 dummy = 0;
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case FP_INIT_VERSION:
|
|
goodix_sensor_cmd (self, MOC_CMD0_GET_VERSION, MOC_CMD1_DEFAULT,
|
|
false,
|
|
&dummy,
|
|
1,
|
|
fp_init_version_cb);
|
|
break;
|
|
|
|
case FP_INIT_CONFIG:
|
|
goodix_sensor_cmd (self, MOC_CMD0_UPDATE_CONFIG, MOC_CMD1_WRITE_CFG_TO_FLASH,
|
|
false,
|
|
(guint8 *) self->sensorcfg,
|
|
sizeof (gxfp_sensor_cfg_t),
|
|
fp_init_config_cb);
|
|
break;
|
|
|
|
case FP_INIT_TEMPLATE_LIST:
|
|
/* List prints to check whether the template DB was corrupted.
|
|
* As of 2022-06-13 there is a known firmware issue that can cause the
|
|
* stored templates for Linux to be corrupted when the Windows storage
|
|
* area is initialized.
|
|
* In that case, we'll get a protocol failure trying to retrieve the
|
|
* list of prints.
|
|
*/
|
|
goodix_sensor_cmd (self, MOC_CMD0_GETFINGERLIST, MOC_CMD1_DEFAULT,
|
|
FALSE,
|
|
(const guint8 *) &dummy,
|
|
1,
|
|
fp_init_cb_reset_or_complete);
|
|
break;
|
|
|
|
case FP_INIT_RESET_DEVICE:
|
|
fp_warn ("Resetting device storage, you will need to enroll all prints again!");
|
|
goodix_sensor_cmd (self, MOC_CMD0_DELETETEMPLATE, MOC_CMD1_DELETE_ALL,
|
|
FALSE,
|
|
NULL,
|
|
0,
|
|
fp_init_reset_device_cb);
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
static void
|
|
fp_init_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (dev);
|
|
|
|
if (error)
|
|
{
|
|
fpi_device_open_complete (dev, error);
|
|
return;
|
|
}
|
|
self->task_ssm = NULL;
|
|
fpi_device_open_complete (dev, NULL);
|
|
}
|
|
/******************************************************************************
|
|
*
|
|
* fp_template_delete Function
|
|
*
|
|
*****************************************************************************/
|
|
static gboolean
|
|
parse_print_data (GVariant *data,
|
|
guint8 *finger,
|
|
const guint8 **tid,
|
|
gsize *tid_len,
|
|
const guint8 **user_id,
|
|
gsize *user_id_len)
|
|
{
|
|
g_autoptr(GVariant) user_id_var = NULL;
|
|
g_autoptr(GVariant) tid_var = NULL;
|
|
|
|
g_return_val_if_fail (data != NULL, FALSE);
|
|
g_return_val_if_fail (finger != NULL, FALSE);
|
|
g_return_val_if_fail (tid != NULL, FALSE);
|
|
g_return_val_if_fail (tid_len != NULL, FALSE);
|
|
g_return_val_if_fail (user_id != NULL, FALSE);
|
|
g_return_val_if_fail (user_id_len != NULL, FALSE);
|
|
|
|
*tid = NULL;
|
|
*tid_len = 0;
|
|
*user_id = NULL;
|
|
*user_id_len = 0;
|
|
|
|
|
|
if (!g_variant_check_format_string (data, "(y@ay@ay)", FALSE))
|
|
return FALSE;
|
|
|
|
g_variant_get (data,
|
|
"(y@ay@ay)",
|
|
finger,
|
|
&tid_var,
|
|
&user_id_var);
|
|
|
|
*tid = g_variant_get_fixed_array (tid_var, tid_len, 1);
|
|
*user_id = g_variant_get_fixed_array (user_id_var, user_id_len, 1);
|
|
|
|
if (*user_id_len == 0 || *user_id_len > 100)
|
|
return FALSE;
|
|
|
|
if (*user_id_len <= 0 || *user_id[0] == ' ')
|
|
return FALSE;
|
|
|
|
if(*tid_len != TEMPLATE_ID_SIZE)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fp_template_delete_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
|
|
if (error)
|
|
{
|
|
fpi_device_delete_complete (device, error);
|
|
return;
|
|
}
|
|
if ((resp->result >= GX_FAILED) && (resp->result != GX_ERROR_FINGER_ID_NOEXIST))
|
|
{
|
|
fpi_device_delete_complete (FP_DEVICE (self),
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
|
|
"Failed delete enrolled users, result: 0x%x",
|
|
resp->result));
|
|
return;
|
|
}
|
|
|
|
fp_info ("Successfully deleted enrolled user");
|
|
fpi_device_delete_complete (device, NULL);
|
|
}
|
|
|
|
static void
|
|
fp_template_delete_all_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
|
|
if (error)
|
|
{
|
|
fpi_device_clear_storage_complete (device, error);
|
|
return;
|
|
}
|
|
if ((resp->result >= GX_FAILED) && (resp->result != GX_ERROR_FINGER_ID_NOEXIST))
|
|
{
|
|
fpi_device_clear_storage_complete (FP_DEVICE (self),
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
|
|
"Failed clear storage, result: 0x%x",
|
|
resp->result));
|
|
return;
|
|
}
|
|
|
|
fp_info ("Successfully cleared storage");
|
|
fpi_device_clear_storage_complete (device, NULL);
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* fp_template_list Function
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
fp_template_list_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
FpDevice *device = FP_DEVICE (self);
|
|
|
|
if (error)
|
|
{
|
|
fpi_device_list_complete (FP_DEVICE (self), NULL, error);
|
|
return;
|
|
}
|
|
if (resp->result != GX_SUCCESS)
|
|
{
|
|
fp_info ("Failed to query enrolled users: %d", resp->result);
|
|
fpi_device_list_complete (FP_DEVICE (self),
|
|
NULL,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
|
|
"Failed to query enrolled users, result: 0x%x",
|
|
resp->result));
|
|
return;
|
|
}
|
|
|
|
self->list_result = g_ptr_array_new_with_free_func (g_object_unref);
|
|
|
|
if(resp->finger_list_resp.finger_num == 0)
|
|
{
|
|
fp_info ("Database is empty");
|
|
fpi_device_list_complete (device,
|
|
g_steal_pointer (&self->list_result),
|
|
NULL);
|
|
return;
|
|
}
|
|
|
|
for (int n = 0; n < resp->finger_list_resp.finger_num; n++)
|
|
{
|
|
FpPrint *print;
|
|
|
|
print = fp_print_from_template (self, &resp->finger_list_resp.finger_list[n]);
|
|
|
|
g_ptr_array_add (self->list_result, g_object_ref_sink (print));
|
|
}
|
|
|
|
fp_info ("Query complete!");
|
|
fpi_device_list_complete (device,
|
|
g_steal_pointer (&self->list_result),
|
|
NULL);
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Interface Function
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
gx_fp_probe (FpDevice *device)
|
|
{
|
|
GUsbDevice *usb_dev;
|
|
GError *error = NULL;
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
g_autofree gchar *serial = NULL;
|
|
gint productid = 0;
|
|
|
|
/* 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_reset (usb_dev, &error))
|
|
goto err_close;
|
|
|
|
if (!g_usb_device_claim_interface (usb_dev, 0, 0, &error))
|
|
goto err_close;
|
|
|
|
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);
|
|
|
|
if (serial && !g_str_has_suffix (serial, "B0"))
|
|
fp_warn ("Device with serial %s not supported", serial);
|
|
if (error)
|
|
{
|
|
g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (device)),
|
|
0, 0, NULL);
|
|
goto err_close;
|
|
}
|
|
}
|
|
productid = g_usb_device_get_pid (usb_dev);
|
|
switch (productid)
|
|
{
|
|
case 0x6496:
|
|
case 0x60A2:
|
|
case 0x60A4:
|
|
case 0x6014:
|
|
case 0x6092:
|
|
case 0x6094:
|
|
case 0x609A:
|
|
case 0x609C:
|
|
case 0x60BC:
|
|
case 0x60C2:
|
|
case 0x6304:
|
|
case 0x631C:
|
|
case 0x633C:
|
|
case 0x634C:
|
|
case 0x6384:
|
|
case 0x639C:
|
|
case 0x63AC:
|
|
case 0x63BC:
|
|
case 0x63CC:
|
|
case 0x650A:
|
|
case 0x650C:
|
|
case 0x6582:
|
|
case 0x6A94:
|
|
case 0x659A:
|
|
self->max_enroll_stage = 12;
|
|
break;
|
|
|
|
default:
|
|
self->max_enroll_stage = DEFAULT_ENROLL_SAMPLES;
|
|
break;
|
|
}
|
|
|
|
fpi_device_set_nr_enroll_stages (device, self->max_enroll_stage);
|
|
|
|
g_usb_device_close (usb_dev, NULL);
|
|
fpi_device_probe_complete (device, serial, NULL, error);
|
|
return;
|
|
err_close:
|
|
|
|
g_usb_device_close (usb_dev, NULL);
|
|
fpi_device_probe_complete (device, NULL, NULL, error);
|
|
|
|
}
|
|
|
|
static void
|
|
gx_fp_init (FpDevice *device)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
GError *error = NULL;
|
|
int ret = 0;
|
|
|
|
self->max_stored_prints = FP_MAX_FINGERNUM;
|
|
self->is_power_button_shield_on = false;
|
|
|
|
self->sensorcfg = g_new0 (gxfp_sensor_cfg_t, 1);
|
|
|
|
ret = gx_proto_init_sensor_config (self->sensorcfg);
|
|
if (ret != 0)
|
|
{
|
|
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "Init sensor failed");
|
|
fpi_device_open_complete (FP_DEVICE (self), error);
|
|
return;
|
|
}
|
|
self->sensorcfg->config[6] = self->max_enroll_stage;
|
|
|
|
if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error))
|
|
{
|
|
fpi_device_open_complete (FP_DEVICE (self), error);
|
|
return;
|
|
}
|
|
|
|
/* Claim usb interface */
|
|
if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error))
|
|
{
|
|
fpi_device_open_complete (FP_DEVICE (self), error);
|
|
return;
|
|
}
|
|
|
|
self->task_ssm = fpi_ssm_new (device, fp_init_sm_run_state,
|
|
FP_INIT_NUM_STATES);
|
|
|
|
fpi_ssm_start (self->task_ssm, fp_init_ssm_done);
|
|
|
|
}
|
|
|
|
static void
|
|
gx_fp_release_interface (FpiDeviceGoodixMoc *self,
|
|
GError *error)
|
|
{
|
|
g_autoptr(GError) release_error = NULL;
|
|
|
|
g_clear_pointer (&self->sensorcfg, g_free);
|
|
|
|
/* Release usb interface */
|
|
g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (self)),
|
|
0, 0, &release_error);
|
|
/* Retain passed error if set, otherwise propagate error from release. */
|
|
if (error == NULL)
|
|
error = g_steal_pointer (&release_error);
|
|
|
|
/* Notify close complete */
|
|
fpi_device_close_complete (FP_DEVICE (self), error);
|
|
|
|
}
|
|
|
|
static void
|
|
gx_fp_exit_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
|
|
|
|
if (resp->result >= GX_FAILED)
|
|
fp_dbg ("Setting power button shield failed, result: 0x%x", resp->result);
|
|
self->is_power_button_shield_on = false;
|
|
gx_fp_release_interface (self, error);
|
|
}
|
|
|
|
static void
|
|
gx_fp_exit (FpDevice *device)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
|
|
if (self->is_power_button_shield_on)
|
|
{
|
|
goodix_sensor_cmd (self,
|
|
MOC_CMD0_PWR_BTN_SHIELD,
|
|
MOC_CMD1_PWR_BTN_SHIELD_OFF,
|
|
false,
|
|
NULL,
|
|
0,
|
|
gx_fp_exit_cb);
|
|
}
|
|
else
|
|
{
|
|
gx_fp_release_interface (self, NULL);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
gx_fp_verify_identify (FpDevice *device)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
|
|
self->task_ssm = fpi_ssm_new_full (device, fp_verify_sm_run_state,
|
|
FP_VERIFY_NUM_STATES,
|
|
FP_VERIFY_PWR_BTN_SHIELD_OFF,
|
|
"verify");
|
|
|
|
fpi_ssm_start (self->task_ssm, fp_verify_ssm_done);
|
|
|
|
}
|
|
|
|
static void
|
|
gx_fp_enroll (FpDevice *device)
|
|
{
|
|
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
|
|
self->enroll_stage = 0;
|
|
|
|
self->task_ssm = fpi_ssm_new_full (device, fp_enroll_sm_run_state,
|
|
FP_ENROLL_NUM_STATES,
|
|
FP_ENROLL_PWR_BTN_SHIELD_OFF,
|
|
"enroll");
|
|
|
|
fpi_ssm_start (self->task_ssm, fp_enroll_ssm_done);
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
gx_fp_template_list (FpDevice *device)
|
|
{
|
|
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
guint8 dummy[1] = { 0 };
|
|
|
|
G_DEBUG_HERE ();
|
|
|
|
goodix_sensor_cmd (self, MOC_CMD0_GETFINGERLIST, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) &dummy,
|
|
1,
|
|
fp_template_list_cb);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
gx_fp_template_delete (FpDevice *device)
|
|
{
|
|
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
FpPrint *print = NULL;
|
|
|
|
g_autoptr(GVariant) data = NULL;
|
|
guint8 finger;
|
|
const guint8 *user_id;
|
|
gsize user_id_len = 0;
|
|
const guint8 *tid;
|
|
gsize tid_len = 0;
|
|
gsize payload_len = 0;
|
|
g_autofree guint8 *payload = NULL;
|
|
|
|
fpi_device_get_delete_data (device, &print);
|
|
|
|
g_object_get (print, "fpi-data", &data, NULL);
|
|
|
|
if (!parse_print_data (data, &finger, &tid, &tid_len, &user_id, &user_id_len))
|
|
{
|
|
fpi_device_delete_complete (device,
|
|
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
|
|
return;
|
|
}
|
|
if (!encode_finger_id (tid, tid_len, user_id, user_id_len, &payload, (guint16 *) &payload_len))
|
|
{
|
|
fpi_device_delete_complete (device,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"encode_finger_id failed"));
|
|
return;
|
|
}
|
|
|
|
goodix_sensor_cmd (self, MOC_CMD0_DELETETEMPLATE, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) payload,
|
|
payload_len,
|
|
fp_template_delete_cb);
|
|
|
|
}
|
|
|
|
static void
|
|
gx_fp_template_delete_all (FpDevice *device)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
|
|
goodix_sensor_cmd (self, MOC_CMD0_DELETETEMPLATE, MOC_CMD1_DELETE_ALL,
|
|
false,
|
|
NULL,
|
|
0,
|
|
fp_template_delete_all_cb);
|
|
|
|
}
|
|
|
|
static void
|
|
fpi_device_goodixmoc_init (FpiDeviceGoodixMoc *self)
|
|
{
|
|
|
|
}
|
|
|
|
static const FpIdEntry id_table[] = {
|
|
{ .vid = 0x27c6, .pid = 0x5840, },
|
|
{ .vid = 0x27c6, .pid = 0x6014, },
|
|
{ .vid = 0x27c6, .pid = 0x6092, },
|
|
{ .vid = 0x27c6, .pid = 0x6094, },
|
|
{ .vid = 0x27c6, .pid = 0x609A, },
|
|
{ .vid = 0x27c6, .pid = 0x609C, },
|
|
{ .vid = 0x27c6, .pid = 0x60A2, },
|
|
{ .vid = 0x27c6, .pid = 0x60A4, },
|
|
{ .vid = 0x27c6, .pid = 0x60BC, },
|
|
{ .vid = 0x27c6, .pid = 0x60C2, },
|
|
{ .vid = 0x27c6, .pid = 0x6304, },
|
|
{ .vid = 0x27c6, .pid = 0x631C, },
|
|
{ .vid = 0x27c6, .pid = 0x633C, },
|
|
{ .vid = 0x27c6, .pid = 0x634C, },
|
|
{ .vid = 0x27c6, .pid = 0x6384, },
|
|
{ .vid = 0x27c6, .pid = 0x639C, },
|
|
{ .vid = 0x27c6, .pid = 0x63AC, },
|
|
{ .vid = 0x27c6, .pid = 0x63BC, },
|
|
{ .vid = 0x27c6, .pid = 0x63CC, },
|
|
{ .vid = 0x27c6, .pid = 0x6496, },
|
|
{ .vid = 0x27c6, .pid = 0x650A, },
|
|
{ .vid = 0x27c6, .pid = 0x650C, },
|
|
{ .vid = 0x27c6, .pid = 0x6582, },
|
|
{ .vid = 0x27c6, .pid = 0x6584, },
|
|
{ .vid = 0x27c6, .pid = 0x658C, },
|
|
{ .vid = 0x27c6, .pid = 0x6592, },
|
|
{ .vid = 0x27c6, .pid = 0x6594, },
|
|
{ .vid = 0x27c6, .pid = 0x659A, },
|
|
{ .vid = 0x27c6, .pid = 0x659C, },
|
|
{ .vid = 0x27c6, .pid = 0x6A94, },
|
|
{ .vid = 0x27c6, .pid = 0x6512, },
|
|
{ .vid = 0x27c6, .pid = 0x689A, },
|
|
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
|
|
};
|
|
|
|
|
|
static void
|
|
fpi_device_goodixmoc_class_init (FpiDeviceGoodixMocClass *klass)
|
|
{
|
|
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
|
|
|
dev_class->id = "goodixmoc";
|
|
dev_class->full_name = "Goodix MOC Fingerprint Sensor";
|
|
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 = DEFAULT_ENROLL_SAMPLES;
|
|
dev_class->temp_hot_seconds = -1;
|
|
|
|
dev_class->open = gx_fp_init;
|
|
dev_class->close = gx_fp_exit;
|
|
dev_class->probe = gx_fp_probe;
|
|
dev_class->enroll = gx_fp_enroll;
|
|
dev_class->delete = gx_fp_template_delete;
|
|
dev_class->clear_storage = gx_fp_template_delete_all;
|
|
dev_class->list = gx_fp_template_list;
|
|
dev_class->verify = gx_fp_verify_identify;
|
|
dev_class->identify = gx_fp_verify_identify;
|
|
|
|
fpi_device_class_auto_initialize_features (dev_class);
|
|
dev_class->features |= FP_DEVICE_FEATURE_DUPLICATES_CHECK;
|
|
}
|