9e164485f0
Add PID 6594 used by LENOVO
1582 lines
47 KiB
C
1582 lines
47 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 "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;
|
|
GCancellable *cancellable;
|
|
GPtrArray *list_result;
|
|
guint8 template_id[TEMPLATE_ID_SIZE];
|
|
gboolean is_enroll_identify;
|
|
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);
|
|
/******************************************************************************
|
|
*
|
|
* 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);
|
|
CommandData *data = user_data;
|
|
int ret = -1, ssm_state = 0;
|
|
gxfp_cmd_response_t cmd_reponse = {0, };
|
|
pack_header header;
|
|
guint32 crc32_calc = 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 received"));
|
|
return;
|
|
}
|
|
|
|
gx_proto_crc32_calc (transfer->buffer, PACKAGE_HEADER_SIZE + header.len, (uint8_t *) &crc32_calc);
|
|
if(crc32_calc != GUINT32_FROM_LE (*(uint32_t *) (transfer->buffer + PACKAGE_HEADER_SIZE + header.len)))
|
|
{
|
|
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 ? self->cancellable : 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)
|
|
{
|
|
g_autoptr(GPtrArray) templates = NULL;
|
|
FpDevice *device = FP_DEVICE (self);
|
|
FpPrint *print = NULL;
|
|
gint cnt = 0;
|
|
gboolean find = false;
|
|
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
if (resp->verify.match)
|
|
{
|
|
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
|
|
{
|
|
templates = g_ptr_array_sized_new (1);
|
|
fpi_device_get_verify_data (device, &print);
|
|
g_ptr_array_add (templates, print);
|
|
}
|
|
else
|
|
{
|
|
fpi_device_get_identify_data (device, &templates);
|
|
g_ptr_array_ref (templates);
|
|
}
|
|
for (cnt = 0; cnt < templates->len; cnt++)
|
|
{
|
|
g_autoptr(GVariant) data = NULL;
|
|
guint8 finger;
|
|
const guint8 *user_id;
|
|
gsize user_id_len = 0;
|
|
const guint8 *tid;
|
|
gsize tid_len = 0;
|
|
print = g_ptr_array_index (templates, cnt);
|
|
g_object_get (print, "fpi-data", &data, NULL);
|
|
if (!parse_print_data (data, &finger, &tid, &tid_len, &user_id, &user_id_len))
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID,
|
|
"Parse print error"));
|
|
return;
|
|
}
|
|
if (memcmp (&resp->verify.template.tid, tid, TEMPLATE_ID_SIZE) == 0)
|
|
{
|
|
find = true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (find)
|
|
{
|
|
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
|
|
fpi_device_verify_report (device, FPI_MATCH_SUCCESS, NULL, error);
|
|
else
|
|
fpi_device_identify_report (device, print, print, error);
|
|
}
|
|
}
|
|
|
|
if (!find)
|
|
{
|
|
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
|
|
fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, error);
|
|
else
|
|
fpi_device_identify_report (device, NULL, NULL, 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_jump_to_state (self->task_ssm, FP_ENROLL_CAPTURE);
|
|
}
|
|
|
|
static void
|
|
fp_enroll_identify_cb (FpiDeviceGoodixMoc *self,
|
|
gxfp_cmd_response_t *resp,
|
|
GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm, error);
|
|
return;
|
|
}
|
|
if (resp->verify.match)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_DUPLICATE,
|
|
"Finger is too similar to another, try use a different finger"));
|
|
// maybe need fpi_device_enroll_report_message ...
|
|
return;
|
|
}
|
|
fpi_ssm_next_state (self->task_ssm);
|
|
|
|
}
|
|
|
|
static void
|
|
fp_enroll_init_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_init.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_warn ("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_warn ("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;
|
|
}
|
|
if (self->is_enroll_identify)
|
|
{
|
|
self->is_enroll_identify = false;
|
|
fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_IDENTIFY);
|
|
}
|
|
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)
|
|
{
|
|
fpi_ssm_mark_failed (self->task_ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_DUPLICATE,
|
|
"Finger has already enrolled"));
|
|
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_ENUM:
|
|
{
|
|
goodix_sensor_cmd (self, MOC_CMD0_GETFINGERLIST, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) &dummy,
|
|
1,
|
|
fp_enroll_enum_cb);
|
|
}
|
|
break;
|
|
|
|
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_IDENTIFY:
|
|
{
|
|
dummy[0] = 0x01;
|
|
dummy[1] = self->sensorcfg->config[10];
|
|
dummy[2] = self->sensorcfg->config[11];
|
|
goodix_sensor_cmd (self, MOC_CMD0_IDENTIFY, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) &self->template_id,
|
|
TEMPLATE_ID_SIZE,
|
|
fp_enroll_identify_cb);
|
|
}
|
|
break;
|
|
|
|
case FP_ENROLL_CREATE:
|
|
{
|
|
goodix_sensor_cmd (self, MOC_CMD0_ENROLL_INIT, MOC_CMD1_DEFAULT,
|
|
false,
|
|
(const guint8 *) &dummy,
|
|
1,
|
|
fp_enroll_init_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_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;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
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);
|
|
}
|
|
/******************************************************************************
|
|
*
|
|
* 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++)
|
|
{
|
|
GVariant *data = NULL;
|
|
GVariant *tid = NULL;
|
|
GVariant *uid = NULL;
|
|
FpPrint *print;
|
|
gchar *userid;
|
|
|
|
userid = (gchar *) resp->finger_list_resp.finger_list[n].payload.data;
|
|
|
|
print = fp_print_new (FP_DEVICE (self));
|
|
|
|
tid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
|
|
resp->finger_list_resp.finger_list[n].tid,
|
|
TEMPLATE_ID_SIZE,
|
|
1);
|
|
|
|
uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
|
|
resp->finger_list_resp.finger_list[n].payload.data,
|
|
resp->finger_list_resp.finger_list[n].payload.size,
|
|
1);
|
|
|
|
data = g_variant_new ("(y@ay@ay)",
|
|
resp->finger_list_resp.finger_list[n].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);
|
|
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:
|
|
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->cancellable = g_cancellable_new ();
|
|
|
|
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_object (&self->cancellable);
|
|
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 (device, fp_verify_sm_run_state,
|
|
FP_VERIFY_NUM_STATES);
|
|
|
|
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->is_enroll_identify = true;
|
|
|
|
self->task_ssm = fpi_ssm_new (device, fp_enroll_sm_run_state,
|
|
FP_ENROLL_NUM_STATES);
|
|
|
|
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
|
|
fpi_device_goodixmoc_init (FpiDeviceGoodixMoc *self)
|
|
{
|
|
|
|
}
|
|
|
|
static void
|
|
gx_fp_cancel (FpDevice *device)
|
|
{
|
|
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
|
|
|
/* 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->cancellable);
|
|
g_clear_object (&self->cancellable);
|
|
self->cancellable = g_cancellable_new ();
|
|
|
|
}
|
|
|
|
static const FpIdEntry id_table[] = {
|
|
{ .vid = 0x27c6, .pid = 0x5840, },
|
|
{ .vid = 0x27c6, .pid = 0x6496, },
|
|
{ .vid = 0x27c6, .pid = 0x60A2, },
|
|
{ .vid = 0x27c6, .pid = 0x63AC, },
|
|
{ .vid = 0x27c6, .pid = 0x639C, },
|
|
{ .vid = 0x27c6, .pid = 0x6594, },
|
|
{ .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->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->list = gx_fp_template_list;
|
|
dev_class->cancel = gx_fp_cancel;
|
|
dev_class->verify = gx_fp_verify_identify;
|
|
dev_class->identify = gx_fp_verify_identify;
|
|
}
|