Refactor Validity Driver Code for Improved Readability and Maintainability

- Introduced helper functions in validity_tls.c to encapsulate command sending and response handling for TLS flash read and handshake processes, reducing code duplication and enhancing clarity.
- Reorganized the validity_verify.c file by creating dedicated functions for each step in the verification process, including capture building, matching, and result retrieval, to streamline the state machine logic.
- Enhanced the delete and clear operations in validity_verify.c by modularizing the code into smaller functions, improving readability and maintainability.
- Updated the list operations in validity_verify.c to utilize helper functions for fetching user storage and user details, simplifying the state management.
- Overall, these changes aim to improve the structure of the code, making it easier to follow and modify in the future.
This commit is contained in:
Leonardo Francisco 2026-04-09 20:36:45 -04:00 committed by lewohart
parent 1971829873
commit 65277fce2f
6 changed files with 3446 additions and 2501 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -376,297 +376,404 @@ fwext_upload_data_free (gpointer data)
g_free (ud);
}
static void
fwext_send_write_hw_reg (FpiSsm *ssm,
FpiDeviceValidity *self)
{
guint8 cmd[10];
gsize cmd_len;
validity_fwext_build_write_hw_reg32 (FWEXT_HW_REG_WRITE_ADDR,
FWEXT_HW_REG_WRITE_VALUE,
cmd, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
static void
fwext_recv_write_hw_reg (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"WRITE_HW_REG failed: status=0x%04x",
self->cmd_response_status));
return;
}
fpi_ssm_next_state (ssm);
}
static void
fwext_send_read_hw_reg (FpiSsm *ssm,
FpiDeviceValidity *self)
{
guint8 cmd[6];
gsize cmd_len;
validity_fwext_build_read_hw_reg32 (FWEXT_HW_REG_READ_ADDR,
cmd, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
static void
fwext_recv_read_hw_reg (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"READ_HW_REG failed: status=0x%04x",
self->cmd_response_status));
return;
}
guint32 value;
if (!validity_fwext_parse_read_hw_reg32 (self->cmd_response_data,
self->cmd_response_len,
&value))
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"READ_HW_REG response too short"));
return;
}
if (value != 2 && value != 3)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Unexpected HW register value: %u "
"(expected 2 or 3)", value));
return;
}
fp_dbg ("FWEXT: HW register 0x%08x = %u (OK)", FWEXT_HW_REG_READ_ADDR, value);
fpi_ssm_next_state (ssm);
}
static void
fwext_load_file (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FpDevice *dev = FP_DEVICE (self);
FwextUploadData *ud = fpi_ssm_get_data (ssm);
GError *error = NULL;
GUsbDevice *usb_dev = fpi_device_get_usb_device (dev);
guint16 vid = g_usb_device_get_vid (usb_dev);
guint16 pid = g_usb_device_get_pid (usb_dev);
ud->vid = vid;
ud->pid = pid;
g_autofree gchar *fw_path = validity_fwext_find_firmware (vid, pid, &error);
if (fw_path == NULL)
{
fpi_ssm_mark_failed (ssm, error);
return;
}
if (!validity_fwext_load_file (fw_path, &ud->fwext, &error))
{
fpi_ssm_mark_failed (ssm, error);
return;
}
ud->write_offset = 0;
fp_info ("FWEXT: Loaded firmware file, %zu bytes to write",
ud->fwext.payload_len);
fpi_ssm_next_state (ssm);
}
static void
fwext_send_db_write_enable (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FwextUploadData *ud = fpi_ssm_get_data (ssm);
gsize dbe_len;
const guint8 *dbe = validity_fwext_get_db_write_enable (ud->vid,
ud->pid,
&dbe_len);
if (dbe == NULL || dbe_len == 0)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED,
"No db_write_enable blob for "
"%04x:%04x", ud->vid, ud->pid));
return;
}
vcsfw_tls_cmd_send (self, ssm, dbe, dbe_len, NULL);
}
static void
fwext_recv_db_write_enable (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"db_write_enable failed: "
"status=0x%04x",
self->cmd_response_status));
return;
}
fpi_ssm_next_state (ssm);
}
static void
fwext_send_write_chunk (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FwextUploadData *ud = fpi_ssm_get_data (ssm);
gsize remaining = ud->fwext.payload_len - ud->write_offset;
gsize chunk_size = MIN (remaining, FWEXT_CHUNK_SIZE);
/* cmd buffer: 13-byte header + payload */
g_autofree guint8 *cmd = g_malloc (13 + chunk_size);
gsize cmd_len;
validity_fwext_build_write_flash (FWEXT_PARTITION,
(guint32) ud->write_offset,
ud->fwext.payload + ud->write_offset,
chunk_size,
cmd, &cmd_len);
fp_dbg ("FWEXT: Writing chunk at offset 0x%zx (%zu/%zu bytes)",
ud->write_offset, ud->write_offset + chunk_size,
ud->fwext.payload_len);
ud->write_offset += chunk_size;
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
static void
fwext_recv_write_chunk (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"WRITE_FLASH chunk failed: "
"status=0x%04x",
self->cmd_response_status));
return;
}
fpi_ssm_next_state (ssm);
}
static void
fwext_recv_cleanup (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FwextUploadData *ud = fpi_ssm_get_data (ssm);
/* Status 0x0491 means "nothing to commit" -- not an error */
if (self->cmd_response_status != VCSFW_STATUS_OK &&
self->cmd_response_status != 0x0491)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Cleanup cmd failed: "
"status=0x%04x",
self->cmd_response_status));
return;
}
if (ud->write_offset < ud->fwext.payload_len)
/* More chunks to write -- loop back to db_write_enable */
fpi_ssm_jump_to_state (ssm, FWEXT_SEND_DB_WRITE_ENABLE);
else
/* All chunks written -- proceed to signature */
fpi_ssm_next_state (ssm);
}
static void
fwext_send_write_signature (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FwextUploadData *ud = fpi_ssm_get_data (ssm);
guint8 cmd[5 + FWEXT_SIGNATURE_SIZE];
gsize cmd_len;
validity_fwext_build_write_fw_sig (FWEXT_PARTITION,
ud->fwext.signature,
FWEXT_SIGNATURE_SIZE,
cmd, &cmd_len);
fp_info ("FWEXT: Writing firmware signature (%d bytes)",
FWEXT_SIGNATURE_SIZE);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
static void
fwext_recv_write_signature (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"WRITE_FW_SIG failed: "
"status=0x%04x",
self->cmd_response_status));
return;
}
fpi_ssm_next_state (ssm);
}
static void
fwext_recv_verify (FpiSsm *ssm,
FpiDeviceValidity *self)
{
ValidityFwInfo info;
if (!validity_fwext_parse_fw_info (self->cmd_response_data,
self->cmd_response_len,
self->cmd_response_status,
&info) ||
!info.loaded)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Firmware not detected after upload"));
return;
}
fp_info ("FWEXT: Upload verified -- firmware v%d.%d, %d modules",
info.major, info.minor, info.module_count);
fpi_ssm_next_state (ssm);
}
static void
fwext_send_reboot (FpiSsm *ssm,
FpiDeviceValidity *self)
{
guint8 cmd[3];
gsize cmd_len;
validity_fwext_build_reboot (cmd, &cmd_len);
fp_info ("FWEXT: Rebooting sensor to activate new firmware");
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
static void
fwext_recv_reboot (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* Sensor will disconnect and re-enumerate on USB.
* We mark SSM completed -- the caller (open sequence)
* handles the post-reboot re-init. */
fp_info ("FWEXT: Reboot sent. Device will re-enumerate.");
fpi_ssm_mark_completed (ssm);
}
static void
fwext_send_cleanup (FpiSsm *ssm,
FpiDeviceValidity *self)
{
guint8 cmd[] = { VCSFW_CMD_CLEANUP };
vcsfw_tls_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
static void
fwext_send_verify (FpiSsm *ssm,
FpiDeviceValidity *self)
{
guint8 cmd[] = { VCSFW_CMD_GET_FW_INFO, FWEXT_PARTITION };
vcsfw_tls_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
void
validity_fwext_upload_run_state (FpiSsm *ssm,
FpDevice *dev)
{
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (dev);
FwextUploadData *ud = fpi_ssm_get_data (ssm);
switch (fpi_ssm_get_cur_state (ssm))
{
case FWEXT_SEND_WRITE_HW_REG:
{
guint8 cmd[10];
gsize cmd_len;
validity_fwext_build_write_hw_reg32 (FWEXT_HW_REG_WRITE_ADDR,
FWEXT_HW_REG_WRITE_VALUE,
cmd, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
fwext_send_write_hw_reg (ssm, self);
break;
case FWEXT_RECV_WRITE_HW_REG:
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"WRITE_HW_REG failed: status=0x%04x",
self->cmd_response_status));
return;
}
fpi_ssm_next_state (ssm);
fwext_recv_write_hw_reg (ssm, self);
break;
case FWEXT_SEND_READ_HW_REG:
{
guint8 cmd[6];
gsize cmd_len;
validity_fwext_build_read_hw_reg32 (FWEXT_HW_REG_READ_ADDR,
cmd, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
fwext_send_read_hw_reg (ssm, self);
break;
case FWEXT_RECV_READ_HW_REG:
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"READ_HW_REG failed: status=0x%04x",
self->cmd_response_status));
return;
}
guint32 value;
if (!validity_fwext_parse_read_hw_reg32 (self->cmd_response_data,
self->cmd_response_len,
&value))
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"READ_HW_REG response too short"));
return;
}
if (value != 2 && value != 3)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Unexpected HW register value: %u "
"(expected 2 or 3)", value));
return;
}
fp_dbg ("FWEXT: HW register 0x%08x = %u (OK)", FWEXT_HW_REG_READ_ADDR, value);
fpi_ssm_next_state (ssm);
}
fwext_recv_read_hw_reg (ssm, self);
break;
case FWEXT_LOAD_FILE:
{
GError *error = NULL;
GUsbDevice *usb_dev = fpi_device_get_usb_device (dev);
guint16 vid = g_usb_device_get_vid (usb_dev);
guint16 pid = g_usb_device_get_pid (usb_dev);
ud->vid = vid;
ud->pid = pid;
g_autofree gchar *fw_path = validity_fwext_find_firmware (vid, pid, &error);
if (fw_path == NULL)
{
fpi_ssm_mark_failed (ssm, error);
return;
}
if (!validity_fwext_load_file (fw_path, &ud->fwext, &error))
{
fpi_ssm_mark_failed (ssm, error);
return;
}
ud->write_offset = 0;
fp_info ("FWEXT: Loaded firmware file, %zu bytes to write",
ud->fwext.payload_len);
fpi_ssm_next_state (ssm);
}
fwext_load_file (ssm, self);
break;
case FWEXT_SEND_DB_WRITE_ENABLE:
{
gsize dbe_len;
const guint8 *dbe = validity_fwext_get_db_write_enable (ud->vid,
ud->pid,
&dbe_len);
if (dbe == NULL || dbe_len == 0)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED,
"No db_write_enable blob for "
"%04x:%04x", ud->vid, ud->pid));
return;
}
vcsfw_tls_cmd_send (self, ssm, dbe, dbe_len, NULL);
}
fwext_send_db_write_enable (ssm, self);
break;
case FWEXT_RECV_DB_WRITE_ENABLE:
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"db_write_enable failed: "
"status=0x%04x",
self->cmd_response_status));
return;
}
fpi_ssm_next_state (ssm);
fwext_recv_db_write_enable (ssm, self);
break;
case FWEXT_SEND_WRITE_CHUNK:
{
gsize remaining = ud->fwext.payload_len - ud->write_offset;
gsize chunk_size = MIN (remaining, FWEXT_CHUNK_SIZE);
/* cmd buffer: 13-byte header + payload */
g_autofree guint8 *cmd = g_malloc (13 + chunk_size);
gsize cmd_len;
validity_fwext_build_write_flash (FWEXT_PARTITION,
(guint32) ud->write_offset,
ud->fwext.payload + ud->write_offset,
chunk_size,
cmd, &cmd_len);
fp_dbg ("FWEXT: Writing chunk at offset 0x%zx (%zu/%zu bytes)",
ud->write_offset, ud->write_offset + chunk_size,
ud->fwext.payload_len);
ud->write_offset += chunk_size;
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
fwext_send_write_chunk (ssm, self);
break;
case FWEXT_RECV_WRITE_CHUNK:
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"WRITE_FLASH chunk failed: "
"status=0x%04x",
self->cmd_response_status));
return;
}
fpi_ssm_next_state (ssm);
fwext_recv_write_chunk (ssm, self);
break;
case FWEXT_SEND_CLEANUP:
{
guint8 cmd[] = { VCSFW_CMD_CLEANUP };
vcsfw_tls_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
fwext_send_cleanup (ssm, self);
break;
case FWEXT_RECV_CLEANUP:
/* Status 0x0491 means "nothing to commit" -- not an error */
if (self->cmd_response_status != VCSFW_STATUS_OK &&
self->cmd_response_status != 0x0491)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Cleanup cmd failed: "
"status=0x%04x",
self->cmd_response_status));
return;
}
if (ud->write_offset < ud->fwext.payload_len)
/* More chunks to write -- loop back to db_write_enable */
fpi_ssm_jump_to_state (ssm, FWEXT_SEND_DB_WRITE_ENABLE);
else
/* All chunks written -- proceed to signature */
fpi_ssm_next_state (ssm);
fwext_recv_cleanup (ssm, self);
break;
case FWEXT_SEND_WRITE_SIGNATURE:
{
guint8 cmd[5 + FWEXT_SIGNATURE_SIZE];
gsize cmd_len;
validity_fwext_build_write_fw_sig (FWEXT_PARTITION,
ud->fwext.signature,
FWEXT_SIGNATURE_SIZE,
cmd, &cmd_len);
fp_info ("FWEXT: Writing firmware signature (%d bytes)",
FWEXT_SIGNATURE_SIZE);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
fwext_send_write_signature (ssm, self);
break;
case FWEXT_RECV_WRITE_SIGNATURE:
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"WRITE_FW_SIG failed: "
"status=0x%04x",
self->cmd_response_status));
return;
}
fpi_ssm_next_state (ssm);
fwext_recv_write_signature (ssm, self);
break;
case FWEXT_SEND_VERIFY:
{
guint8 cmd[] = { VCSFW_CMD_GET_FW_INFO, FWEXT_PARTITION };
vcsfw_tls_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
fwext_send_verify (ssm, self);
break;
case FWEXT_RECV_VERIFY:
{
ValidityFwInfo info;
if (!validity_fwext_parse_fw_info (self->cmd_response_data,
self->cmd_response_len,
self->cmd_response_status,
&info) ||
!info.loaded)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Firmware not detected after upload"));
return;
}
fp_info ("FWEXT: Upload verified -- firmware v%d.%d, %d modules",
info.major, info.minor, info.module_count);
fpi_ssm_next_state (ssm);
}
fwext_recv_verify (ssm, self);
break;
case FWEXT_SEND_REBOOT:
{
guint8 cmd[3];
gsize cmd_len;
validity_fwext_build_reboot (cmd, &cmd_len);
fp_info ("FWEXT: Rebooting sensor to activate new firmware");
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
fwext_send_reboot (ssm, self);
break;
case FWEXT_RECV_REBOOT:
/* Sensor will disconnect and re-enumerate on USB.
* We mark SSM completed -- the caller (open sequence)
* handles the post-reboot re-init. */
fp_info ("FWEXT: Reboot sent. Device will re-enumerate.");
fpi_ssm_mark_completed (ssm);
fwext_recv_reboot (ssm, self);
break;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1725,6 +1725,23 @@ validity_tls_parse_server_finish (ValidityTlsState *tls,
* TLS Flash Read SSM (reads flash partition 1 over USB)
* ================================================================ */
static void
tls_flash_read_cmd (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* READ_FLASH(partition=1, offset=0, size=0x1000)
* Format from python-validity: pack('<BBBHLL', 0x40, partition, 1, 0, addr, size) */
guint8 cmd[13];
cmd[0] = VCSFW_CMD_READ_FLASH;
cmd[1] = 0x01; /* partition */
cmd[2] = 0x01; /* access flag */
FP_WRITE_UINT16_LE (&cmd[3], 0x0000); /* reserved */
FP_WRITE_UINT32_LE (&cmd[5], 0x0000); /* offset */
FP_WRITE_UINT32_LE (&cmd[9], 0x1000); /* size */
vcsfw_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
void
validity_tls_flash_read_run_state (FpiSsm *ssm,
FpDevice *dev)
@ -1734,18 +1751,7 @@ validity_tls_flash_read_run_state (FpiSsm *ssm,
switch (fpi_ssm_get_cur_state (ssm))
{
case TLS_FLASH_READ_CMD:
{
/* READ_FLASH(partition=1, offset=0, size=0x1000)
* Format from python-validity: pack('<BBBHLL', 0x40, partition, 1, 0, addr, size) */
guint8 cmd[13];
cmd[0] = VCSFW_CMD_READ_FLASH;
cmd[1] = 0x01; /* partition */
cmd[2] = 0x01; /* access flag */
FP_WRITE_UINT16_LE (&cmd[3], 0x0000); /* reserved */
FP_WRITE_UINT32_LE (&cmd[5], 0x0000); /* offset */
FP_WRITE_UINT32_LE (&cmd[9], 0x1000); /* size */
vcsfw_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
tls_flash_read_cmd (ssm, self);
break;
case TLS_FLASH_READ_RECV:
@ -1817,111 +1823,154 @@ tls_raw_recv_cb (FpiUsbTransfer *transfer,
fpi_ssm_next_state (transfer->ssm);
}
static void
tls_hs_send_client_hello (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FpDevice *dev = FP_DEVICE (self);
FpiUsbTransfer *transfer;
gsize cmd_len;
guint8 *cmd = validity_tls_build_client_hello (&self->tls, &cmd_len);
transfer = fpi_usb_transfer_new (dev);
transfer->short_is_error = TRUE;
transfer->ssm = ssm;
fpi_usb_transfer_fill_bulk (transfer, VALIDITY_EP_CMD_OUT, cmd_len);
memcpy (transfer->buffer, cmd, cmd_len);
g_free (cmd);
fpi_usb_transfer_submit (transfer, VALIDITY_USB_TIMEOUT, NULL,
fpi_ssm_usb_transfer_cb, NULL);
}
static void
tls_hs_recv_server_hello (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FpDevice *dev = FP_DEVICE (self);
FpiUsbTransfer *transfer;
/* Receive raw TLS records (ServerHello + CertReq + ServerHelloDone) */
transfer = fpi_usb_transfer_new (dev);
transfer->ssm = ssm;
fpi_usb_transfer_fill_bulk (transfer, VALIDITY_EP_CMD_IN,
VALIDITY_MAX_TRANSFER_LEN);
fpi_usb_transfer_submit (transfer, VALIDITY_USB_TIMEOUT, NULL,
tls_raw_recv_cb, NULL);
}
static void
tls_hs_send_client_finish (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FpDevice *dev = FP_DEVICE (self);
FpiUsbTransfer *transfer;
/* Parse the stored ServerHello response (synchronous) */
GError *error = NULL;
if (!self->cmd_response_data)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"TLS handshake: no ServerHello response"));
return;
}
if (!validity_tls_parse_server_hello (&self->tls,
self->cmd_response_data,
self->cmd_response_len,
&error))
{
fpi_ssm_mark_failed (ssm, error);
return;
}
/* Build and send Certificate + KeyExchange + CertVerify + CCS + Finished */
gsize cmd_len;
guint8 *cmd = validity_tls_build_client_finish (&self->tls, &cmd_len);
transfer = fpi_usb_transfer_new (dev);
transfer->short_is_error = TRUE;
transfer->ssm = ssm;
fpi_usb_transfer_fill_bulk (transfer, VALIDITY_EP_CMD_OUT, cmd_len);
memcpy (transfer->buffer, cmd, cmd_len);
g_free (cmd);
fpi_usb_transfer_submit (transfer, VALIDITY_USB_TIMEOUT, NULL,
fpi_ssm_usb_transfer_cb, NULL);
}
static void
tls_hs_recv_server_finish (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FpDevice *dev = FP_DEVICE (self);
FpiUsbTransfer *transfer;
/* Receive raw TLS records (CCS + encrypted Finished) */
transfer = fpi_usb_transfer_new (dev);
transfer->ssm = ssm;
fpi_usb_transfer_fill_bulk (transfer, VALIDITY_EP_CMD_IN,
VALIDITY_MAX_TRANSFER_LEN);
fpi_usb_transfer_submit (transfer, VALIDITY_USB_TIMEOUT, NULL,
tls_raw_recv_cb, NULL);
}
static void
tls_hs_parse_server_finish (FpiSsm *ssm,
FpiDeviceValidity *self)
{
GError *error = NULL;
if (!self->cmd_response_data)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"TLS handshake: no ServerFinish response"));
return;
}
if (!validity_tls_parse_server_finish (&self->tls,
self->cmd_response_data,
self->cmd_response_len,
&error))
{
fpi_ssm_mark_failed (ssm, error);
return;
}
fp_info ("TLS session established (secure_rx=%d secure_tx=%d)",
self->tls.secure_rx, self->tls.secure_tx);
fpi_ssm_mark_completed (ssm);
}
void
validity_tls_handshake_run_state (FpiSsm *ssm,
FpDevice *dev)
{
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (dev);
FpiUsbTransfer *transfer;
switch (fpi_ssm_get_cur_state (ssm))
{
case TLS_HS_SEND_CLIENT_HELLO:
{
gsize cmd_len;
guint8 *cmd = validity_tls_build_client_hello (&self->tls, &cmd_len);
transfer = fpi_usb_transfer_new (dev);
transfer->short_is_error = TRUE;
transfer->ssm = ssm;
fpi_usb_transfer_fill_bulk (transfer, VALIDITY_EP_CMD_OUT, cmd_len);
memcpy (transfer->buffer, cmd, cmd_len);
g_free (cmd);
fpi_usb_transfer_submit (transfer, VALIDITY_USB_TIMEOUT, NULL,
fpi_ssm_usb_transfer_cb, NULL);
}
tls_hs_send_client_hello (ssm, self);
break;
case TLS_HS_RECV_SERVER_HELLO:
/* Receive raw TLS records (ServerHello + CertReq + ServerHelloDone) */
transfer = fpi_usb_transfer_new (dev);
transfer->ssm = ssm;
fpi_usb_transfer_fill_bulk (transfer, VALIDITY_EP_CMD_IN,
VALIDITY_MAX_TRANSFER_LEN);
fpi_usb_transfer_submit (transfer, VALIDITY_USB_TIMEOUT, NULL,
tls_raw_recv_cb, NULL);
tls_hs_recv_server_hello (ssm, self);
break;
case TLS_HS_SEND_CLIENT_FINISH:
{
/* Parse the stored ServerHello response (synchronous) */
GError *error = NULL;
if (!self->cmd_response_data)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"TLS handshake: no ServerHello response"));
return;
}
if (!validity_tls_parse_server_hello (&self->tls,
self->cmd_response_data,
self->cmd_response_len,
&error))
{
fpi_ssm_mark_failed (ssm, error);
return;
}
/* Build and send Certificate + KeyExchange + CertVerify + CCS + Finished */
gsize cmd_len;
guint8 *cmd = validity_tls_build_client_finish (&self->tls, &cmd_len);
transfer = fpi_usb_transfer_new (dev);
transfer->short_is_error = TRUE;
transfer->ssm = ssm;
fpi_usb_transfer_fill_bulk (transfer, VALIDITY_EP_CMD_OUT, cmd_len);
memcpy (transfer->buffer, cmd, cmd_len);
g_free (cmd);
fpi_usb_transfer_submit (transfer, VALIDITY_USB_TIMEOUT, NULL,
fpi_ssm_usb_transfer_cb, NULL);
}
tls_hs_send_client_finish (ssm, self);
break;
case TLS_HS_RECV_SERVER_FINISH:
/* Receive raw TLS records (CCS + encrypted Finished) */
transfer = fpi_usb_transfer_new (dev);
transfer->ssm = ssm;
fpi_usb_transfer_fill_bulk (transfer, VALIDITY_EP_CMD_IN,
VALIDITY_MAX_TRANSFER_LEN);
fpi_usb_transfer_submit (transfer, VALIDITY_USB_TIMEOUT, NULL,
tls_raw_recv_cb, NULL);
tls_hs_recv_server_finish (ssm, self);
break;
case TLS_HS_PARSE_SERVER_FINISH:
{
GError *error = NULL;
if (!self->cmd_response_data)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"TLS handshake: no ServerFinish response"));
return;
}
if (!validity_tls_parse_server_finish (&self->tls,
self->cmd_response_data,
self->cmd_response_len,
&error))
{
fpi_ssm_mark_failed (ssm, error);
return;
}
fp_info ("TLS session established (secure_rx=%d secure_tx=%d)",
self->tls.secure_rx, self->tls.secure_tx);
fpi_ssm_mark_completed (ssm);
}
tls_hs_parse_server_finish (ssm, self);
break;
}
}

View file

@ -317,6 +317,158 @@ validity_find_gallery_match (GPtrArray *gallery,
* Verify/Identify SSM
* ================================================================ */
static void
verify_build_capture (FpiSsm *ssm,
FpiDeviceValidity *self)
{
gsize cmd_len;
guint8 *cmd = validity_capture_build_cmd_02 (&self->capture,
self->sensor.type_info,
VALIDITY_CAPTURE_IDENTIFY,
&cmd_len);
if (!cmd)
{
fp_warn ("Failed to build identify capture command");
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
return;
}
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
verify_capture_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_warn ("Capture (identify) failed: status=0x%04x",
self->cmd_response_status);
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
return;
}
fpi_ssm_next_state (ssm);
}
static void
verify_match_start (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* cmd 0x5E: match_finger */
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_match_finger (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
verify_match_start_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_warn ("match_finger failed: status=0x%04x",
self->cmd_response_status);
/* No match — continue to cleanup */
fpi_ssm_jump_to_state (ssm, VERIFY_CLEANUP);
return;
}
fpi_ssm_next_state (ssm);
}
static void
verify_get_result (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* cmd 0x60: get_match_result */
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_match_result (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
verify_get_result_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_info ("No match found (status=0x%04x)",
self->cmd_response_status);
/* Store no-match indicator */
g_clear_pointer (&self->bulk_data, g_free);
self->bulk_data_len = 0;
}
else if (self->cmd_response_data && self->cmd_response_len > 0)
{
/* Store match result for later reporting */
g_clear_pointer (&self->bulk_data, g_free);
self->bulk_data = g_memdup2 (self->cmd_response_data,
self->cmd_response_len);
self->bulk_data_len = self->cmd_response_len;
}
fpi_ssm_next_state (ssm);
}
static void
verify_cleanup (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* cmd 0x62: match_cleanup */
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_match_cleanup (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
verify_led_on (FpiSsm *ssm,
FpiDeviceValidity *self)
{
gsize cmd_len;
const guint8 *cmd = validity_capture_glow_start_cmd (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
static void
verify_get_prg_status (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* cmd 0x51: get_prg_status2 (after scan complete, before capture stop) */
const guint8 cmd[] = { 0x51, 0x00, 0x20, 0x00, 0x00 };
vcsfw_tls_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
static void
verify_capture_stop (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* cmd 0x04: capture stop/cleanup */
const guint8 cmd[] = { 0x04 };
vcsfw_tls_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
static void
verify_led_off (FpiSsm *ssm,
FpiDeviceValidity *self)
{
gsize cmd_len;
const guint8 *cmd = validity_capture_glow_end_cmd (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
static void
verify_run_state (FpiSsm *ssm,
FpDevice *dev)
@ -326,11 +478,7 @@ verify_run_state (FpiSsm *ssm,
switch (fpi_ssm_get_cur_state (ssm))
{
case VERIFY_LED_ON:
{
gsize cmd_len;
const guint8 *cmd = validity_capture_glow_start_cmd (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
verify_led_on (ssm, self);
break;
case VERIFY_LED_ON_RECV:
@ -338,23 +486,7 @@ verify_run_state (FpiSsm *ssm,
break;
case VERIFY_BUILD_CAPTURE:
{
gsize cmd_len;
guint8 *cmd = validity_capture_build_cmd_02 (&self->capture,
self->sensor.type_info,
VALIDITY_CAPTURE_IDENTIFY,
&cmd_len);
if (!cmd)
{
fp_warn ("Failed to build identify capture command");
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
return;
}
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
verify_build_capture (ssm, self);
break;
case VERIFY_CAPTURE_SEND:
@ -362,17 +494,7 @@ verify_run_state (FpiSsm *ssm,
break;
case VERIFY_CAPTURE_RECV:
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_warn ("Capture (identify) failed: status=0x%04x",
self->cmd_response_status);
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
return;
}
fpi_ssm_next_state (ssm);
}
verify_capture_recv (ssm, self);
break;
case VERIFY_WAIT_FINGER:
@ -384,11 +506,7 @@ verify_run_state (FpiSsm *ssm,
break;
case VERIFY_GET_PRG_STATUS:
{
/* cmd 0x51: get_prg_status2 (after scan complete, before capture stop) */
const guint8 cmd[] = { 0x51, 0x00, 0x20, 0x00, 0x00 };
vcsfw_tls_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
verify_get_prg_status (ssm, self);
break;
case VERIFY_GET_PRG_STATUS_RECV:
@ -397,11 +515,7 @@ verify_run_state (FpiSsm *ssm,
break;
case VERIFY_CAPTURE_STOP:
{
/* cmd 0x04: capture stop/cleanup */
const guint8 cmd[] = { 0x04 };
vcsfw_tls_cmd_send (self, ssm, cmd, sizeof (cmd), NULL);
}
verify_capture_stop (ssm, self);
break;
case VERIFY_CAPTURE_STOP_RECV:
@ -410,27 +524,11 @@ verify_run_state (FpiSsm *ssm,
break;
case VERIFY_MATCH_START:
{
/* cmd 0x5E: match_finger */
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_match_finger (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
verify_match_start (ssm, self);
break;
case VERIFY_MATCH_START_RECV:
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_warn ("match_finger failed: status=0x%04x",
self->cmd_response_status);
/* No match — continue to cleanup */
fpi_ssm_jump_to_state (ssm, VERIFY_CLEANUP);
return;
}
fpi_ssm_next_state (ssm);
}
verify_match_start_recv (ssm, self);
break;
case VERIFY_WAIT_MATCH_INT:
@ -439,46 +537,15 @@ verify_run_state (FpiSsm *ssm,
break;
case VERIFY_GET_RESULT:
{
/* cmd 0x60: get_match_result */
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_match_result (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
verify_get_result (ssm, self);
break;
case VERIFY_GET_RESULT_RECV:
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_info ("No match found (status=0x%04x)",
self->cmd_response_status);
/* Store no-match indicator */
g_clear_pointer (&self->bulk_data, g_free);
self->bulk_data_len = 0;
}
else if (self->cmd_response_data && self->cmd_response_len > 0)
{
/* Store match result for later reporting */
g_clear_pointer (&self->bulk_data, g_free);
self->bulk_data = g_memdup2 (self->cmd_response_data,
self->cmd_response_len);
self->bulk_data_len = self->cmd_response_len;
}
fpi_ssm_next_state (ssm);
}
verify_get_result_recv (ssm, self);
break;
case VERIFY_CLEANUP:
{
/* cmd 0x62: match_cleanup */
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_match_cleanup (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
verify_cleanup (ssm, self);
break;
case VERIFY_CLEANUP_RECV:
@ -487,11 +554,7 @@ verify_run_state (FpiSsm *ssm,
break;
case VERIFY_LED_OFF:
{
gsize cmd_len;
const guint8 *cmd = validity_capture_glow_end_cmd (&cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
}
verify_led_off (ssm, self);
break;
case VERIFY_LED_OFF_RECV:
@ -612,115 +675,138 @@ validity_identify (FpDevice *device)
* List prints enumerate enrolled fingerprints from sensor DB
* ================================================================ */
static void
list_get_storage (FpiSsm *ssm,
FpiDeviceValidity *self)
{
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user_storage (
VALIDITY_STORAGE_NAME, &cmd_len);
self->list_user_idx = 0;
memset (&self->list_storage, 0, sizeof (self->list_storage));
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
list_get_storage_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_info ("No user storage found (status=0x%04x)",
self->cmd_response_status);
fpi_ssm_jump_to_state (ssm, LIST_DONE);
return;
}
if (!self->cmd_response_data ||
!validity_db_parse_user_storage (self->cmd_response_data,
self->cmd_response_len,
&self->list_storage))
{
fp_info ("Failed to parse user storage — no enrolled prints");
fpi_ssm_jump_to_state (ssm, LIST_DONE);
return;
}
fp_info ("Storage '%s': %u users",
self->list_storage.name ? self->list_storage.name : "",
self->list_storage.user_count);
if (self->list_storage.user_count == 0)
{
fpi_ssm_jump_to_state (ssm, LIST_DONE);
return;
}
self->list_user_idx = 0;
fpi_ssm_next_state (ssm);
}
static void
list_get_user (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->list_user_idx >= self->list_storage.user_count)
{
fpi_ssm_jump_to_state (ssm, LIST_DONE);
return;
}
guint16 user_dbid = self->list_storage.user_dbids[self->list_user_idx];
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user (user_dbid, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
list_get_user_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FpDevice *dev = FP_DEVICE (self);
GPtrArray *prints_array = fpi_ssm_get_data (ssm);
if (self->cmd_response_status == VCSFW_STATUS_OK &&
self->cmd_response_data)
{
ValidityUser user = { 0 };
if (validity_db_parse_user (self->cmd_response_data,
self->cmd_response_len,
&user))
{
for (guint16 i = 0; i < user.finger_count; i++)
{
FpPrint *print = fp_print_new (dev);
gint finger = validity_subtype_to_finger (
user.fingers[i].subtype);
fpi_print_set_type (print, FPI_PRINT_RAW);
fpi_print_set_device_stored (print, TRUE);
if (finger >= 0)
fp_print_set_finger (print, (FpFinger) finger);
g_ptr_array_add (prints_array, print);
}
validity_user_clear (&user);
}
}
self->list_user_idx++;
if (self->list_user_idx < self->list_storage.user_count)
fpi_ssm_jump_to_state (ssm, LIST_GET_USER);
else
fpi_ssm_next_state (ssm);
}
static void
list_run_state (FpiSsm *ssm,
FpDevice *dev)
{
FpiDeviceValidity *self = FPI_DEVICE_VALIDITY (dev);
GPtrArray *prints_array = fpi_ssm_get_data (ssm);
switch (fpi_ssm_get_cur_state (ssm))
{
case LIST_GET_STORAGE:
{
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user_storage (
VALIDITY_STORAGE_NAME, &cmd_len);
self->list_user_idx = 0;
memset (&self->list_storage, 0, sizeof (self->list_storage));
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
list_get_storage (ssm, self);
break;
case LIST_GET_STORAGE_RECV:
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_info ("No user storage found (status=0x%04x)",
self->cmd_response_status);
fpi_ssm_jump_to_state (ssm, LIST_DONE);
return;
}
if (!self->cmd_response_data ||
!validity_db_parse_user_storage (self->cmd_response_data,
self->cmd_response_len,
&self->list_storage))
{
fp_info ("Failed to parse user storage — no enrolled prints");
fpi_ssm_jump_to_state (ssm, LIST_DONE);
return;
}
fp_info ("Storage '%s': %u users",
self->list_storage.name ? self->list_storage.name : "",
self->list_storage.user_count);
if (self->list_storage.user_count == 0)
{
fpi_ssm_jump_to_state (ssm, LIST_DONE);
return;
}
self->list_user_idx = 0;
fpi_ssm_next_state (ssm);
}
list_get_storage_recv (ssm, self);
break;
case LIST_GET_USER:
{
if (self->list_user_idx >= self->list_storage.user_count)
{
fpi_ssm_jump_to_state (ssm, LIST_DONE);
return;
}
guint16 user_dbid = self->list_storage.user_dbids[self->list_user_idx];
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user (user_dbid, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
list_get_user (ssm, self);
break;
case LIST_GET_USER_RECV:
{
if (self->cmd_response_status == VCSFW_STATUS_OK &&
self->cmd_response_data)
{
ValidityUser user = { 0 };
if (validity_db_parse_user (self->cmd_response_data,
self->cmd_response_len,
&user))
{
for (guint16 i = 0; i < user.finger_count; i++)
{
FpPrint *print = fp_print_new (dev);
gint finger = validity_subtype_to_finger (
user.fingers[i].subtype);
fpi_print_set_type (print, FPI_PRINT_RAW);
fpi_print_set_device_stored (print, TRUE);
if (finger >= 0)
fp_print_set_finger (print, (FpFinger) finger);
g_ptr_array_add (prints_array, print);
}
validity_user_clear (&user);
}
}
self->list_user_idx++;
if (self->list_user_idx < self->list_storage.user_count)
fpi_ssm_jump_to_state (ssm, LIST_GET_USER);
else
fpi_ssm_next_state (ssm);
}
list_get_user_recv (ssm, self);
break;
case LIST_DONE:
@ -767,6 +853,149 @@ validity_list (FpDevice *device)
* Delete print remove a fingerprint record from the sensor DB
* ================================================================ */
static void
delete_get_storage (FpiSsm *ssm,
FpiDeviceValidity *self)
{
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user_storage (
VALIDITY_STORAGE_NAME, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
delete_get_storage_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
FpDevice *dev = FP_DEVICE (self);
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
return;
}
/* Parse into list_storage (shared with list SSM, not concurrent) */
validity_user_storage_clear (&self->list_storage);
if (!self->cmd_response_data ||
!validity_db_parse_user_storage (self->cmd_response_data,
self->cmd_response_len,
&self->list_storage))
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
return;
}
self->delete_storage_dbid = self->list_storage.dbid;
/* Extract finger subtype from the print to delete */
{
FpPrint *print = NULL;
fpi_device_get_delete_data (dev, &print);
FpFinger finger = fp_print_get_finger (print);
self->delete_finger_subtype = validity_finger_to_subtype (finger);
}
self->list_user_idx = 0;
fpi_ssm_next_state (ssm);
}
static void
delete_lookup_user (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* Look up the user matching the print to delete.
* Iterate users to find one with a matching finger subtype.
* python-validity: db.lookup_user(identity) */
if (self->list_user_idx >= self->list_storage.user_count)
{
/* No matching finger found across all users */
fp_info ("Delete: no matching finger (subtype=%u) found in DB",
self->delete_finger_subtype);
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
return;
}
{
guint16 user_dbid = self->list_storage.user_dbids[self->list_user_idx];
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user (user_dbid, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
}
static void
delete_lookup_user_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* Parse user and look for the finger to delete */
if (self->cmd_response_status == VCSFW_STATUS_OK &&
self->cmd_response_data)
{
ValidityUser user = { 0 };
if (validity_db_parse_user (self->cmd_response_data,
self->cmd_response_len,
&user))
{
for (guint16 i = 0; i < user.finger_count; i++)
{
if (user.fingers[i].subtype == self->delete_finger_subtype)
{
/* Found matching finger — store dbid for deletion */
self->delete_finger_dbid = user.fingers[i].dbid;
validity_user_clear (&user);
fpi_ssm_next_state (ssm);
return;
}
}
validity_user_clear (&user);
}
}
/* Try next user — jump back to DELETE_LOOKUP_USER */
self->list_user_idx++;
fpi_ssm_jump_to_state (ssm, DELETE_LOOKUP_USER);
}
static void
delete_del_record (FpiSsm *ssm,
FpiDeviceValidity *self)
{
/* Delete the finger record via cmd 0x48
* python-validity: db.del_record(dbid) */
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_del_record (
self->delete_finger_dbid, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
delete_del_record_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_warn ("del_record failed: status=0x%04x",
self->cmd_response_status);
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
return;
}
fp_info ("Deleted finger record: dbid=%u", self->delete_finger_dbid);
fpi_ssm_next_state (ssm);
}
static void
delete_run_state (FpiSsm *ssm,
FpDevice *dev)
@ -776,135 +1005,27 @@ delete_run_state (FpiSsm *ssm,
switch (fpi_ssm_get_cur_state (ssm))
{
case DELETE_GET_STORAGE:
{
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user_storage (
VALIDITY_STORAGE_NAME, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
delete_get_storage (ssm, self);
break;
case DELETE_GET_STORAGE_RECV:
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
return;
}
/* Parse into list_storage (shared with list SSM, not concurrent) */
validity_user_storage_clear (&self->list_storage);
if (!self->cmd_response_data ||
!validity_db_parse_user_storage (self->cmd_response_data,
self->cmd_response_len,
&self->list_storage))
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
return;
}
self->delete_storage_dbid = self->list_storage.dbid;
/* Extract finger subtype from the print to delete */
{
FpPrint *print = NULL;
fpi_device_get_delete_data (dev, &print);
FpFinger finger = fp_print_get_finger (print);
self->delete_finger_subtype = validity_finger_to_subtype (finger);
}
self->list_user_idx = 0;
fpi_ssm_next_state (ssm);
}
delete_get_storage_recv (ssm, self);
break;
case DELETE_LOOKUP_USER:
{
/* Look up the user matching the print to delete.
* Iterate users to find one with a matching finger subtype.
* python-validity: db.lookup_user(identity) */
if (self->list_user_idx >= self->list_storage.user_count)
{
/* No matching finger found across all users */
fp_info ("Delete: no matching finger (subtype=%u) found in DB",
self->delete_finger_subtype);
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
return;
}
{
guint16 user_dbid = self->list_storage.user_dbids[self->list_user_idx];
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user (user_dbid, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
}
delete_lookup_user (ssm, self);
break;
case DELETE_LOOKUP_USER_RECV:
{
/* Parse user and look for the finger to delete */
if (self->cmd_response_status == VCSFW_STATUS_OK &&
self->cmd_response_data)
{
ValidityUser user = { 0 };
if (validity_db_parse_user (self->cmd_response_data,
self->cmd_response_len,
&user))
{
for (guint16 i = 0; i < user.finger_count; i++)
{
if (user.fingers[i].subtype == self->delete_finger_subtype)
{
/* Found matching finger — store dbid for deletion */
self->delete_finger_dbid = user.fingers[i].dbid;
validity_user_clear (&user);
fpi_ssm_next_state (ssm);
return;
}
}
validity_user_clear (&user);
}
}
/* Try next user — jump back to DELETE_LOOKUP_USER */
self->list_user_idx++;
fpi_ssm_jump_to_state (ssm, DELETE_LOOKUP_USER);
}
delete_lookup_user_recv (ssm, self);
break;
case DELETE_DEL_RECORD:
{
/* Delete the finger record via cmd 0x48
* python-validity: db.del_record(dbid) */
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_del_record (
self->delete_finger_dbid, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
delete_del_record (ssm, self);
break;
case DELETE_DEL_RECORD_RECV:
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
{
fp_warn ("del_record failed: status=0x%04x",
self->cmd_response_status);
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
return;
}
fp_info ("Deleted finger record: dbid=%u", self->delete_finger_dbid);
fpi_ssm_next_state (ssm);
}
delete_del_record_recv (ssm, self);
break;
case DELETE_DONE:
@ -940,6 +1061,74 @@ validity_delete (FpDevice *device)
* python-validity: for user in db.get_user_storage(): db.del_record(user.dbid)
* ================================================================ */
static void
clear_get_storage (FpiSsm *ssm,
FpiDeviceValidity *self)
{
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user_storage (
VALIDITY_STORAGE_NAME, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
clear_get_storage_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
validity_user_storage_clear (&self->list_storage);
if (self->cmd_response_status != VCSFW_STATUS_OK ||
!self->cmd_response_data ||
!validity_db_parse_user_storage (self->cmd_response_data,
self->cmd_response_len,
&self->list_storage))
{
/* No storage or parse error — nothing to clear */
fpi_ssm_jump_to_state (ssm, CLEAR_DONE);
return;
}
self->list_user_idx = 0;
fpi_ssm_next_state (ssm);
}
static void
clear_del_user (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->list_user_idx >= self->list_storage.user_count)
{
fpi_ssm_jump_to_state (ssm, CLEAR_DONE);
return;
}
guint16 user_dbid = self->list_storage.user_dbids[self->list_user_idx];
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_del_record (user_dbid, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
static void
clear_del_user_recv (FpiSsm *ssm,
FpiDeviceValidity *self)
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
fp_warn ("clear_storage: del_record(dbid=%u) failed: status=0x%04x",
self->list_storage.user_dbids[self->list_user_idx],
self->cmd_response_status);
self->list_user_idx++;
fpi_ssm_jump_to_state (ssm, CLEAR_DEL_USER);
}
static void
clear_run_state (FpiSsm *ssm,
FpDevice *dev)
@ -949,62 +1138,19 @@ clear_run_state (FpiSsm *ssm,
switch (fpi_ssm_get_cur_state (ssm))
{
case CLEAR_GET_STORAGE:
{
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_get_user_storage (
VALIDITY_STORAGE_NAME, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
clear_get_storage (ssm, self);
break;
case CLEAR_GET_STORAGE_RECV:
{
validity_user_storage_clear (&self->list_storage);
if (self->cmd_response_status != VCSFW_STATUS_OK ||
!self->cmd_response_data ||
!validity_db_parse_user_storage (self->cmd_response_data,
self->cmd_response_len,
&self->list_storage))
{
/* No storage or parse error — nothing to clear */
fpi_ssm_jump_to_state (ssm, CLEAR_DONE);
return;
}
self->list_user_idx = 0;
fpi_ssm_next_state (ssm);
}
clear_get_storage_recv (ssm, self);
break;
case CLEAR_DEL_USER:
{
if (self->list_user_idx >= self->list_storage.user_count)
{
fpi_ssm_jump_to_state (ssm, CLEAR_DONE);
return;
}
guint16 user_dbid = self->list_storage.user_dbids[self->list_user_idx];
gsize cmd_len;
guint8 *cmd = validity_db_build_cmd_del_record (user_dbid, &cmd_len);
vcsfw_tls_cmd_send (self, ssm, cmd, cmd_len, NULL);
g_free (cmd);
}
clear_del_user (ssm, self);
break;
case CLEAR_DEL_USER_RECV:
{
if (self->cmd_response_status != VCSFW_STATUS_OK)
fp_warn ("clear_storage: del_record(dbid=%u) failed: status=0x%04x",
self->list_storage.user_dbids[self->list_user_idx],
self->cmd_response_status);
self->list_user_idx++;
fpi_ssm_jump_to_state (ssm, CLEAR_DEL_USER);
}
clear_del_user_recv (ssm, self);
break;
case CLEAR_DONE: