mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2026-06-11 10:34:18 +00:00
Merge libfprint/libfprint master branch into tod
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
|
||||
#define AES1660_FRAME_SIZE 0x244
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
/* First init sequence, 0x07 cmd returns following before INIT1:
|
||||
* { 0x07, 0x05, 0x00, 0x8f, 0x16, 0x25, 0x01, 0x00 }
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#define AES2660_FRAME_SIZE 0x354
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
/* First init sequence, 0x07 cmd returns following before INIT1:
|
||||
* { 0x07, 0x05, 0x00, 0x91, 0x26, 0x21, 0x00, 0x00 }
|
||||
*/
|
||||
|
||||
@@ -58,6 +58,7 @@ struct _FpiDeviceGoodixMoc
|
||||
GPtrArray *list_result;
|
||||
guint8 template_id[TEMPLATE_ID_SIZE];
|
||||
gboolean is_enroll_identify;
|
||||
gboolean is_power_button_shield_on;
|
||||
|
||||
};
|
||||
|
||||
@@ -127,7 +128,7 @@ fp_cmd_receive_cb (FpiUsbTransfer *transfer,
|
||||
}
|
||||
|
||||
gx_proto_crc32_calc (transfer->buffer, PACKAGE_HEADER_SIZE + header.len, (uint8_t *) &crc32_calc);
|
||||
if(crc32_calc != *(uint32_t *) (transfer->buffer + PACKAGE_HEADER_SIZE + header.len))
|
||||
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,
|
||||
@@ -309,6 +310,35 @@ goodix_sensor_cmd (FpiDeviceGoodixMoc *self,
|
||||
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* 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
|
||||
@@ -419,7 +449,7 @@ fp_verify_cb (FpiDeviceGoodixMoc *self,
|
||||
fpi_device_identify_report (device, NULL, NULL, error);
|
||||
}
|
||||
|
||||
fpi_ssm_mark_completed (self->task_ssm);
|
||||
fpi_ssm_next_state (self->task_ssm);
|
||||
|
||||
}
|
||||
|
||||
@@ -436,6 +466,14 @@ fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device)
|
||||
|
||||
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,
|
||||
@@ -454,6 +492,14 @@ fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device)
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -468,9 +514,9 @@ fp_verify_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
|
||||
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, error);
|
||||
fpi_device_verify_report (dev, FPI_MATCH_ERROR, NULL, g_steal_pointer (&error));
|
||||
else
|
||||
fpi_device_identify_report (dev, NULL, NULL, error);
|
||||
fpi_device_identify_report (dev, NULL, NULL, g_steal_pointer (&error));
|
||||
}
|
||||
|
||||
if (fpi_device_get_current_action (dev) == FPI_DEVICE_ACTION_VERIFY)
|
||||
@@ -812,6 +858,16 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device)
|
||||
}
|
||||
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;
|
||||
@@ -926,9 +982,17 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device)
|
||||
|
||||
}
|
||||
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
|
||||
@@ -1282,6 +1346,7 @@ gx_fp_init (FpDevice *device)
|
||||
int ret = 0;
|
||||
|
||||
self->max_stored_prints = FP_MAX_FINGERNUM;
|
||||
self->is_power_button_shield_on = false;
|
||||
|
||||
self->cancellable = g_cancellable_new ();
|
||||
|
||||
@@ -1317,20 +1382,59 @@ gx_fp_init (FpDevice *device)
|
||||
}
|
||||
|
||||
static void
|
||||
gx_fp_exit (FpDevice *device)
|
||||
gx_fp_release_interface (FpiDeviceGoodixMoc *self,
|
||||
GError *error)
|
||||
{
|
||||
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
|
||||
GError *error = NULL;
|
||||
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 (device)),
|
||||
0, 0, &error);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1432,7 +1536,6 @@ 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
|
||||
@@ -1450,6 +1553,7 @@ static const FpIdEntry id_table[] = {
|
||||
{ .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 */
|
||||
};
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ typedef enum {
|
||||
|
||||
|
||||
typedef enum {
|
||||
FP_ENROLL_ENUM = 0,
|
||||
FP_ENROLL_PWR_BTN_SHIELD_ON = 0,
|
||||
FP_ENROLL_ENUM,
|
||||
FP_ENROLL_IDENTIFY,
|
||||
FP_ENROLL_CREATE,
|
||||
FP_ENROLL_CAPTURE,
|
||||
@@ -48,11 +49,14 @@ typedef enum {
|
||||
FP_ENROLL_WAIT_FINGER_UP,
|
||||
FP_ENROLL_CHECK_DUPLICATE,
|
||||
FP_ENROLL_COMMIT,
|
||||
FP_ENROLL_PWR_BTN_SHIELD_OFF,
|
||||
FP_ENROLL_NUM_STATES,
|
||||
} FpEnrollState;
|
||||
|
||||
typedef enum {
|
||||
FP_VERIFY_CAPTURE = 0,
|
||||
FP_VERIFY_PWR_BTN_SHIELD_ON = 0,
|
||||
FP_VERIFY_CAPTURE,
|
||||
FP_VERIFY_IDENTIFY,
|
||||
FP_VERIFY_PWR_BTN_SHIELD_OFF,
|
||||
FP_VERIFY_NUM_STATES,
|
||||
} FpVerifyState;
|
||||
|
||||
@@ -141,8 +141,11 @@ crc32_update (gf_crc32_context *ctx, const uint8_t *message, uint32_t n_bytes)
|
||||
static void
|
||||
crc32_final (gf_crc32_context *ctx, uint8_t *md)
|
||||
{
|
||||
uint32_t crc = 0;
|
||||
|
||||
ctx->crc = (REFLECT_REMAINDER (ctx->crc) ^ FINAL_XOR_VALUE);
|
||||
memcpy (md, &ctx->crc, 4);
|
||||
crc = GUINT32_TO_LE (ctx->crc);
|
||||
memcpy (md, &crc, 4);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
@@ -184,7 +187,7 @@ init_pack_header (
|
||||
pheader->cmd1 = LOBYTE (cmd);
|
||||
pheader->packagenum = packagenum;
|
||||
pheader->reserved = dump_seq++;
|
||||
pheader->len = len + PACKAGE_CRC_SIZE;
|
||||
pheader->len = GUINT16_TO_LE (len + PACKAGE_CRC_SIZE);
|
||||
pheader->crc8 = gx_proto_crc8_calc ((uint8_t *) pheader, 6);
|
||||
pheader->rev_crc8 = ~pheader->crc8;
|
||||
}
|
||||
@@ -224,14 +227,14 @@ gx_proto_parse_header (
|
||||
{
|
||||
if (!buffer || !pheader)
|
||||
return -1;
|
||||
if (buffer_len < PACKAGE_HEADER_SIZE)
|
||||
if (buffer_len < PACKAGE_HEADER_SIZE + PACKAGE_CRC_SIZE)
|
||||
return -1;
|
||||
|
||||
memcpy (pheader, buffer, sizeof (pack_header));
|
||||
|
||||
pheader->len = GUINT16_FROM_LE ( *(uint16_t *) (buffer + 4));
|
||||
pheader->len = GUINT16_FROM_LE (pheader->len);
|
||||
if (buffer_len < pheader->len + PACKAGE_HEADER_SIZE)
|
||||
return -1;
|
||||
pheader->len -= PACKAGE_CRC_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -248,7 +251,7 @@ gx_proto_parse_fingerid (
|
||||
if (!template || !fid_buffer)
|
||||
return -1;
|
||||
|
||||
if (fid_buffer_size < 70)
|
||||
if (fid_buffer_size < G_STRUCT_OFFSET (template_format_t, payload) + sizeof (uint32_t))
|
||||
return -1;
|
||||
|
||||
buffer = fid_buffer;
|
||||
@@ -256,28 +259,30 @@ gx_proto_parse_fingerid (
|
||||
|
||||
if (buffer[Offset++] != 67)
|
||||
return -1;
|
||||
fid_buffer_size--;
|
||||
|
||||
template->type = buffer[Offset++];
|
||||
fid_buffer_size--;
|
||||
template->finger_index = buffer[Offset++];
|
||||
fid_buffer_size--;
|
||||
Offset++;
|
||||
|
||||
memcpy (template->accountid, &buffer[Offset], 32);
|
||||
Offset += 32;
|
||||
|
||||
memcpy (template->tid, &buffer[Offset], 32);
|
||||
Offset += 32; // Offset == 68
|
||||
|
||||
memcpy (template->accountid, &buffer[Offset], sizeof (template->accountid));
|
||||
Offset += sizeof (template->accountid);
|
||||
memcpy (template->tid, &buffer[Offset], sizeof (template->tid));
|
||||
Offset += sizeof (template->tid); // Offset == 68
|
||||
template->payload.size = buffer[Offset++];
|
||||
memset (template->payload.data, 0, 56);
|
||||
if (template->payload.size > sizeof (template->payload.data))
|
||||
return -1;
|
||||
memset (template->payload.data, 0, template->payload.size);
|
||||
memcpy (template->payload.data, &buffer[Offset], template->payload.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint32_t buffer_len, pgxfp_cmd_response_t presp)
|
||||
gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint16_t buffer_len, pgxfp_cmd_response_t presp)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
uint16_t offset = 0;
|
||||
uint8_t *fingerlist = NULL;
|
||||
|
||||
if (!buffer || !presp)
|
||||
@@ -289,6 +294,8 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint32_t buffer_len, pgxfp_c
|
||||
{
|
||||
case RESPONSE_PACKAGE_CMD:
|
||||
{
|
||||
if (buffer_len < sizeof (gxfp_parse_msg_t) + 1)
|
||||
return -1;
|
||||
presp->parse_msg.ack_cmd = buffer[1];
|
||||
}
|
||||
break;
|
||||
@@ -296,32 +303,56 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint32_t buffer_len, pgxfp_c
|
||||
case MOC_CMD0_UPDATE_CONFIG:
|
||||
{
|
||||
presp->finger_config.status = buffer[0];
|
||||
presp->finger_config.max_stored_prints = buffer[2];
|
||||
if (buffer_len >= 3)
|
||||
presp->finger_config.max_stored_prints = buffer[2];
|
||||
else
|
||||
/* to compatiable old version firmware */
|
||||
presp->finger_config.max_stored_prints = FP_MAX_FINGERNUM;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case MOC_CMD0_COMMITENROLLMENT:
|
||||
case MOC_CMD0_DELETETEMPLATE:
|
||||
/* just check result */
|
||||
break;
|
||||
|
||||
case MOC_CMD0_PWR_BTN_SHIELD:
|
||||
presp->power_button_shield_resp.resp_cmd1 = LOBYTE (cmd);
|
||||
if (buffer_len >= 2)
|
||||
{
|
||||
uint8_t support_pwr_shield = buffer[1];
|
||||
if (support_pwr_shield == 0xFF)
|
||||
g_debug ("Power button shield feature not supported!\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case MOC_CMD0_GET_VERSION:
|
||||
if (buffer_len < sizeof (gxfp_version_info_t) + 1)
|
||||
return -1;
|
||||
memcpy (&presp->version_info, buffer + 1, sizeof (gxfp_version_info_t));
|
||||
break;
|
||||
|
||||
case MOC_CMD0_CAPTURE_DATA:
|
||||
if (LOBYTE (cmd) == MOC_CMD1_DEFAULT)
|
||||
{
|
||||
if (buffer_len < sizeof (gxfp_capturedata_t) + 1)
|
||||
return -1;
|
||||
presp->capture_data_resp.img_quality = buffer[1];
|
||||
presp->capture_data_resp.img_coverage = buffer[2];
|
||||
}
|
||||
break;
|
||||
|
||||
case MOC_CMD0_ENROLL_INIT:
|
||||
if (buffer_len < sizeof (gxfp_enroll_init_t) + 1)
|
||||
return -1;
|
||||
if (presp->result == GX_SUCCESS)
|
||||
memcpy (&presp->enroll_init.tid, &buffer[1], TEMPLATE_ID_SIZE);
|
||||
break;
|
||||
|
||||
case MOC_CMD0_ENROLL:
|
||||
if (buffer_len < sizeof (gxfp_enroll_update_t))
|
||||
return -1;
|
||||
presp->enroll_update.rollback = (buffer[0] < 0x80) ? false : true;
|
||||
presp->enroll_update.img_overlay = buffer[1];
|
||||
presp->enroll_update.img_preoverlay = buffer[2];
|
||||
@@ -331,7 +362,11 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint32_t buffer_len, pgxfp_c
|
||||
presp->check_duplicate_resp.duplicate = (presp->result == 0) ? false : true;
|
||||
if (presp->check_duplicate_resp.duplicate)
|
||||
{
|
||||
uint16_t tid_size = GUINT16_FROM_LE (*(buffer + 1));
|
||||
if (buffer_len < 3)
|
||||
return -1;
|
||||
uint16_t tid_size = GUINT16_FROM_LE (*(uint16_t *) (buffer + 1));
|
||||
if ((buffer_len < tid_size + 3) || (buffer_len > sizeof (template_format_t)) + 3)
|
||||
return -1;
|
||||
memcpy (&presp->check_duplicate_resp.template, buffer + 3, tid_size);
|
||||
}
|
||||
break;
|
||||
@@ -339,18 +374,16 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint32_t buffer_len, pgxfp_c
|
||||
case MOC_CMD0_GETFINGERLIST:
|
||||
if (presp->result != GX_SUCCESS)
|
||||
break;
|
||||
if (buffer_len < 2)
|
||||
return -1;
|
||||
presp->finger_list_resp.finger_num = buffer[1];
|
||||
if (presp->finger_list_resp.finger_num > FP_MAX_FINGERNUM)
|
||||
{
|
||||
presp->finger_list_resp.finger_num = 0;
|
||||
presp->result = GX_ERROR_NO_AVAILABLE_SPACE;
|
||||
break;
|
||||
}
|
||||
fingerlist = buffer + 2;
|
||||
for(uint8_t num = 0; num < presp->finger_list_resp.finger_num; num++)
|
||||
{
|
||||
uint16_t fingerid_length = GUINT16_FROM_LE (*(fingerlist + offset));
|
||||
uint16_t fingerid_length = GUINT16_FROM_LE (*(uint16_t *) (fingerlist + offset));
|
||||
offset += 2;
|
||||
if (buffer_len < fingerid_length + offset + 2)
|
||||
return -1;
|
||||
if (gx_proto_parse_fingerid (fingerlist + offset,
|
||||
fingerid_length,
|
||||
&presp->finger_list_resp.finger_list[num]) != 0)
|
||||
@@ -372,14 +405,16 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint32_t buffer_len, pgxfp_c
|
||||
presp->verify.match = (buffer[0] == 0) ? true : false;
|
||||
if (presp->verify.match)
|
||||
{
|
||||
if (buffer_len < sizeof (template_format_t) + 10)
|
||||
return -1;
|
||||
offset += 1;
|
||||
presp->verify.rejectdetail = GUINT16_FROM_LE (*(buffer + offset));
|
||||
presp->verify.rejectdetail = GUINT16_FROM_LE (*(uint16_t *) (buffer + offset));
|
||||
offset += 2;
|
||||
score = GUINT32_FROM_LE (*(buffer + offset));
|
||||
score = GUINT32_FROM_LE (*(uint32_t *) (buffer + offset));
|
||||
offset += 4;
|
||||
study = GUINT16_FROM_LE (*(buffer + offset));
|
||||
study = buffer[offset];
|
||||
offset += 1;
|
||||
fingerid_size = GUINT16_FROM_LE (*(buffer + offset));
|
||||
fingerid_size = GUINT16_FROM_LE (*(uint16_t *) (buffer + offset));
|
||||
offset += 2;
|
||||
if (gx_proto_parse_fingerid (buffer + offset, fingerid_size, &presp->verify.template) != 0)
|
||||
{
|
||||
|
||||
@@ -75,6 +75,11 @@
|
||||
#define MOC_CMD1_GET_FINGER_MODE 0x00
|
||||
#define MOC_CMD1_SET_FINGER_DOWN 0x01
|
||||
#define MOC_CMD1_SET_FINGER_UP 0x02
|
||||
|
||||
#define MOC_CMD0_PWR_BTN_SHIELD 0xE0
|
||||
#define MOC_CMD1_PWR_BTN_SHIELD_OFF 0x00
|
||||
#define MOC_CMD1_PWR_BTN_SHIELD_ON 0x01
|
||||
|
||||
/* */
|
||||
|
||||
typedef struct _gxfp_version_info
|
||||
@@ -89,7 +94,7 @@ typedef struct _gxfp_version_info
|
||||
uint8_t interface[GX_VERSION_LEN];
|
||||
uint8_t protocol[GX_VERSION_LEN];
|
||||
uint8_t flashVersion[GX_VERSION_LEN];
|
||||
uint8_t reserved[62];
|
||||
uint8_t reserved[38];
|
||||
} gxfp_version_info_t, *pgxfp_version_info_t;
|
||||
|
||||
|
||||
@@ -173,6 +178,11 @@ typedef struct _fp_finger_config
|
||||
uint8_t max_stored_prints;
|
||||
} fp_finger_config_t, *pfp_finger_config_t;
|
||||
|
||||
typedef struct _fp_pwr_btn_shield
|
||||
{
|
||||
uint8_t resp_cmd1;
|
||||
} fp_pwr_btn_shield_t, *pfp_pwr_btn_shield_t;
|
||||
|
||||
typedef struct _fp_cmd_response
|
||||
{
|
||||
uint8_t result;
|
||||
@@ -189,6 +199,7 @@ typedef struct _fp_cmd_response
|
||||
gxfp_version_info_t version_info;
|
||||
fp_finger_status_t finger_status;
|
||||
fp_finger_config_t finger_config;
|
||||
fp_pwr_btn_shield_t power_button_shield_resp;
|
||||
};
|
||||
} gxfp_cmd_response_t, *pgxfp_cmd_response_t;
|
||||
|
||||
@@ -225,7 +236,7 @@ int gx_proto_parse_header (uint8_t *buffer,
|
||||
|
||||
int gx_proto_parse_body (uint16_t cmd,
|
||||
uint8_t *buffer,
|
||||
uint32_t buffer_len,
|
||||
uint16_t buffer_len,
|
||||
pgxfp_cmd_response_t presponse);
|
||||
|
||||
int gx_proto_init_sensor_config (pgxfp_sensor_cfg_t pconfig);
|
||||
|
||||
@@ -348,7 +348,7 @@ synaptics_sensor_cmd (FpiDeviceSynaptics *self,
|
||||
* may only be a cancellation currently). */
|
||||
if (seq_num <= 0)
|
||||
{
|
||||
self->last_seq_num = MAX (1, self->last_seq_num + 1);
|
||||
self->last_seq_num = MAX (1, (self->last_seq_num + 1) & 0xff);
|
||||
real_seq_num = self->last_seq_num;
|
||||
if (seq_num == 0)
|
||||
self->cmd_seq_num = self->last_seq_num;
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define UPEKTC_CMD_LEN 0x40
|
||||
|
||||
@@ -375,6 +375,10 @@ stop_irq_handler (FpImageDevice *dev, irqs_stopped_cb_fn cb)
|
||||
g_cancellable_cancel (self->irq_cancellable);
|
||||
self->irqs_stopped_cb = cb;
|
||||
}
|
||||
else
|
||||
{
|
||||
cb (dev);
|
||||
}
|
||||
}
|
||||
|
||||
/***** STATE CHANGING *****/
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
/* There are many similar blocks in the data below, also the data are
|
||||
* self-similar (looks like some config blocks? pokes like in vfs101?) */
|
||||
|
||||
@@ -1621,56 +1623,56 @@ static const unsigned char vfs301_24[] = { /* 119 B */
|
||||
#define vfs301_02D0_ALIGNED_BLOB \
|
||||
PACKET ("0200", "8005", \
|
||||
"FF830720" "5F820720" "FF830720" \
|
||||
"5F820720" "FF830720" "5F820720" "FF830720" \
|
||||
"5F820720" "FF830720" "5F820720" "FF8B0720" \
|
||||
"608A0720" "FF930720" "61920720" "FF9B0720" \
|
||||
"629A0720" "FFA30720" "63A20720" "FFAB0720" \
|
||||
"64AA0720" "FFB30720" "65B20720" "FFBB0720" \
|
||||
"66BA0720" "FFC30720" "67C20720" "FFCB0720" \
|
||||
"68CA0720" "FFD30720" "69D20720" "FFDB0720" \
|
||||
"6ADA0720" "FFE30720" "6BE20720" "FFEB0720" \
|
||||
"6CEA0720" "FFF30720" "6DF20720" "FFFB0720" \
|
||||
"6EFA0720" "FF850720" "6F840720" "FF8D0720" \
|
||||
"708C0720" "FF950720" "71940720" "FF9D0720" \
|
||||
"729C0720" "FFA50720" "73A40720" "FFAD0720" \
|
||||
"74AC0720" "FFB50720" "75B40720" "FFBD0720" \
|
||||
"76BC0720" "FFC50720" "77C40720" "FFCD0720" \
|
||||
"78CC0720" "FFD50720" "79D40720" "FFDD0720" \
|
||||
"7ADC0720" "FFE50720" "7BE40720" "FFED0720" \
|
||||
"7CEC0720" "FFF50720" "7DF40720" "FFFD0720" \
|
||||
"7EFC0720" "FF870720" "7F860720" "FF8F0720" \
|
||||
"808E0720" "FF970720" "81960720" "FF9F0720" \
|
||||
"829E0720" "FFA70720" "83A60720" "FFAF0720" \
|
||||
"84AE0720" "FFB70720" "85B60720" "FFBF0720" \
|
||||
"86BE0720" "FFC70720" "87C60720" "FFCF0720" \
|
||||
"88CE0720" "FFD70720" "89D60720" "FFDF0720" \
|
||||
"8ADE0720" "FFE70720" "8BE60720" "FFEF0720" \
|
||||
"8CEE0720" "FFF70720" "8DF60720" "FFFF0720" \
|
||||
"8EFE0720" \
|
||||
"FFFF0720" "8EFE0720" "FFF70720" "8DF60720" \
|
||||
"FFEF0720" "8CEE0720" "FFE70720" "8BE60720" \
|
||||
"FFDF0720" "8ADE0720" "FFD70720" "89D60720" \
|
||||
"FFCF0720" "88CE0720" "FFC70720" "87C60720" \
|
||||
"FFBF0720" "86BE0720" "FFB70720" "85B60720" \
|
||||
"FFAF0720" "84AE0720" "FFA70720" "83A60720" \
|
||||
"FF9F0720" "829E0720" "FF970720" "81960720" \
|
||||
"FF8F0720" "808E0720" "FF870720" "7F860720" \
|
||||
"FFFD0720" "7EFC0720" "FFF50720" "7DF40720" \
|
||||
"FFED0720" "7CEC0720" "FFE50720" "7BE40720" \
|
||||
"FFDD0720" "7ADC0720" "FFD50720" "79D40720" \
|
||||
"FFCD0720" "78CC0720" "FFC50720" "77C40720" \
|
||||
"FFBD0720" "76BC0720" "FFB50720" "75B40720" \
|
||||
"FFAD0720" "74AC0720" "FFA50720" "73A40720" \
|
||||
"FF9D0720" "729C0720" "FF950720" "71940720" \
|
||||
"FF8D0720" "708C0720" "FF850720" "6F840720" \
|
||||
"FFFB0720" "6EFA0720" "FFF30720" "6DF20720" \
|
||||
"FFEB0720" "6CEA0720" "FFE30720" "6BE20720" \
|
||||
"FFDB0720" "6ADA0720" "FFD30720" "69D20720" \
|
||||
"FFCB0720" "68CA0720" "FFC30720" "67C20720" \
|
||||
"FFBB0720" "66BA0720" "FFB30720" "65B20720" \
|
||||
"FFAB0720" "64AA0720" "FFA30720" "63A20720" \
|
||||
"FF9B0720" "629A0720" "FF930720" "61920720" \
|
||||
"FF8B0720" "608A0720" "FF830720" "5F820720" \
|
||||
"5F820720" "FF830720" "5F820720" "FF830720" \
|
||||
"5F820720" "FF830720" "5F820720" "FF8B0720" \
|
||||
"608A0720" "FF930720" "61920720" "FF9B0720" \
|
||||
"629A0720" "FFA30720" "63A20720" "FFAB0720" \
|
||||
"64AA0720" "FFB30720" "65B20720" "FFBB0720" \
|
||||
"66BA0720" "FFC30720" "67C20720" "FFCB0720" \
|
||||
"68CA0720" "FFD30720" "69D20720" "FFDB0720" \
|
||||
"6ADA0720" "FFE30720" "6BE20720" "FFEB0720" \
|
||||
"6CEA0720" "FFF30720" "6DF20720" "FFFB0720" \
|
||||
"6EFA0720" "FF850720" "6F840720" "FF8D0720" \
|
||||
"708C0720" "FF950720" "71940720" "FF9D0720" \
|
||||
"729C0720" "FFA50720" "73A40720" "FFAD0720" \
|
||||
"74AC0720" "FFB50720" "75B40720" "FFBD0720" \
|
||||
"76BC0720" "FFC50720" "77C40720" "FFCD0720" \
|
||||
"78CC0720" "FFD50720" "79D40720" "FFDD0720" \
|
||||
"7ADC0720" "FFE50720" "7BE40720" "FFED0720" \
|
||||
"7CEC0720" "FFF50720" "7DF40720" "FFFD0720" \
|
||||
"7EFC0720" "FF870720" "7F860720" "FF8F0720" \
|
||||
"808E0720" "FF970720" "81960720" "FF9F0720" \
|
||||
"829E0720" "FFA70720" "83A60720" "FFAF0720" \
|
||||
"84AE0720" "FFB70720" "85B60720" "FFBF0720" \
|
||||
"86BE0720" "FFC70720" "87C60720" "FFCF0720" \
|
||||
"88CE0720" "FFD70720" "89D60720" "FFDF0720" \
|
||||
"8ADE0720" "FFE70720" "8BE60720" "FFEF0720" \
|
||||
"8CEE0720" "FFF70720" "8DF60720" "FFFF0720" \
|
||||
"8EFE0720" \
|
||||
"FFFF0720" "8EFE0720" "FFF70720" "8DF60720" \
|
||||
"FFEF0720" "8CEE0720" "FFE70720" "8BE60720" \
|
||||
"FFDF0720" "8ADE0720" "FFD70720" "89D60720" \
|
||||
"FFCF0720" "88CE0720" "FFC70720" "87C60720" \
|
||||
"FFBF0720" "86BE0720" "FFB70720" "85B60720" \
|
||||
"FFAF0720" "84AE0720" "FFA70720" "83A60720" \
|
||||
"FF9F0720" "829E0720" "FF970720" "81960720" \
|
||||
"FF8F0720" "808E0720" "FF870720" "7F860720" \
|
||||
"FFFD0720" "7EFC0720" "FFF50720" "7DF40720" \
|
||||
"FFED0720" "7CEC0720" "FFE50720" "7BE40720" \
|
||||
"FFDD0720" "7ADC0720" "FFD50720" "79D40720" \
|
||||
"FFCD0720" "78CC0720" "FFC50720" "77C40720" \
|
||||
"FFBD0720" "76BC0720" "FFB50720" "75B40720" \
|
||||
"FFAD0720" "74AC0720" "FFA50720" "73A40720" \
|
||||
"FF9D0720" "729C0720" "FF950720" "71940720" \
|
||||
"FF8D0720" "708C0720" "FF850720" "6F840720" \
|
||||
"FFFB0720" "6EFA0720" "FFF30720" "6DF20720" \
|
||||
"FFEB0720" "6CEA0720" "FFE30720" "6BE20720" \
|
||||
"FFDB0720" "6ADA0720" "FFD30720" "69D20720" \
|
||||
"FFCB0720" "68CA0720" "FFC30720" "67C20720" \
|
||||
"FFBB0720" "66BA0720" "FFB30720" "65B20720" \
|
||||
"FFAB0720" "64AA0720" "FFA30720" "63A20720" \
|
||||
"FF9B0720" "629A0720" "FF930720" "61920720" \
|
||||
"FF8B0720" "608A0720" "FF830720" "5F820720" \
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () \
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () \
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () \
|
||||
@@ -1871,49 +1873,49 @@ const char *vfs301_0220_01[] = {
|
||||
"A46C0420"
|
||||
"A46C0400"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420" "83688420" "83688420"
|
||||
"83688420" "83688420"
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 ()
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 ()
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 ()
|
||||
@@ -2268,55 +2270,55 @@ const char *vfs301_02D0_04[] = {
|
||||
* any troubles. */
|
||||
PACKET ("0200", "8005",
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
"FFF30720" "80F20720" "FFF30720" "80F20720"
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 ()
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 ()
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 ()
|
||||
@@ -2437,55 +2439,55 @@ const char *vfs301_02D0_05[] = {
|
||||
* any troubles. */
|
||||
PACKET ("0200", "8005",
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
"FFF34720" "80F24720" "FFF34720" "80F24720"
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 ()
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 ()
|
||||
Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 () Z8 ()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
#define VFS5011_LINE_SIZE 240
|
||||
#define VFS5011_IMAGE_WIDTH 160
|
||||
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Socket utilities for "simple" device debugging
|
||||
*
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
* Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "virtual_device_connection"
|
||||
|
||||
#include "fpi-log.h"
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
#include <gio/gunixsocketaddress.h>
|
||||
|
||||
#include "virtual-device-private.h"
|
||||
|
||||
struct _FpDeviceVirtualListener
|
||||
{
|
||||
GSocketListener parent_instance;
|
||||
|
||||
GSocketConnection *connection;
|
||||
GCancellable *cancellable;
|
||||
guint cancellable_id;
|
||||
|
||||
FpDeviceVirtualListenerConnectionCb ready_cb;
|
||||
gpointer ready_cb_data;
|
||||
|
||||
gint socket_fd;
|
||||
gint client_fd;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FpDeviceVirtualListener, fp_device_virtual_listener, G_TYPE_SOCKET_LISTENER)
|
||||
|
||||
static void start_listen (FpDeviceVirtualListener *self);
|
||||
|
||||
FpDeviceVirtualListener *
|
||||
fp_device_virtual_listener_new (void)
|
||||
{
|
||||
return g_object_new (fp_device_virtual_listener_get_type (), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_device_virtual_listener_dispose (GObject *object)
|
||||
{
|
||||
FpDeviceVirtualListener *self = FP_DEVICE_VIRTUAL_LISTENER (object);
|
||||
|
||||
if (self->cancellable_id)
|
||||
{
|
||||
g_cancellable_disconnect (self->cancellable, self->cancellable_id);
|
||||
self->cancellable_id = 0;
|
||||
}
|
||||
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
g_clear_object (&self->connection);
|
||||
|
||||
self->ready_cb = NULL;
|
||||
|
||||
G_OBJECT_CLASS (fp_device_virtual_listener_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_device_virtual_listener_class_init (FpDeviceVirtualListenerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = fp_device_virtual_listener_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
fp_device_virtual_listener_init (FpDeviceVirtualListener *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualListener *self = user_data;
|
||||
GSocketConnection *connection;
|
||||
|
||||
connection = g_socket_listener_accept_finish (G_SOCKET_LISTENER (source_object),
|
||||
res,
|
||||
NULL,
|
||||
&error);
|
||||
if (!connection)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
return;
|
||||
|
||||
g_warning ("Error accepting a new connection: %s", error->message);
|
||||
start_listen (self);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Always allow further connections.
|
||||
* If we get a new one, we generally just close the old connection. */
|
||||
start_listen (self);
|
||||
if (self->connection)
|
||||
{
|
||||
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
|
||||
g_clear_object (&self->connection);
|
||||
}
|
||||
|
||||
self->connection = connection;
|
||||
fp_dbg ("Got a new connection!");
|
||||
|
||||
self->ready_cb (self, self->ready_cb_data);
|
||||
}
|
||||
|
||||
static void
|
||||
start_listen (FpDeviceVirtualListener *self)
|
||||
{
|
||||
g_socket_listener_accept_async (G_SOCKET_LISTENER (self),
|
||||
self->cancellable,
|
||||
new_connection_cb,
|
||||
self);
|
||||
}
|
||||
|
||||
static void
|
||||
on_cancelled (GCancellable *cancellable,
|
||||
FpDeviceVirtualListener *self)
|
||||
{
|
||||
fp_device_virtual_listener_connection_close (self);
|
||||
g_socket_listener_close (G_SOCKET_LISTENER (self));
|
||||
g_clear_object (&self->cancellable);
|
||||
self->ready_cb = NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
fp_device_virtual_listener_start (FpDeviceVirtualListener *self,
|
||||
const char *address,
|
||||
GCancellable *cancellable,
|
||||
FpDeviceVirtualListenerConnectionCb cb,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GSocketAddress) addr = NULL;
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
g_return_val_if_fail (FP_IS_DEVICE_VIRTUAL_LISTENER (self), FALSE);
|
||||
g_return_val_if_fail (cb != NULL, FALSE);
|
||||
g_return_val_if_fail (self->ready_cb == NULL, FALSE);
|
||||
|
||||
self->client_fd = -1;
|
||||
|
||||
g_socket_listener_set_backlog (G_SOCKET_LISTENER (self), 1);
|
||||
|
||||
/* Remove any left over socket. */
|
||||
g_unlink (address);
|
||||
|
||||
addr = g_unix_socket_address_new (address);
|
||||
|
||||
if (!g_socket_listener_add_address (G_SOCKET_LISTENER (self),
|
||||
addr,
|
||||
G_SOCKET_TYPE_STREAM,
|
||||
G_SOCKET_PROTOCOL_DEFAULT,
|
||||
NULL,
|
||||
NULL,
|
||||
error))
|
||||
{
|
||||
g_warning ("Could not listen on unix socket: %s", (*error)->message);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->ready_cb = cb;
|
||||
self->ready_cb_data = user_data;
|
||||
self->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
|
||||
|
||||
if (self->cancellable)
|
||||
self->cancellable_id = g_cancellable_connect (self->cancellable,
|
||||
G_CALLBACK (on_cancelled), self, NULL);
|
||||
|
||||
start_listen (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
fp_device_virtual_listener_connection_close (FpDeviceVirtualListener *self)
|
||||
{
|
||||
g_return_val_if_fail (FP_IS_DEVICE_VIRTUAL_LISTENER (self), FALSE);
|
||||
|
||||
if (!self->connection)
|
||||
return FALSE;
|
||||
|
||||
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
|
||||
g_clear_object (&self->connection);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_stream_read_cb (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(GTask) task = user_data;
|
||||
FpDeviceVirtualListener *self = g_task_get_source_object (task);
|
||||
gboolean all;
|
||||
gboolean success;
|
||||
gsize bytes;
|
||||
|
||||
all = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task), "all"));
|
||||
|
||||
if (all)
|
||||
{
|
||||
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error);
|
||||
}
|
||||
else
|
||||
{
|
||||
gssize sbytes;
|
||||
|
||||
sbytes = g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error);
|
||||
bytes = sbytes;
|
||||
success = (sbytes >= 0);
|
||||
}
|
||||
|
||||
if (g_task_return_error_if_cancelled (task))
|
||||
return;
|
||||
|
||||
/* If we are cancelled, just return immediately. */
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED))
|
||||
{
|
||||
g_task_return_int (task, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If this error is for an old connection (that should be closed already),
|
||||
* then just give up immediately with a CLOSED error.
|
||||
*/
|
||||
if (self->connection &&
|
||||
g_io_stream_get_input_stream (G_IO_STREAM (self->connection)) != G_INPUT_STREAM (source_object))
|
||||
{
|
||||
g_task_return_new_error (task,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_CLOSED,
|
||||
"Error on old connection, ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!success || bytes == 0)
|
||||
{
|
||||
/* We accept it if someone tries to read twice and just return that error. */
|
||||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
|
||||
{
|
||||
if (self->connection)
|
||||
{
|
||||
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
|
||||
g_clear_object (&self->connection);
|
||||
}
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Got empty data");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g_task_return_int (task, bytes);
|
||||
}
|
||||
|
||||
void
|
||||
fp_device_virtual_listener_read (FpDeviceVirtualListener *self,
|
||||
gboolean all,
|
||||
void *buffer,
|
||||
gsize count,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GTask) task = NULL;
|
||||
GInputStream *stream;
|
||||
|
||||
g_return_if_fail (FP_IS_DEVICE_VIRTUAL_LISTENER (self));
|
||||
|
||||
task = g_task_new (self, self->cancellable, callback, user_data);
|
||||
g_object_set_data (G_OBJECT (task), "all", GINT_TO_POINTER (all));
|
||||
|
||||
if (!self->connection || g_io_stream_is_closed (G_IO_STREAM (self->connection)))
|
||||
{
|
||||
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED,
|
||||
"Listener not connected to any stream");
|
||||
return;
|
||||
}
|
||||
|
||||
stream = g_io_stream_get_input_stream (G_IO_STREAM (self->connection));
|
||||
if (all)
|
||||
{
|
||||
g_input_stream_read_all_async (stream, buffer, count,
|
||||
G_PRIORITY_DEFAULT,
|
||||
self->cancellable,
|
||||
on_stream_read_cb,
|
||||
g_steal_pointer (&task));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_input_stream_read_async (stream, buffer, count,
|
||||
G_PRIORITY_DEFAULT,
|
||||
self->cancellable,
|
||||
on_stream_read_cb,
|
||||
g_steal_pointer (&task));
|
||||
}
|
||||
}
|
||||
|
||||
gsize
|
||||
fp_device_virtual_listener_read_finish (FpDeviceVirtualListener *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, self), 0);
|
||||
|
||||
return g_task_propagate_int (G_TASK (result), error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
fp_device_virtual_listener_write_sync (FpDeviceVirtualListener *self,
|
||||
const char *buffer,
|
||||
gsize count,
|
||||
GError **error)
|
||||
{
|
||||
if (!self->connection || g_io_stream_is_closed (G_IO_STREAM (self->connection)))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED,
|
||||
"Listener not connected to any stream");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (self->connection)),
|
||||
buffer,
|
||||
count,
|
||||
NULL,
|
||||
self->cancellable,
|
||||
error);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Virtual driver for "simple" device debugging
|
||||
*
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
* Copyright (C) 2020 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a virtual driver to debug the non-image based drivers. A small
|
||||
* python script is provided to connect to it via a socket, allowing
|
||||
* prints to registered programmatically.
|
||||
* Using this, it is possible to test libfprint and fprintd.
|
||||
*/
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "fpi-device.h"
|
||||
|
||||
#define MAX_LINE_LEN 1024
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FpDeviceVirtualListener, fp_device_virtual_listener, FP, DEVICE_VIRTUAL_LISTENER, GSocketListener)
|
||||
|
||||
typedef void (*FpDeviceVirtualListenerConnectionCb) (FpDeviceVirtualListener *listener,
|
||||
gpointer user_data);
|
||||
|
||||
FpDeviceVirtualListener * fp_device_virtual_listener_new (void);
|
||||
|
||||
gboolean fp_device_virtual_listener_start (FpDeviceVirtualListener *listener,
|
||||
const char *address,
|
||||
GCancellable *cancellable,
|
||||
FpDeviceVirtualListenerConnectionCb cb,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
|
||||
gboolean fp_device_virtual_listener_connection_close (FpDeviceVirtualListener *listener);
|
||||
|
||||
void fp_device_virtual_listener_read (FpDeviceVirtualListener *listener,
|
||||
gboolean all,
|
||||
void *buffer,
|
||||
gsize count,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gsize fp_device_virtual_listener_read_finish (FpDeviceVirtualListener *listener,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
gboolean fp_device_virtual_listener_write_sync (FpDeviceVirtualListener *self,
|
||||
const char *buffer,
|
||||
gsize count,
|
||||
GError **error);
|
||||
|
||||
|
||||
struct _FpDeviceVirtualDevice
|
||||
{
|
||||
FpDevice parent;
|
||||
|
||||
FpDeviceVirtualListener *listener;
|
||||
GCancellable *cancellable;
|
||||
|
||||
char recv_buf[MAX_LINE_LEN];
|
||||
|
||||
GPtrArray *pending_commands;
|
||||
|
||||
GHashTable *prints_storage;
|
||||
|
||||
guint wait_command_id;
|
||||
guint sleep_timeout_id;
|
||||
guint enroll_stages_passed;
|
||||
gboolean match_reported;
|
||||
gboolean supports_cancellation;
|
||||
gboolean injected_synthetic_cmd;
|
||||
gboolean ignore_wait;
|
||||
gboolean keep_alive;
|
||||
};
|
||||
|
||||
/* Not really final here, but we can do this to share the FpDeviceVirtualDevice
|
||||
* contents without having to use a shared private struct instead. */
|
||||
G_DECLARE_FINAL_TYPE (FpDeviceVirtualDevice, fpi_device_virtual_device, FP, DEVICE_VIRTUAL_DEVICE, FpDevice)
|
||||
|
||||
struct _FpDeviceVirtualDeviceStorage
|
||||
{
|
||||
FpDeviceVirtualDevice parent;
|
||||
};
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FpDeviceVirtualDeviceStorage, fpi_device_virtual_device_storage, FP, DEVICE_VIRTUAL_DEVICE_STORAGE, FpDeviceVirtualDevice)
|
||||
|
||||
|
||||
char * process_cmds (FpDeviceVirtualDevice * self, gboolean scan, GError **error);
|
||||
char * start_scan_command (FpDeviceVirtualDevice *self,
|
||||
GError **error);
|
||||
gboolean should_wait_for_command (FpDeviceVirtualDevice *self,
|
||||
GError *error);
|
||||
gboolean should_wait_to_sleep (FpDeviceVirtualDevice *self,
|
||||
const char *scan_id,
|
||||
GError *error);
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Virtual driver for "simple" device debugging with storage
|
||||
*
|
||||
* Copyright (C) 2020 Bastien Nocera <hadess@hadess.net>
|
||||
* Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a virtual driver to debug the non-image based drivers. A small
|
||||
* python script is provided to connect to it via a socket, allowing
|
||||
* prints to registered programmatically.
|
||||
* Using this, it is possible to test libfprint and fprintd.
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "virtual_device_storage"
|
||||
|
||||
#include "virtual-device-private.h"
|
||||
#include "fpi-log.h"
|
||||
|
||||
G_DEFINE_TYPE (FpDeviceVirtualDeviceStorage, fpi_device_virtual_device_storage, fpi_device_virtual_device_get_type ())
|
||||
|
||||
static GPtrArray * get_stored_prints (FpDeviceVirtualDevice * self);
|
||||
|
||||
static void
|
||||
dev_identify (FpDevice *dev)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (dev);
|
||||
g_autofree char *scan_id = NULL;
|
||||
|
||||
scan_id = start_scan_command (self, &error);
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
|
||||
return;
|
||||
|
||||
if (scan_id)
|
||||
{
|
||||
g_autoptr(GPtrArray) stored = get_stored_prints (self);
|
||||
GPtrArray *prints;
|
||||
GVariant *data = NULL;
|
||||
FpPrint *new_scan;
|
||||
FpPrint *match = NULL;
|
||||
guint idx;
|
||||
|
||||
new_scan = fp_print_new (dev);
|
||||
fpi_print_set_type (new_scan, FPI_PRINT_RAW);
|
||||
fpi_print_set_device_stored (new_scan, TRUE);
|
||||
data = g_variant_new_string (scan_id);
|
||||
g_object_set (new_scan, "fpi-data", data, NULL);
|
||||
|
||||
fpi_device_get_identify_data (dev, &prints);
|
||||
g_debug ("Trying to identify print '%s' against a gallery of %u prints", scan_id, prints->len);
|
||||
|
||||
if (!g_ptr_array_find_with_equal_func (stored,
|
||||
new_scan,
|
||||
(GEqualFunc) fp_print_equal,
|
||||
NULL))
|
||||
error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND);
|
||||
else if (g_ptr_array_find_with_equal_func (prints,
|
||||
new_scan,
|
||||
(GEqualFunc) fp_print_equal,
|
||||
&idx))
|
||||
match = g_ptr_array_index (prints, idx);
|
||||
|
||||
if (!self->match_reported)
|
||||
{
|
||||
self->match_reported = TRUE;
|
||||
fpi_device_identify_report (dev,
|
||||
match,
|
||||
new_scan,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
else if (error && error->domain == FP_DEVICE_RETRY)
|
||||
{
|
||||
fpi_device_identify_report (dev, NULL, NULL, g_steal_pointer (&error));
|
||||
}
|
||||
|
||||
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
||||
FP_FINGER_STATUS_NONE,
|
||||
FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
if (should_wait_to_sleep (self, scan_id, error))
|
||||
return;
|
||||
|
||||
self->match_reported = FALSE;
|
||||
fpi_device_identify_complete (dev, g_steal_pointer (&error));
|
||||
}
|
||||
|
||||
struct ListData
|
||||
{
|
||||
FpDevice *dev;
|
||||
GPtrArray *res;
|
||||
};
|
||||
|
||||
static void
|
||||
dev_list_insert_print (gpointer key,
|
||||
gpointer value,
|
||||
gpointer user_data)
|
||||
{
|
||||
struct ListData *data = user_data;
|
||||
FpPrint *print = fp_print_new (data->dev);
|
||||
GVariant *var = NULL;
|
||||
|
||||
fpi_print_fill_from_user_id (print, key);
|
||||
fpi_print_set_type (print, FPI_PRINT_RAW);
|
||||
var = g_variant_new_string (key);
|
||||
g_object_set (print, "fpi-data", var, NULL);
|
||||
g_object_ref_sink (print);
|
||||
|
||||
g_ptr_array_add (data->res, print);
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
get_stored_prints (FpDeviceVirtualDevice *self)
|
||||
{
|
||||
GPtrArray * prints_list;
|
||||
struct ListData data;
|
||||
|
||||
prints_list = g_ptr_array_new_full (g_hash_table_size (self->prints_storage),
|
||||
g_object_unref);
|
||||
data.dev = FP_DEVICE (self);
|
||||
data.res = prints_list;
|
||||
|
||||
g_hash_table_foreach (self->prints_storage, dev_list_insert_print, &data);
|
||||
|
||||
return prints_list;
|
||||
}
|
||||
|
||||
static void
|
||||
dev_list (FpDevice *dev)
|
||||
{
|
||||
g_autoptr(GPtrArray) prints_list = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualDevice *vdev = FP_DEVICE_VIRTUAL_DEVICE (dev);
|
||||
|
||||
process_cmds (vdev, FALSE, &error);
|
||||
if (should_wait_for_command (vdev, error))
|
||||
return;
|
||||
|
||||
if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
fpi_device_list_complete (dev, NULL, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
fpi_device_list_complete (dev, get_stored_prints (vdev), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_delete (FpDevice *dev)
|
||||
{
|
||||
g_autoptr(GVariant) data = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualDevice *vdev = FP_DEVICE_VIRTUAL_DEVICE (dev);
|
||||
FpPrint *print = NULL;
|
||||
const char *id = NULL;
|
||||
|
||||
process_cmds (vdev, FALSE, &error);
|
||||
if (should_wait_for_command (vdev, error))
|
||||
return;
|
||||
|
||||
if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
fpi_device_delete_complete (dev, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
fpi_device_get_delete_data (dev, &print);
|
||||
|
||||
g_object_get (print, "fpi-data", &data, NULL);
|
||||
if (data == NULL)
|
||||
{
|
||||
fpi_device_delete_complete (dev,
|
||||
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
|
||||
return;
|
||||
}
|
||||
|
||||
id = g_variant_get_string (data, NULL);
|
||||
|
||||
fp_dbg ("Deleting print %s for user %s",
|
||||
id,
|
||||
fp_print_get_username (print));
|
||||
|
||||
if (g_hash_table_remove (vdev->prints_storage, id))
|
||||
fpi_device_delete_complete (dev, NULL);
|
||||
else
|
||||
fpi_device_delete_complete (dev,
|
||||
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_virtual_device_storage_init (FpDeviceVirtualDeviceStorage *self)
|
||||
{
|
||||
FpDeviceVirtualDevice *vdev = FP_DEVICE_VIRTUAL_DEVICE (self);
|
||||
|
||||
vdev->prints_storage = g_hash_table_new_full (g_str_hash,
|
||||
g_str_equal,
|
||||
g_free,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_virtual_device_storage_finalize (GObject *object)
|
||||
{
|
||||
FpDeviceVirtualDevice *vdev = FP_DEVICE_VIRTUAL_DEVICE (object);
|
||||
|
||||
G_DEBUG_HERE ();
|
||||
g_clear_pointer (&vdev->prints_storage, g_hash_table_destroy);
|
||||
G_OBJECT_CLASS (fpi_device_virtual_device_storage_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static const FpIdEntry driver_ids[] = {
|
||||
{ .virtual_envvar = "FP_VIRTUAL_DEVICE_STORAGE" },
|
||||
{ .virtual_envvar = "FP_VIRTUAL_DEVICE_IDENT" },
|
||||
{ .virtual_envvar = NULL }
|
||||
};
|
||||
|
||||
static void
|
||||
fpi_device_virtual_device_storage_class_init (FpDeviceVirtualDeviceStorageClass *klass)
|
||||
{
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = fpi_device_virtual_device_storage_finalize;
|
||||
|
||||
dev_class->id = FP_COMPONENT;
|
||||
dev_class->full_name = "Virtual device with storage and identification for debugging";
|
||||
dev_class->id_table = driver_ids;
|
||||
|
||||
dev_class->identify = dev_identify;
|
||||
dev_class->list = dev_list;
|
||||
dev_class->delete = dev_delete;
|
||||
}
|
||||
@@ -0,0 +1,779 @@
|
||||
/*
|
||||
* Virtual driver for "simple" device debugging
|
||||
*
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
* Copyright (C) 2020 Bastien Nocera <hadess@hadess.net>
|
||||
* Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a virtual driver to debug the non-image based drivers. A small
|
||||
* python script is provided to connect to it via a socket, allowing
|
||||
* prints to registered programmatically.
|
||||
* Using this, it is possible to test libfprint and fprintd.
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "virtual_device"
|
||||
|
||||
#include "virtual-device-private.h"
|
||||
#include "fpi-log.h"
|
||||
|
||||
G_DEFINE_TYPE (FpDeviceVirtualDevice, fpi_device_virtual_device, FP_TYPE_DEVICE)
|
||||
|
||||
#define INSERT_CMD_PREFIX "INSERT "
|
||||
#define REMOVE_CMD_PREFIX "REMOVE "
|
||||
#define SCAN_CMD_PREFIX "SCAN "
|
||||
#define ERROR_CMD_PREFIX "ERROR "
|
||||
#define RETRY_CMD_PREFIX "RETRY "
|
||||
#define FINGER_CMD_PREFIX "FINGER "
|
||||
#define SLEEP_CMD_PREFIX "SLEEP "
|
||||
#define SET_ENROLL_STAGES_PREFIX "SET_ENROLL_STAGES "
|
||||
#define SET_SCAN_TYPE_PREFIX "SET_SCAN_TYPE "
|
||||
#define SET_CANCELLATION_PREFIX "SET_CANCELLATION_ENABLED "
|
||||
#define SET_KEEP_ALIVE_PREFIX "SET_KEEP_ALIVE "
|
||||
|
||||
#define LIST_CMD "LIST"
|
||||
#define UNPLUG_CMD "UNPLUG"
|
||||
|
||||
static void
|
||||
maybe_continue_current_action (FpDeviceVirtualDevice *self)
|
||||
{
|
||||
FpDevice *dev = FP_DEVICE (self);
|
||||
|
||||
if (self->sleep_timeout_id)
|
||||
return;
|
||||
|
||||
switch (fpi_device_get_current_action (dev))
|
||||
{
|
||||
case FPI_DEVICE_ACTION_ENROLL:
|
||||
FP_DEVICE_GET_CLASS (self)->enroll (dev);
|
||||
break;
|
||||
|
||||
case FPI_DEVICE_ACTION_VERIFY:
|
||||
FP_DEVICE_GET_CLASS (self)->verify (dev);
|
||||
break;
|
||||
|
||||
case FPI_DEVICE_ACTION_IDENTIFY:
|
||||
FP_DEVICE_GET_CLASS (self)->identify (dev);
|
||||
break;
|
||||
|
||||
case FPI_DEVICE_ACTION_LIST:
|
||||
FP_DEVICE_GET_CLASS (self)->list (dev);
|
||||
break;
|
||||
|
||||
case FPI_DEVICE_ACTION_DELETE:
|
||||
FP_DEVICE_GET_CLASS (self)->delete (dev);
|
||||
break;
|
||||
|
||||
case FPI_DEVICE_ACTION_OPEN:
|
||||
FP_DEVICE_GET_CLASS (self)->open (dev);
|
||||
break;
|
||||
|
||||
case FPI_DEVICE_ACTION_CLOSE:
|
||||
FP_DEVICE_GET_CLASS (self)->close (dev);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sleep_timeout_cb (gpointer data)
|
||||
{
|
||||
FpDeviceVirtualDevice *self = data;
|
||||
|
||||
self->sleep_timeout_id = 0;
|
||||
|
||||
if (g_cancellable_is_cancelled (self->cancellable))
|
||||
return FALSE;
|
||||
|
||||
g_debug ("Sleeping completed");
|
||||
maybe_continue_current_action (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char *
|
||||
process_cmds (FpDeviceVirtualDevice * self,
|
||||
gboolean scan,
|
||||
GError **error)
|
||||
{
|
||||
if (g_cancellable_is_cancelled (self->cancellable) ||
|
||||
(fpi_device_get_current_action (FP_DEVICE (self)) != FPI_DEVICE_ACTION_NONE &&
|
||||
g_cancellable_is_cancelled (fpi_device_get_cancellable (FP_DEVICE (self)))))
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
|
||||
"Operation was cancelled");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (self->pending_commands->len > 0)
|
||||
{
|
||||
gchar *cmd = g_ptr_array_index (self->pending_commands, 0);
|
||||
|
||||
g_debug ("Processing command %s", cmd);
|
||||
|
||||
/* These are always processed. */
|
||||
if (g_str_has_prefix (cmd, INSERT_CMD_PREFIX))
|
||||
{
|
||||
g_assert (self->prints_storage);
|
||||
g_hash_table_add (self->prints_storage,
|
||||
g_strdup (cmd + strlen (INSERT_CMD_PREFIX)));
|
||||
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
continue;
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, REMOVE_CMD_PREFIX))
|
||||
{
|
||||
g_assert (self->prints_storage);
|
||||
if (!g_hash_table_remove (self->prints_storage,
|
||||
cmd + strlen (REMOVE_CMD_PREFIX)))
|
||||
g_warning ("ID %s was not found in storage", cmd + strlen (REMOVE_CMD_PREFIX));
|
||||
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
continue;
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, SLEEP_CMD_PREFIX))
|
||||
{
|
||||
guint64 sleep_ms = g_ascii_strtoull (cmd + strlen (SLEEP_CMD_PREFIX), NULL, 10);
|
||||
|
||||
g_debug ("Sleeping %lums", sleep_ms);
|
||||
self->sleep_timeout_id = g_timeout_add (sleep_ms, sleep_timeout_cb, self);
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, ERROR_CMD_PREFIX))
|
||||
{
|
||||
g_propagate_error (error,
|
||||
fpi_device_error_new (g_ascii_strtoull (cmd + strlen (ERROR_CMD_PREFIX), NULL, 10)));
|
||||
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If we are not scanning, then we have to stop here. */
|
||||
if (!scan)
|
||||
{
|
||||
g_warning ("Could not process command: %s", cmd);
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_str_has_prefix (cmd, SCAN_CMD_PREFIX))
|
||||
{
|
||||
char *res = g_strdup (cmd + strlen (SCAN_CMD_PREFIX));
|
||||
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
return res;
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, RETRY_CMD_PREFIX))
|
||||
{
|
||||
g_propagate_error (error,
|
||||
fpi_device_retry_new (g_ascii_strtoull (cmd + strlen (RETRY_CMD_PREFIX), NULL, 10)));
|
||||
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
return NULL;
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, FINGER_CMD_PREFIX))
|
||||
{
|
||||
gboolean finger_present;
|
||||
|
||||
finger_present = g_ascii_strtoull (cmd + strlen (FINGER_CMD_PREFIX), NULL, 10) != 0;
|
||||
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
||||
finger_present ? FP_FINGER_STATUS_PRESENT : FP_FINGER_STATUS_NONE,
|
||||
finger_present ? FP_FINGER_STATUS_NONE : FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Could not process command: %s", cmd);
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* No commands left, throw a timeout error. */
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "No commands left that can be run!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
write_key_to_listener (void *key, void *val, void *user_data)
|
||||
{
|
||||
FpDeviceVirtualListener *listener = FP_DEVICE_VIRTUAL_LISTENER (user_data);
|
||||
|
||||
if (!fp_device_virtual_listener_write_sync (listener, key, strlen (key), NULL) ||
|
||||
!fp_device_virtual_listener_write_sync (listener, "\n", 1, NULL))
|
||||
g_warning ("Error writing reply to LIST command");
|
||||
}
|
||||
|
||||
static void
|
||||
recv_instruction_cb (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualListener *listener = FP_DEVICE_VIRTUAL_LISTENER (source_object);
|
||||
gsize bytes;
|
||||
|
||||
bytes = fp_device_virtual_listener_read_finish (listener, res, &error);
|
||||
fp_dbg ("Got instructions of length %ld", bytes);
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
return;
|
||||
|
||||
g_warning ("Error receiving instruction data: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes > 0)
|
||||
{
|
||||
FpDeviceVirtualDevice *self;
|
||||
g_autofree char *cmd = NULL;
|
||||
|
||||
self = FP_DEVICE_VIRTUAL_DEVICE (user_data);
|
||||
|
||||
cmd = g_strndup (self->recv_buf, bytes);
|
||||
fp_dbg ("Received command %s", cmd);
|
||||
|
||||
if (g_str_has_prefix (cmd, LIST_CMD))
|
||||
{
|
||||
if (self->prints_storage)
|
||||
g_hash_table_foreach (self->prints_storage, write_key_to_listener, listener);
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, UNPLUG_CMD))
|
||||
{
|
||||
fpi_device_remove (FP_DEVICE (self));
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, SET_ENROLL_STAGES_PREFIX))
|
||||
{
|
||||
guint stages;
|
||||
|
||||
stages = g_ascii_strtoull (cmd + strlen (SET_ENROLL_STAGES_PREFIX), NULL, 10);
|
||||
fpi_device_set_nr_enroll_stages (FP_DEVICE (self), stages);
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, SET_SCAN_TYPE_PREFIX))
|
||||
{
|
||||
const char *scan_type = cmd + strlen (SET_SCAN_TYPE_PREFIX);
|
||||
g_autoptr(GEnumClass) scan_types = g_type_class_ref (fp_scan_type_get_type ());
|
||||
GEnumValue *value = g_enum_get_value_by_nick (scan_types, scan_type);
|
||||
|
||||
if (value)
|
||||
fpi_device_set_scan_type (FP_DEVICE (self), value->value);
|
||||
else
|
||||
g_warning ("Scan type '%s' not found", scan_type);
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, SET_CANCELLATION_PREFIX))
|
||||
{
|
||||
self->supports_cancellation = g_ascii_strtoull (
|
||||
cmd + strlen (SET_CANCELLATION_PREFIX), NULL, 10) != 0;
|
||||
|
||||
g_debug ("Cancellation support toggled: %d",
|
||||
self->supports_cancellation);
|
||||
}
|
||||
else if (g_str_has_prefix (cmd, SET_KEEP_ALIVE_PREFIX))
|
||||
{
|
||||
self->keep_alive = g_ascii_strtoull (
|
||||
cmd + strlen (SET_KEEP_ALIVE_PREFIX), NULL, 10) != 0;
|
||||
|
||||
g_debug ("Keep alive toggled: %d", self->keep_alive);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ptr_array_add (self->pending_commands, g_steal_pointer (&cmd));
|
||||
g_clear_handle_id (&self->wait_command_id, g_source_remove);
|
||||
|
||||
maybe_continue_current_action (self);
|
||||
}
|
||||
}
|
||||
|
||||
fp_device_virtual_listener_connection_close (listener);
|
||||
}
|
||||
|
||||
static void
|
||||
recv_instruction (FpDeviceVirtualDevice *self)
|
||||
{
|
||||
fp_device_virtual_listener_read (self->listener,
|
||||
FALSE,
|
||||
self->recv_buf,
|
||||
sizeof (self->recv_buf),
|
||||
recv_instruction_cb,
|
||||
self);
|
||||
}
|
||||
|
||||
static void
|
||||
on_listener_connected (FpDeviceVirtualListener *listener,
|
||||
gpointer user_data)
|
||||
{
|
||||
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (user_data);
|
||||
|
||||
recv_instruction (self);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_init (FpDevice *dev)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(GCancellable) cancellable = NULL;
|
||||
g_autoptr(FpDeviceVirtualListener) listener = NULL;
|
||||
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (dev);
|
||||
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
process_cmds (self, FALSE, &error);
|
||||
if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
fpi_device_open_complete (dev, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
else if (self->sleep_timeout_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (self->listener)
|
||||
{
|
||||
fpi_device_open_complete (dev, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
listener = fp_device_virtual_listener_new ();
|
||||
cancellable = g_cancellable_new ();
|
||||
|
||||
if (!fp_device_virtual_listener_start (listener,
|
||||
fpi_device_get_virtual_env (FP_DEVICE (self)),
|
||||
cancellable,
|
||||
on_listener_connected,
|
||||
self,
|
||||
&error))
|
||||
{
|
||||
fpi_device_open_complete (dev, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
self->listener = g_steal_pointer (&listener);
|
||||
self->cancellable = g_steal_pointer (&cancellable);
|
||||
|
||||
fpi_device_open_complete (dev, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wait_for_command_timeout (gpointer data)
|
||||
{
|
||||
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (data);
|
||||
GError *error = NULL;
|
||||
|
||||
self->wait_command_id = 0;
|
||||
|
||||
switch (fpi_device_get_current_action (FP_DEVICE (self)))
|
||||
{
|
||||
case FPI_DEVICE_ACTION_LIST:
|
||||
case FPI_DEVICE_ACTION_DELETE:
|
||||
self->ignore_wait = TRUE;
|
||||
maybe_continue_current_action (self);
|
||||
self->ignore_wait = FALSE;
|
||||
return FALSE;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
error = g_error_new (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "No commands arrived in time to run!");
|
||||
fpi_device_action_error (FP_DEVICE (self), error);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
should_wait_for_command (FpDeviceVirtualDevice *self,
|
||||
GError *error)
|
||||
{
|
||||
if (!error && self->sleep_timeout_id)
|
||||
return TRUE;
|
||||
|
||||
if (self->ignore_wait)
|
||||
return FALSE;
|
||||
|
||||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
return FALSE;
|
||||
|
||||
if (self->wait_command_id)
|
||||
return FALSE;
|
||||
|
||||
self->wait_command_id = g_timeout_add (500, wait_for_command_timeout, self);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *
|
||||
start_scan_command (FpDeviceVirtualDevice *self,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_autofree char *scan_id = NULL;
|
||||
|
||||
if (fp_device_get_finger_status (FP_DEVICE (self)) == FP_FINGER_STATUS_NONE)
|
||||
self->injected_synthetic_cmd = FALSE;
|
||||
|
||||
scan_id = process_cmds (self, TRUE, &local_error);
|
||||
|
||||
if (!self->sleep_timeout_id)
|
||||
{
|
||||
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
||||
FP_FINGER_STATUS_NEEDED,
|
||||
FP_FINGER_STATUS_NONE);
|
||||
}
|
||||
|
||||
if (should_wait_for_command (self, local_error))
|
||||
{
|
||||
g_assert (!scan_id);
|
||||
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING,
|
||||
"Still waiting for command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (local_error)
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
else
|
||||
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
||||
FP_FINGER_STATUS_PRESENT,
|
||||
FP_FINGER_STATUS_NONE);
|
||||
|
||||
return g_steal_pointer (&scan_id);
|
||||
}
|
||||
|
||||
gboolean
|
||||
should_wait_to_sleep (FpDeviceVirtualDevice *self,
|
||||
const char *scan_id,
|
||||
GError *error)
|
||||
{
|
||||
const gchar *cmd;
|
||||
|
||||
if (self->sleep_timeout_id)
|
||||
return TRUE;
|
||||
|
||||
if (!self->pending_commands->len)
|
||||
return FALSE;
|
||||
|
||||
cmd = g_ptr_array_index (self->pending_commands, 0);
|
||||
|
||||
if (g_str_has_prefix (cmd, SLEEP_CMD_PREFIX))
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_free (process_cmds (self, FALSE, &local_error));
|
||||
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
return FALSE;
|
||||
|
||||
g_assert (!self->injected_synthetic_cmd);
|
||||
g_assert (self->sleep_timeout_id != 0);
|
||||
|
||||
if (!self->pending_commands->len)
|
||||
{
|
||||
g_autofree char *injected_cmd = NULL;
|
||||
|
||||
if (scan_id)
|
||||
injected_cmd = g_strconcat (SCAN_CMD_PREFIX, scan_id, NULL);
|
||||
else if (error && error->domain == FP_DEVICE_ERROR)
|
||||
injected_cmd = g_strdup_printf (ERROR_CMD_PREFIX " %d", error->code);
|
||||
else if (error && error->domain == FP_DEVICE_RETRY)
|
||||
injected_cmd = g_strdup_printf (RETRY_CMD_PREFIX " %d", error->code);
|
||||
else
|
||||
return TRUE;
|
||||
|
||||
g_debug ("Sleeping now, command queued for later: %s", injected_cmd);
|
||||
|
||||
g_ptr_array_insert (self->pending_commands, 0, g_steal_pointer (&injected_cmd));
|
||||
self->injected_synthetic_cmd = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return self->sleep_timeout_id != 0;
|
||||
}
|
||||
|
||||
static void
|
||||
dev_verify (FpDevice *dev)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (dev);
|
||||
g_autofree char *scan_id = NULL;
|
||||
|
||||
scan_id = start_scan_command (self, &error);
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
|
||||
return;
|
||||
|
||||
if (scan_id)
|
||||
{
|
||||
GVariant *data = NULL;
|
||||
FpPrint *new_scan;
|
||||
FpPrint *print;
|
||||
gboolean success;
|
||||
|
||||
g_debug ("Virtual device scanned print %s", scan_id);
|
||||
fpi_device_get_verify_data (dev, &print);
|
||||
|
||||
new_scan = fp_print_new (dev);
|
||||
fpi_print_set_type (new_scan, FPI_PRINT_RAW);
|
||||
if (self->prints_storage)
|
||||
fpi_print_set_device_stored (new_scan, TRUE);
|
||||
data = g_variant_new_string (scan_id);
|
||||
g_object_set (new_scan, "fpi-data", data, NULL);
|
||||
|
||||
if (self->prints_storage && !g_hash_table_contains (self->prints_storage, scan_id))
|
||||
{
|
||||
error = fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND);
|
||||
success = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = fp_print_equal (print, new_scan);
|
||||
}
|
||||
|
||||
if (!self->match_reported)
|
||||
{
|
||||
self->match_reported = TRUE;
|
||||
fpi_device_verify_report (dev,
|
||||
success ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL,
|
||||
new_scan,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
else if (error)
|
||||
{
|
||||
g_debug ("Virtual device scan failed with error: %s", error->message);
|
||||
}
|
||||
|
||||
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
||||
FP_FINGER_STATUS_NONE,
|
||||
FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
if (error && error->domain == FP_DEVICE_RETRY)
|
||||
fpi_device_verify_report (dev, FPI_MATCH_ERROR, NULL, g_steal_pointer (&error));
|
||||
|
||||
if (should_wait_to_sleep (self, scan_id, error))
|
||||
return;
|
||||
|
||||
self->match_reported = FALSE;
|
||||
fpi_device_verify_complete (dev, g_steal_pointer (&error));
|
||||
}
|
||||
|
||||
static void
|
||||
dev_enroll (FpDevice *dev)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (dev);
|
||||
FpPrint *print = NULL;
|
||||
g_autofree char *id = NULL;
|
||||
|
||||
id = start_scan_command (self, &error);
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
|
||||
return;
|
||||
|
||||
fpi_device_get_enroll_data (dev, &print);
|
||||
|
||||
if (id)
|
||||
{
|
||||
GVariant *data;
|
||||
gboolean completed;
|
||||
|
||||
if (self->prints_storage && g_hash_table_contains (self->prints_storage, id))
|
||||
{
|
||||
if (should_wait_to_sleep (self, id, error))
|
||||
return;
|
||||
|
||||
fpi_device_enroll_complete (dev, NULL,
|
||||
fpi_device_error_new (FP_DEVICE_ERROR_DATA_DUPLICATE));
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->enroll_stages_passed == 0)
|
||||
{
|
||||
fpi_print_set_type (print, FPI_PRINT_RAW);
|
||||
data = g_variant_new_string (id);
|
||||
g_object_set (print, "fpi-data", data, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
gboolean changed;
|
||||
|
||||
g_object_get (print, "fpi-data", &data, NULL);
|
||||
changed = !g_str_equal (id, g_variant_get_string (data, NULL));
|
||||
g_variant_unref (data);
|
||||
|
||||
if (changed)
|
||||
{
|
||||
g_set_error (&error, FP_DEVICE_RETRY, FP_DEVICE_RETRY_GENERAL, "ID Mismatch");
|
||||
fpi_device_enroll_progress (dev, self->enroll_stages_passed, NULL,
|
||||
g_steal_pointer (&error));
|
||||
|
||||
if (!should_wait_to_sleep (self, id, error))
|
||||
self->sleep_timeout_id = g_idle_add (sleep_timeout_cb, self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self->enroll_stages_passed++;
|
||||
completed = self->enroll_stages_passed == fp_device_get_nr_enroll_stages (FP_DEVICE (self));
|
||||
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
||||
completed ?
|
||||
FP_FINGER_STATUS_NEEDED :
|
||||
FP_FINGER_STATUS_NONE,
|
||||
FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
fpi_device_enroll_progress (dev, self->enroll_stages_passed, print, NULL);
|
||||
|
||||
if (completed)
|
||||
{
|
||||
if (self->prints_storage)
|
||||
{
|
||||
fpi_print_set_device_stored (print, TRUE);
|
||||
g_hash_table_add (self->prints_storage, g_strdup (id));
|
||||
}
|
||||
|
||||
fpi_device_enroll_complete (dev, g_object_ref (print), NULL);
|
||||
self->enroll_stages_passed = 0;
|
||||
}
|
||||
else if (!should_wait_to_sleep (self, id, error))
|
||||
{
|
||||
self->sleep_timeout_id = g_idle_add (sleep_timeout_cb, self);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fpi_device_report_finger_status_changes (FP_DEVICE (self),
|
||||
FP_FINGER_STATUS_NONE,
|
||||
FP_FINGER_STATUS_PRESENT);
|
||||
|
||||
if (error && error->domain == FP_DEVICE_RETRY)
|
||||
{
|
||||
fpi_device_enroll_progress (dev, self->enroll_stages_passed, NULL, g_steal_pointer (&error));
|
||||
|
||||
if (!should_wait_to_sleep (self, id, error))
|
||||
self->sleep_timeout_id = g_idle_add (sleep_timeout_cb, self);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (should_wait_to_sleep (self, id, error))
|
||||
return;
|
||||
|
||||
self->enroll_stages_passed = 0;
|
||||
fpi_device_enroll_complete (dev, NULL, g_steal_pointer (&error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dev_cancel (FpDevice *dev)
|
||||
{
|
||||
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (dev);
|
||||
|
||||
if (self->injected_synthetic_cmd)
|
||||
{
|
||||
self->injected_synthetic_cmd = FALSE;
|
||||
g_ptr_array_remove_index (self->pending_commands, 0);
|
||||
}
|
||||
|
||||
if (!self->supports_cancellation)
|
||||
return;
|
||||
|
||||
g_debug ("Got cancellation!");
|
||||
g_clear_handle_id (&self->sleep_timeout_id, g_source_remove);
|
||||
|
||||
maybe_continue_current_action (self);
|
||||
}
|
||||
|
||||
static void
|
||||
stop_listener (FpDeviceVirtualDevice *self)
|
||||
{
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
g_clear_object (&self->listener);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_deinit (FpDevice *dev)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (dev);
|
||||
|
||||
process_cmds (self, FALSE, &error);
|
||||
if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
fpi_device_close_complete (dev, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
else if (self->sleep_timeout_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_clear_handle_id (&self->wait_command_id, g_source_remove);
|
||||
g_clear_handle_id (&self->sleep_timeout_id, g_source_remove);
|
||||
|
||||
if (!self->keep_alive)
|
||||
stop_listener (self);
|
||||
|
||||
fpi_device_close_complete (dev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_virtual_device_finalize (GObject *object)
|
||||
{
|
||||
FpDeviceVirtualDevice *self = FP_DEVICE_VIRTUAL_DEVICE (object);
|
||||
|
||||
G_DEBUG_HERE ();
|
||||
stop_listener (self);
|
||||
g_clear_pointer (&self->pending_commands, g_ptr_array_unref);
|
||||
G_OBJECT_CLASS (fpi_device_virtual_device_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_virtual_device_init (FpDeviceVirtualDevice *self)
|
||||
{
|
||||
self->supports_cancellation = TRUE;
|
||||
self->pending_commands = g_ptr_array_new_with_free_func (g_free);
|
||||
}
|
||||
|
||||
static const FpIdEntry driver_ids[] = {
|
||||
{ .virtual_envvar = "FP_VIRTUAL_DEVICE", },
|
||||
{ .virtual_envvar = NULL }
|
||||
};
|
||||
|
||||
static void
|
||||
fpi_device_virtual_device_class_init (FpDeviceVirtualDeviceClass *klass)
|
||||
{
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = fpi_device_virtual_device_finalize;
|
||||
|
||||
dev_class->id = FP_COMPONENT;
|
||||
dev_class->full_name = "Virtual device for debugging";
|
||||
dev_class->type = FP_DEVICE_TYPE_VIRTUAL;
|
||||
dev_class->id_table = driver_ids;
|
||||
dev_class->nr_enroll_stages = 5;
|
||||
|
||||
dev_class->open = dev_init;
|
||||
dev_class->close = dev_deinit;
|
||||
dev_class->verify = dev_verify;
|
||||
dev_class->enroll = dev_enroll;
|
||||
dev_class->cancel = dev_cancel;
|
||||
}
|
||||
@@ -29,36 +29,27 @@
|
||||
|
||||
#include "fpi-log.h"
|
||||
|
||||
#include "virtual-device-private.h"
|
||||
|
||||
#include "../fpi-image.h"
|
||||
#include "../fpi-image-device.h"
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gunixsocketaddress.h>
|
||||
|
||||
struct _FpDeviceVirtualImage
|
||||
{
|
||||
FpImageDevice parent;
|
||||
FpImageDevice parent;
|
||||
|
||||
GSocketListener *listener;
|
||||
GSocketConnection *connection;
|
||||
GCancellable *listen_cancellable;
|
||||
GCancellable *cancellable;
|
||||
FpDeviceVirtualListener *listener;
|
||||
GCancellable *cancellable;
|
||||
|
||||
gint socket_fd;
|
||||
gint client_fd;
|
||||
|
||||
gboolean automatic_finger;
|
||||
FpImage *recv_img;
|
||||
gint recv_img_hdr[2];
|
||||
gboolean automatic_finger;
|
||||
FpImage *recv_img;
|
||||
gint recv_img_hdr[2];
|
||||
};
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FPI, DEVICE_VIRTUAL_IMAGE, FpImageDevice)
|
||||
G_DEFINE_TYPE (FpDeviceVirtualImage, fpi_device_virtual_image, FP_TYPE_IMAGE_DEVICE)
|
||||
|
||||
static void start_listen (FpDeviceVirtualImage *dev);
|
||||
static void recv_image (FpDeviceVirtualImage *dev,
|
||||
GInputStream *stream);
|
||||
static void recv_image (FpDeviceVirtualImage *self);
|
||||
|
||||
static void
|
||||
recv_image_img_recv_cb (GObject *source_object,
|
||||
@@ -66,35 +57,20 @@ recv_image_img_recv_cb (GObject *source_object,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualListener *listener = FP_DEVICE_VIRTUAL_LISTENER (source_object);
|
||||
FpDeviceVirtualImage *self;
|
||||
FpImageDevice *device;
|
||||
gboolean success;
|
||||
gsize bytes = 0;
|
||||
gsize bytes;
|
||||
|
||||
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error);
|
||||
bytes = fp_device_virtual_listener_read_finish (listener, res, &error);
|
||||
|
||||
/* Can't use self if the operation was cancelled. */
|
||||
if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
if (!bytes || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED))
|
||||
return;
|
||||
|
||||
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
|
||||
device = FP_IMAGE_DEVICE (self);
|
||||
|
||||
/* Consider success if we received the right amount of data, otherwise
|
||||
* an error must have happened. */
|
||||
if (bytes < self->recv_img->width * self->recv_img->height)
|
||||
{
|
||||
if (!success)
|
||||
g_warning ("Error receiving image data: %s", error->message);
|
||||
else
|
||||
g_warning ("Error receiving image data: end of stream before all data was read");
|
||||
|
||||
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
|
||||
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
|
||||
g_clear_object (&self->connection);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->automatic_finger)
|
||||
fpi_image_device_report_finger_status (device, TRUE);
|
||||
fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img));
|
||||
@@ -102,7 +78,7 @@ recv_image_img_recv_cb (GObject *source_object,
|
||||
fpi_image_device_report_finger_status (device, FALSE);
|
||||
|
||||
/* And, listen for more images from the same client. */
|
||||
recv_image (self, G_INPUT_STREAM (source_object));
|
||||
recv_image (self);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -112,37 +88,30 @@ recv_image_hdr_recv_cb (GObject *source_object,
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDeviceVirtualImage *self;
|
||||
gboolean success;
|
||||
FpDeviceVirtualListener *listener = FP_DEVICE_VIRTUAL_LISTENER (source_object);
|
||||
gsize bytes;
|
||||
|
||||
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error);
|
||||
bytes = fp_device_virtual_listener_read_finish (listener, res, &error);
|
||||
|
||||
if (!success || bytes != sizeof (self->recv_img_hdr))
|
||||
if (error)
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED))
|
||||
return;
|
||||
g_warning ("Error receiving header for image data: %s", error->message);
|
||||
}
|
||||
else if (bytes != 0)
|
||||
{
|
||||
g_warning ("Received incomplete header before end of stream.");
|
||||
}
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
|
||||
return;
|
||||
|
||||
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
|
||||
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
|
||||
g_clear_object (&self->connection);
|
||||
g_warning ("Error receiving header for image data: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bytes)
|
||||
return;
|
||||
|
||||
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
|
||||
if (self->recv_img_hdr[0] > 5000 || self->recv_img_hdr[1] > 5000)
|
||||
{
|
||||
g_warning ("Image header suggests an unrealistically large image, disconnecting client.");
|
||||
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
|
||||
g_clear_object (&self->connection);
|
||||
fp_device_virtual_listener_connection_close (listener);
|
||||
}
|
||||
|
||||
if (self->recv_img_hdr[0] < 0 || self->recv_img_hdr[1] < 0)
|
||||
@@ -178,161 +147,87 @@ recv_image_hdr_recv_cb (GObject *source_object,
|
||||
|
||||
default:
|
||||
/* disconnect client, it didn't play fair */
|
||||
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
|
||||
g_clear_object (&self->connection);
|
||||
fp_device_virtual_listener_connection_close (listener);
|
||||
}
|
||||
|
||||
/* And, listen for more images from the same client. */
|
||||
recv_image (self, G_INPUT_STREAM (source_object));
|
||||
recv_image (self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->recv_img = fp_image_new (self->recv_img_hdr[0], self->recv_img_hdr[1]);
|
||||
g_debug ("image data: %p", self->recv_img->data);
|
||||
g_input_stream_read_all_async (G_INPUT_STREAM (source_object),
|
||||
(guint8 *) self->recv_img->data,
|
||||
self->recv_img->width * self->recv_img->height,
|
||||
G_PRIORITY_DEFAULT,
|
||||
self->cancellable,
|
||||
recv_image_img_recv_cb,
|
||||
self);
|
||||
fp_device_virtual_listener_read (listener,
|
||||
TRUE,
|
||||
(guint8 *) self->recv_img->data,
|
||||
self->recv_img->width * self->recv_img->height,
|
||||
recv_image_img_recv_cb,
|
||||
self);
|
||||
}
|
||||
|
||||
static void
|
||||
recv_image (FpDeviceVirtualImage *self, GInputStream *stream)
|
||||
recv_image (FpDeviceVirtualImage *self)
|
||||
{
|
||||
fp_device_virtual_listener_read (self->listener,
|
||||
TRUE,
|
||||
self->recv_img_hdr,
|
||||
sizeof (self->recv_img_hdr),
|
||||
recv_image_hdr_recv_cb,
|
||||
self);
|
||||
}
|
||||
|
||||
static void
|
||||
on_listener_connected (FpDeviceVirtualListener *listener,
|
||||
gpointer user_data)
|
||||
{
|
||||
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
|
||||
FpiImageDeviceState state;
|
||||
|
||||
g_object_get (self, "fpi-image-device-state", &state, NULL);
|
||||
self->automatic_finger = TRUE;
|
||||
|
||||
g_debug ("Starting image receive (if active), state is: %i", state);
|
||||
g_object_get (self,
|
||||
"fpi-image-device-state", &state,
|
||||
NULL);
|
||||
|
||||
/* Only register if the state is active. */
|
||||
switch (state)
|
||||
{
|
||||
case FPI_IMAGE_DEVICE_STATE_IDLE:
|
||||
case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
|
||||
case FPI_IMAGE_DEVICE_STATE_CAPTURE:
|
||||
case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF:
|
||||
g_input_stream_read_all_async (stream,
|
||||
self->recv_img_hdr,
|
||||
sizeof (self->recv_img_hdr),
|
||||
G_PRIORITY_DEFAULT,
|
||||
self->cancellable,
|
||||
recv_image_hdr_recv_cb,
|
||||
self);
|
||||
/* fallthrough */
|
||||
recv_image (self);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
GSocketConnection *connection;
|
||||
GInputStream *stream;
|
||||
FpDeviceVirtualImage *dev = user_data;
|
||||
|
||||
connection = g_socket_listener_accept_finish (G_SOCKET_LISTENER (source_object),
|
||||
res,
|
||||
NULL,
|
||||
&error);
|
||||
if (!connection)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
return;
|
||||
|
||||
g_warning ("Error accepting a new connection: %s", error->message);
|
||||
start_listen (dev);
|
||||
}
|
||||
|
||||
/* Always accept further connections (but we disconnect them immediately
|
||||
* if we already have a connection). */
|
||||
start_listen (dev);
|
||||
|
||||
if (dev->connection)
|
||||
{
|
||||
/* We may not have noticed that the stream was closed,
|
||||
* if the device is deactivated.
|
||||
* Cancel any ongoing operation on the old connection. */
|
||||
g_cancellable_cancel (dev->cancellable);
|
||||
g_clear_object (&dev->cancellable);
|
||||
dev->cancellable = g_cancellable_new ();
|
||||
|
||||
g_clear_object (&dev->connection);
|
||||
}
|
||||
|
||||
if (dev->connection)
|
||||
{
|
||||
g_warning ("Rejecting new connection");
|
||||
g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
|
||||
g_object_unref (connection);
|
||||
return;
|
||||
}
|
||||
|
||||
dev->connection = connection;
|
||||
dev->automatic_finger = TRUE;
|
||||
stream = g_io_stream_get_input_stream (G_IO_STREAM (connection));
|
||||
|
||||
fp_dbg ("Got a new connection!");
|
||||
recv_image (dev, stream);
|
||||
}
|
||||
|
||||
static void
|
||||
start_listen (FpDeviceVirtualImage *dev)
|
||||
{
|
||||
g_socket_listener_accept_async (dev->listener,
|
||||
dev->listen_cancellable,
|
||||
new_connection_cb,
|
||||
dev);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_init (FpImageDevice *dev)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(GSocketListener) listener = NULL;
|
||||
g_autoptr(FpDeviceVirtualListener) listener = NULL;
|
||||
g_autoptr(GCancellable) cancellable = NULL;
|
||||
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
|
||||
const char *env;
|
||||
|
||||
g_autoptr(GSocketAddress) addr = NULL;
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
self->client_fd = -1;
|
||||
listener = fp_device_virtual_listener_new ();
|
||||
cancellable = g_cancellable_new ();
|
||||
|
||||
env = fpi_device_get_virtual_env (FP_DEVICE (self));
|
||||
|
||||
listener = g_socket_listener_new ();
|
||||
g_socket_listener_set_backlog (listener, 1);
|
||||
|
||||
/* Remove any left over socket. */
|
||||
g_unlink (env);
|
||||
|
||||
addr = g_unix_socket_address_new (env);
|
||||
|
||||
if (!g_socket_listener_add_address (listener,
|
||||
addr,
|
||||
G_SOCKET_TYPE_STREAM,
|
||||
G_SOCKET_PROTOCOL_DEFAULT,
|
||||
NULL,
|
||||
NULL,
|
||||
&error))
|
||||
if (!fp_device_virtual_listener_start (listener,
|
||||
fpi_device_get_virtual_env (FP_DEVICE (self)),
|
||||
cancellable,
|
||||
on_listener_connected,
|
||||
self,
|
||||
&error))
|
||||
{
|
||||
g_warning ("Could not listen on unix socket: %s", error->message);
|
||||
|
||||
fpi_image_device_open_complete (FP_IMAGE_DEVICE (dev), g_steal_pointer (&error));
|
||||
|
||||
fpi_image_device_open_complete (dev, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
self->listener = g_steal_pointer (&listener);
|
||||
self->cancellable = g_cancellable_new ();
|
||||
self->listen_cancellable = g_cancellable_new ();
|
||||
|
||||
start_listen (self);
|
||||
self->cancellable = g_steal_pointer (&cancellable);
|
||||
|
||||
/* Delay result to open up the possibility of testing race conditions. */
|
||||
fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_open_complete, NULL, NULL);
|
||||
@@ -346,11 +241,8 @@ dev_deinit (FpImageDevice *dev)
|
||||
G_DEBUG_HERE ();
|
||||
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_cancellable_cancel (self->listen_cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
g_clear_object (&self->listen_cancellable);
|
||||
g_clear_object (&self->listener);
|
||||
g_clear_object (&self->connection);
|
||||
|
||||
/* Delay result to open up the possibility of testing race conditions. */
|
||||
fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_close_complete, NULL, NULL);
|
||||
@@ -361,23 +253,16 @@ dev_activate (FpImageDevice *dev)
|
||||
{
|
||||
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
|
||||
|
||||
fpi_image_device_activate_complete (dev, NULL);
|
||||
/* Start reading (again). */
|
||||
recv_image (self);
|
||||
|
||||
if (self->connection)
|
||||
recv_image (self, g_io_stream_get_input_stream (G_IO_STREAM (self->connection)));
|
||||
fpi_image_device_activate_complete (dev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_deactivate (FpImageDevice *dev)
|
||||
{
|
||||
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
|
||||
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
self->cancellable = g_cancellable_new ();
|
||||
|
||||
/* XXX: Need to wait for the operation to be cancelled. */
|
||||
fpi_device_add_timeout (FP_DEVICE (dev), 10, (FpTimeoutFunc) fpi_image_device_deactivate_complete, NULL, NULL);
|
||||
fpi_image_device_deactivate_complete (dev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -185,7 +185,7 @@ fp_device_get_property (GObject *object,
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_NR_ENROLL_STAGES:
|
||||
g_value_set_int (value, priv->nr_enroll_stages);
|
||||
g_value_set_uint (value, priv->nr_enroll_stages);
|
||||
break;
|
||||
|
||||
case PROP_SCAN_TYPE:
|
||||
@@ -197,7 +197,7 @@ fp_device_get_property (GObject *object,
|
||||
break;
|
||||
|
||||
case PROP_DRIVER:
|
||||
g_value_set_static_string (value, FP_DEVICE_GET_CLASS (priv)->id);
|
||||
g_value_set_static_string (value, FP_DEVICE_GET_CLASS (self)->id);
|
||||
break;
|
||||
|
||||
case PROP_DEVICE_ID:
|
||||
@@ -979,7 +979,7 @@ fp_device_verify_finish (FpDevice *device,
|
||||
|
||||
data = g_task_get_task_data (G_TASK (result));
|
||||
|
||||
*print = data->print;
|
||||
*print = data ? data->print : NULL;
|
||||
if (*print)
|
||||
g_object_ref (*print);
|
||||
}
|
||||
@@ -1092,13 +1092,13 @@ fp_device_identify_finish (FpDevice *device,
|
||||
|
||||
if (print)
|
||||
{
|
||||
*print = data->print;
|
||||
*print = data ? data->print : NULL;
|
||||
if (*print)
|
||||
g_object_ref (*print);
|
||||
}
|
||||
if (match)
|
||||
{
|
||||
*match = data->match;
|
||||
*match = data ? data->match : NULL;
|
||||
if (*match)
|
||||
g_object_ref (*match);
|
||||
}
|
||||
|
||||
+17
-13
@@ -216,6 +216,7 @@ fpi_device_set_nr_enroll_stages (FpDevice *device,
|
||||
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||
|
||||
g_return_if_fail (FP_IS_DEVICE (device));
|
||||
g_return_if_fail (enroll_stages > 0);
|
||||
|
||||
priv->nr_enroll_stages = enroll_stages;
|
||||
g_object_notify (G_OBJECT (device), "nr-enroll-stages");
|
||||
@@ -751,6 +752,21 @@ fp_device_task_return_in_idle_cb (gpointer user_data)
|
||||
priv->current_action = FPI_DEVICE_ACTION_NONE;
|
||||
priv->current_task_idle_return_source = NULL;
|
||||
|
||||
if (action == FPI_DEVICE_ACTION_OPEN &&
|
||||
data->type != FP_DEVICE_TASK_RETURN_ERROR)
|
||||
{
|
||||
priv->is_open = TRUE;
|
||||
g_object_notify (G_OBJECT (data->device), "open");
|
||||
}
|
||||
else if (action == FPI_DEVICE_ACTION_CLOSE)
|
||||
{
|
||||
/* Always consider the device closed. Drivers should try hard to close the
|
||||
* device. Generally, e.g. cancellations should be ignored.
|
||||
*/
|
||||
priv->is_open = FALSE;
|
||||
g_object_notify (G_OBJECT (data->device), "open");
|
||||
}
|
||||
|
||||
/* Return FP_DEVICE_ERROR_REMOVED if the device is removed,
|
||||
* with the exception of a successful open, which is an odd corner case. */
|
||||
if (priv->is_removed &&
|
||||
@@ -920,12 +936,6 @@ fpi_device_open_complete (FpDevice *device, GError *error)
|
||||
clear_device_cancel_action (device);
|
||||
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
priv->is_open = TRUE;
|
||||
g_object_notify (G_OBJECT (device), "open");
|
||||
}
|
||||
|
||||
if (!error)
|
||||
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
|
||||
GUINT_TO_POINTER (TRUE));
|
||||
@@ -976,12 +986,6 @@ fpi_device_close_complete (FpDevice *device, GError *error)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Always consider the device closed. Drivers should try hard to close the
|
||||
* device. Generally, e.g. cancellations should be ignored.
|
||||
*/
|
||||
priv->is_open = FALSE;
|
||||
g_object_notify (G_OBJECT (device), "open");
|
||||
|
||||
if (!error)
|
||||
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
|
||||
GUINT_TO_POINTER (TRUE));
|
||||
@@ -1529,7 +1533,7 @@ fpi_device_report_finger_status (FpDevice *device,
|
||||
* fpi_device_report_finger_status_changes:
|
||||
* @device: The #FpDevice
|
||||
* @added_status: The #FpFingerStatusFlags to add
|
||||
* @added_status: The #FpFingerStatusFlags to remove
|
||||
* @removed_status: The #FpFingerStatusFlags to remove
|
||||
*
|
||||
* Report the finger status for the @device adding the @added_status flags
|
||||
* and removing the @removed_status flags.
|
||||
|
||||
@@ -130,6 +130,7 @@ fpi_ssm_new_full (FpDevice *dev,
|
||||
{
|
||||
FpiSsm *machine;
|
||||
|
||||
BUG_ON (dev == NULL);
|
||||
BUG_ON (nr_states < 1);
|
||||
BUG_ON (handler == NULL);
|
||||
|
||||
@@ -155,6 +156,8 @@ fpi_ssm_set_data (FpiSsm *machine,
|
||||
gpointer ssm_data,
|
||||
GDestroyNotify ssm_data_destroy)
|
||||
{
|
||||
g_return_if_fail (machine);
|
||||
|
||||
if (machine->ssm_data_destroy && machine->ssm_data)
|
||||
machine->ssm_data_destroy (machine->ssm_data);
|
||||
|
||||
@@ -173,12 +176,16 @@ fpi_ssm_set_data (FpiSsm *machine,
|
||||
void *
|
||||
fpi_ssm_get_data (FpiSsm *machine)
|
||||
{
|
||||
g_return_val_if_fail (machine, NULL);
|
||||
|
||||
return machine->ssm_data;
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_ssm_clear_delayed_action (FpiSsm *machine)
|
||||
{
|
||||
g_return_if_fail (machine);
|
||||
|
||||
if (machine->cancellable_id)
|
||||
{
|
||||
g_cancellable_disconnect (machine->cancellable, machine->cancellable_id);
|
||||
@@ -235,6 +242,8 @@ fpi_ssm_set_delayed_action_timeout (FpiSsm *machine,
|
||||
gpointer user_data,
|
||||
GDestroyNotify destroy_func)
|
||||
{
|
||||
g_return_if_fail (machine);
|
||||
|
||||
BUG_ON (machine->completed);
|
||||
BUG_ON (machine->timeout != NULL);
|
||||
|
||||
@@ -302,6 +311,8 @@ __ssm_call_handler (FpiSsm *machine)
|
||||
void
|
||||
fpi_ssm_start (FpiSsm *ssm, FpiSsmCompletedCallback callback)
|
||||
{
|
||||
g_return_if_fail (ssm != NULL);
|
||||
|
||||
BUG_ON (!ssm->completed);
|
||||
ssm->callback = callback;
|
||||
ssm->cur_state = 0;
|
||||
@@ -336,6 +347,9 @@ __subsm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
|
||||
void
|
||||
fpi_ssm_start_subsm (FpiSsm *parent, FpiSsm *child)
|
||||
{
|
||||
g_return_if_fail (parent != NULL);
|
||||
g_return_if_fail (child != NULL);
|
||||
|
||||
BUG_ON (parent->timeout);
|
||||
child->parentsm = parent;
|
||||
|
||||
@@ -355,6 +369,8 @@ fpi_ssm_start_subsm (FpiSsm *parent, FpiSsm *child)
|
||||
void
|
||||
fpi_ssm_mark_completed (FpiSsm *machine)
|
||||
{
|
||||
g_return_if_fail (machine != NULL);
|
||||
|
||||
BUG_ON (machine->completed);
|
||||
BUG_ON (machine->timeout != NULL);
|
||||
|
||||
@@ -427,6 +443,7 @@ fpi_ssm_mark_completed_delayed (FpiSsm *machine,
|
||||
void
|
||||
fpi_ssm_mark_failed (FpiSsm *machine, GError *error)
|
||||
{
|
||||
g_return_if_fail (machine != NULL);
|
||||
g_assert (error);
|
||||
if (machine->error)
|
||||
{
|
||||
@@ -534,6 +551,8 @@ fpi_ssm_next_state_delayed (FpiSsm *machine,
|
||||
void
|
||||
fpi_ssm_jump_to_state (FpiSsm *machine, int state)
|
||||
{
|
||||
g_return_if_fail (machine != NULL);
|
||||
|
||||
BUG_ON (machine->completed);
|
||||
BUG_ON (state < 0 || state >= machine->nr_states);
|
||||
BUG_ON (machine->timeout != NULL);
|
||||
@@ -610,6 +629,8 @@ fpi_ssm_jump_to_state_delayed (FpiSsm *machine,
|
||||
int
|
||||
fpi_ssm_get_cur_state (FpiSsm *machine)
|
||||
{
|
||||
g_return_val_if_fail (machine != NULL, 0);
|
||||
|
||||
return machine->cur_state;
|
||||
}
|
||||
|
||||
@@ -624,6 +645,8 @@ fpi_ssm_get_cur_state (FpiSsm *machine)
|
||||
GError *
|
||||
fpi_ssm_get_error (FpiSsm *machine)
|
||||
{
|
||||
g_return_val_if_fail (machine != NULL, NULL);
|
||||
|
||||
return machine->error;
|
||||
}
|
||||
|
||||
@@ -638,6 +661,8 @@ fpi_ssm_get_error (FpiSsm *machine)
|
||||
GError *
|
||||
fpi_ssm_dup_error (FpiSsm *machine)
|
||||
{
|
||||
g_return_val_if_fail (machine != NULL, NULL);
|
||||
|
||||
if (machine->error)
|
||||
return g_error_copy (machine->error);
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ fpi_usb_transfer_fill_bulk (FpiUsbTransfer *transfer,
|
||||
* fpi_usb_transfer_fill_bulk_full:
|
||||
* @transfer: The #FpiUsbTransfer
|
||||
* @endpoint: The endpoint to send the transfer to
|
||||
* @buffer: The data to send. A buffer will be created and managed for you if you pass NULL.
|
||||
* @buffer: The data to send.
|
||||
* @length: The size of @buffer
|
||||
* @free_func: (destroy buffer): Destroy notify for @buffer
|
||||
*
|
||||
@@ -275,7 +275,7 @@ fpi_usb_transfer_fill_interrupt (FpiUsbTransfer *transfer,
|
||||
* fpi_usb_transfer_fill_interrupt_full:
|
||||
* @transfer: The #FpiUsbTransfer
|
||||
* @endpoint: The endpoint to send the transfer to
|
||||
* @buffer: The data to send. A buffer will be created and managed for you if you pass NULL.
|
||||
* @buffer: The data to send.
|
||||
* @length: The size of @buffer
|
||||
* @free_func: (destroy buffer): Destroy notify for @buffer
|
||||
*
|
||||
|
||||
@@ -45,12 +45,9 @@ static const FpIdEntry whitelist_id_table[] = {
|
||||
{ .vid = 0x06cb, .pid = 0x00b7 },
|
||||
{ .vid = 0x06cb, .pid = 0x00bb },
|
||||
{ .vid = 0x06cb, .pid = 0x00be },
|
||||
{ .vid = 0x06cb, .pid = 0x00c2 },
|
||||
{ .vid = 0x06cb, .pid = 0x00c9 },
|
||||
{ .vid = 0x06cb, .pid = 0x00cb },
|
||||
{ .vid = 0x06cb, .pid = 0x00d8 },
|
||||
{ .vid = 0x06cb, .pid = 0x00da },
|
||||
{ .vid = 0x06cb, .pid = 0x00e7 },
|
||||
{ .vid = 0x0a5c, .pid = 0x5801 },
|
||||
{ .vid = 0x0a5c, .pid = 0x5805 },
|
||||
{ .vid = 0x0a5c, .pid = 0x5834 },
|
||||
@@ -110,6 +107,7 @@ static const FpIdEntry blacklist_id_table[] = {
|
||||
static const FpDeviceClass whitelist = {
|
||||
.type = FP_DEVICE_TYPE_USB,
|
||||
.id_table = whitelist_id_table,
|
||||
.id = "whitelist",
|
||||
.full_name = "Hardcoded whitelist"
|
||||
};
|
||||
|
||||
@@ -140,33 +138,56 @@ print_driver (const FpDeviceClass *cls)
|
||||
|
||||
if (g_hash_table_lookup (printed, key) != NULL)
|
||||
{
|
||||
if (cls == &whitelist)
|
||||
g_warning ("%s implemented by driver %s",
|
||||
key, (const char *) g_hash_table_lookup (printed, key));
|
||||
g_free (key);
|
||||
continue;
|
||||
}
|
||||
|
||||
g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
|
||||
g_hash_table_insert (printed, key, (void *) cls->id);
|
||||
|
||||
if (num_printed == 0)
|
||||
g_print ("# %s\n", cls->full_name);
|
||||
{
|
||||
if (cls != &whitelist)
|
||||
g_print ("\n# Supported by libfprint driver %s\n", cls->id);
|
||||
else
|
||||
g_print ("\n# Known unsupported devices\n");
|
||||
}
|
||||
|
||||
g_print ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n",
|
||||
g_print ("usb:v%04Xp%04X*\n",
|
||||
entry->vid, entry->pid);
|
||||
g_print ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n",
|
||||
entry->vid, entry->pid, cls->full_name);
|
||||
num_printed++;
|
||||
}
|
||||
|
||||
if (num_printed > 0)
|
||||
g_print ("\n");
|
||||
g_print (" ID_AUTOSUSPEND=1\n");
|
||||
}
|
||||
|
||||
static int
|
||||
driver_compare (gconstpointer p1, gconstpointer p2)
|
||||
{
|
||||
g_autoptr(FpDeviceClass) cls1 = g_type_class_ref (*(GType *) p1);
|
||||
g_autoptr(FpDeviceClass) cls2 = g_type_class_ref (*(GType *) p2);
|
||||
|
||||
return g_strcmp0 (cls1->id, cls2->id);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_autoptr(GArray) drivers = fpi_get_driver_types ();
|
||||
g_autofree char *program_name = NULL;
|
||||
guint i;
|
||||
|
||||
program_name = g_path_get_basename (argv[0]);
|
||||
|
||||
g_print ("# SPDX-License-Identifier: LGPL-2.1-or-later\n");
|
||||
g_print ("# This file has been generated using %s with all drivers enabled\n",
|
||||
program_name);
|
||||
|
||||
printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
g_array_sort (drivers, driver_compare);
|
||||
|
||||
for (i = 0; i < drivers->len; i++)
|
||||
{
|
||||
+30
-11
@@ -155,6 +155,15 @@ foreach driver: drivers
|
||||
if driver == 'virtual_image'
|
||||
drivers_sources += [ 'drivers/virtual-image.c' ]
|
||||
endif
|
||||
if driver == 'virtual_device'
|
||||
drivers_sources += [ 'drivers/virtual-device.c' ]
|
||||
endif
|
||||
if driver == 'virtual_device_storage'
|
||||
drivers_sources += [ 'drivers/virtual-device-storage.c' ]
|
||||
endif
|
||||
if driver.startswith('virtual_')
|
||||
drivers_sources += [ 'drivers/virtual-device-listener.c' ]
|
||||
endif
|
||||
if driver == 'synaptics'
|
||||
drivers_sources += [
|
||||
'drivers/synaptics/synaptics.c',
|
||||
@@ -262,7 +271,7 @@ libfprint_drivers = static_library('fprint-drivers',
|
||||
mapfile = files('libfprint.ver')
|
||||
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.source_root(), mapfile[0])
|
||||
|
||||
libfprint = library(versioned_libname.split('lib')[1],
|
||||
libfprint = shared_library(versioned_libname.split('lib')[1],
|
||||
sources: [
|
||||
fp_enums,
|
||||
libfprint_sources,
|
||||
@@ -299,20 +308,30 @@ libfprint_private_dep = declare_dependency(
|
||||
]
|
||||
)
|
||||
|
||||
udev_rules = executable('fprint-list-udev-rules',
|
||||
'fprint-list-udev-rules.c',
|
||||
udev_hwdb = executable('fprint-list-udev-hwdb',
|
||||
'fprint-list-udev-hwdb.c',
|
||||
dependencies: libfprint_private_dep,
|
||||
link_with: libfprint_drivers,
|
||||
install: false)
|
||||
|
||||
if get_option('udev_rules')
|
||||
custom_target('udev-rules',
|
||||
output: '60-@0@-autosuspend.rules'.format(versioned_libname),
|
||||
capture: true,
|
||||
command: [ udev_rules ],
|
||||
install: true,
|
||||
install_dir: udev_rules_dir)
|
||||
endif
|
||||
udev_hwdb_generator = custom_target('udev-hwdb',
|
||||
output: 'autosuspend.hwdb',
|
||||
depend_files: drivers_sources,
|
||||
capture: true,
|
||||
command: [ udev_hwdb ],
|
||||
install: false,
|
||||
)
|
||||
|
||||
custom_target('sync-udev-hwdb',
|
||||
depends: udev_hwdb_generator,
|
||||
output: 'sync-udev-hwdb',
|
||||
install: false,
|
||||
command: [
|
||||
'cp', '-v',
|
||||
udev_hwdb_generator.full_path(),
|
||||
meson.source_root() / 'data'
|
||||
]
|
||||
)
|
||||
|
||||
supported_devices = executable('fprint-list-supported-devices',
|
||||
'fprint-list-supported-devices.c',
|
||||
|
||||
Reference in New Issue
Block a user