Merge libfprint/libfprint master branch into tod

This commit is contained in:
Marco Trevisan (Treviño)
2021-02-22 19:12:51 +01:00
46 changed files with 3938 additions and 832 deletions
+2
View File
@@ -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 }
*/
+2
View File
@@ -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 }
*/
+116 -12
View File
@@ -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 */
};
+6 -2
View File
@@ -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;
+65 -30
View File
@@ -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)
{
+13 -2
View File
@@ -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);
+1 -1
View File
@@ -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;
+2
View File
@@ -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
+4
View File
@@ -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 *****/
+193 -191
View File
@@ -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 ()
+2
View File
@@ -1,5 +1,7 @@
#pragma once
/* *INDENT-OFF* */
#define VFS5011_LINE_SIZE 240
#define VFS5011_IMAGE_WIDTH 160
+355
View File
@@ -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);
}
+109
View File
@@ -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);
+247
View File
@@ -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;
}
+779
View File
@@ -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;
}
+70 -185
View File
@@ -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
+5 -5
View File
@@ -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
View File
@@ -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.
+25
View File
@@ -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);
+2 -2
View File
@@ -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
View File
@@ -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',