From c7ab8e992c4a32a0e73898e103014dbef5b83277 Mon Sep 17 00:00:00 2001 From: With Mask <1167710-real_with_mask@users.noreply.gitlab.freedesktop.org> Date: Sat, 8 Nov 2025 10:30:27 +0000 Subject: [PATCH 01/27] feat: Add support for Synaptics 06cb:0169 --- libfprint/drivers/synaptics/synaptics.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 8b130937..2c22285c 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -53,6 +53,7 @@ static const FpIdEntry id_table[] = { { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0129, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x015F, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0168, }, + { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0169, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x016C, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0173, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0174, }, From 35956799e7d7011c0a44ca65724e074097a2fdd8 Mon Sep 17 00:00:00 2001 From: With Mask <1167710-real_with_mask@users.noreply.gitlab.freedesktop.org> Date: Sat, 8 Nov 2025 20:57:52 +0000 Subject: [PATCH 02/27] Edit autosuspend.hwdb --- data/autosuspend.hwdb | 1 + 1 file changed, 1 insertion(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index b0804b66..09f63768 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -264,6 +264,7 @@ usb:v06CBp0126* usb:v06CBp0129* usb:v06CBp015F* usb:v06CBp0168* +usb:v06CBp0169* usb:v06CBp016C* usb:v06CBp0173* usb:v06CBp0174* From ebbf806d5ca19d4a679c75354f1668e4e0f67640 Mon Sep 17 00:00:00 2001 From: SilverDiamond Date: Sun, 9 Nov 2025 19:14:24 +0100 Subject: [PATCH 03/27] egismoc: add 1c7a:0588 --- data/autosuspend.hwdb | 1 + libfprint/drivers/egismoc/egismoc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 09f63768..bfb810f1 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -82,6 +82,7 @@ usb:v1C7Ap0582* usb:v1C7Ap0583* usb:v1C7Ap0586* usb:v1C7Ap0587* +usb:v1C7Ap0588* usb:v1C7Ap05A1* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 8f35a67f..f68398e9 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -54,6 +54,7 @@ static const FpIdEntry egismoc_id_table[] = { { .vid = 0x1c7a, .pid = 0x0583, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 }, { .vid = 0x1c7a, .pid = 0x0586, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, { .vid = 0x1c7a, .pid = 0x0587, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, + { .vid = 0x1c7a, .pid = 0x0588, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, { .vid = 0x1c7a, .pid = 0x05a1, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE2 }, { .vid = 0, .pid = 0, .driver_data = 0 } }; From 83367979a13899da12dedb303dd420b5a59a4123 Mon Sep 17 00:00:00 2001 From: Aris Lin Date: Fri, 21 Nov 2025 17:37:57 +0800 Subject: [PATCH 04/27] synaptics: add new PID 0x1A0 --- data/autosuspend.hwdb | 1 + libfprint/drivers/synaptics/synaptics.c | 1 + 2 files changed, 2 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index bfb810f1..49a506d4 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -271,6 +271,7 @@ usb:v06CBp0173* usb:v06CBp0174* usb:v06CBp019D* usb:v06CBp019F* +usb:v06CBp01A0* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 2c22285c..e37a6062 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -59,6 +59,7 @@ static const FpIdEntry id_table[] = { { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0174, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x019D, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x019F, }, + { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x01A0, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; From 1af1acd81c6c42a838533316a7cce5b2af2315d0 Mon Sep 17 00:00:00 2001 From: jialei Date: Wed, 29 Oct 2025 14:32:54 +0800 Subject: [PATCH 05/27] goodixmoc:add new PID 0x66A9 --- data/autosuspend.hwdb | 1 + libfprint/drivers/goodixmoc/goodix.c | 1 + 2 files changed, 2 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 49a506d4..e1c6be3b 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -228,6 +228,7 @@ usb:v27C6p659C* usb:v27C6p6A94* usb:v27C6p6512* usb:v27C6p689A* +usb:v27C6p66A9* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/goodixmoc/goodix.c b/libfprint/drivers/goodixmoc/goodix.c index 659722ae..cccbf4de 100644 --- a/libfprint/drivers/goodixmoc/goodix.c +++ b/libfprint/drivers/goodixmoc/goodix.c @@ -1656,6 +1656,7 @@ static const FpIdEntry id_table[] = { { .vid = 0x27c6, .pid = 0x6A94, }, { .vid = 0x27c6, .pid = 0x6512, }, { .vid = 0x27c6, .pid = 0x689A, }, + { .vid = 0x27c6, .pid = 0x66A9, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; From 9a4faf378e4943ab0a8372f2f9fff58a3d8246d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 13 Oct 2022 23:43:00 +0200 Subject: [PATCH 06/27] libfprint: Use fatal-warnings on g-i-scanner Be strict on introspection checks, so that we are sure to do the things properly. --- libfprint/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/meson.build b/libfprint/meson.build index 0ca17674..34494813 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -410,6 +410,7 @@ if get_option('introspection') 'GObject-2.0', 'GUsb-1.0', ], + fatal_warnings: true, install : true) libfprint_gir = libfprint_girtarget[0] libfprint_typelib = libfprint_girtarget[1] From c2988ae33d93504cc528e7f3d3f28b2542bd621d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 24 Feb 2025 23:10:52 +0100 Subject: [PATCH 07/27] drivers: Do not use Fp-prefix for driver-specific types It's a libfprint specific prefix, so let's avoid confusion. --- libfprint/drivers/elanmoc/elanmoc.c | 14 +-- .../drivers/focaltech_moc/focaltech_moc.c | 14 +-- libfprint/drivers/fpcmoc/fpc.c | 90 +++++++++---------- libfprint/drivers/fpcmoc/fpc.h | 64 ++++++------- libfprint/drivers/goodixmoc/goodix.c | 64 ++++++------- libfprint/drivers/goodixmoc/goodix.h | 56 ++++++------ 6 files changed, 151 insertions(+), 151 deletions(-) diff --git a/libfprint/drivers/elanmoc/elanmoc.c b/libfprint/drivers/elanmoc/elanmoc.c index f5ccc11a..fe2f9fab 100644 --- a/libfprint/drivers/elanmoc/elanmoc.c +++ b/libfprint/drivers/elanmoc/elanmoc.c @@ -132,10 +132,10 @@ fp_cmd_receive_cb (FpiUsbTransfer *transfer, } typedef enum { - FP_CMD_SEND = 0, - FP_CMD_GET, - FP_CMD_NUM_STATES, -} FpCmdState; + ELAN_MOC_CMD_SEND = 0, + ELAN_MOC_CMD_GET, + ELAN_MOC_CMD_NUM_STATES, +} ElanMocCmdState; static void fp_cmd_run_state (FpiSsm *ssm, @@ -146,7 +146,7 @@ fp_cmd_run_state (FpiSsm *ssm, switch (fpi_ssm_get_cur_state (ssm)) { - case FP_CMD_SEND: + case ELAN_MOC_CMD_SEND: if (self->cmd_transfer) { self->cmd_transfer->ssm = ssm; @@ -162,7 +162,7 @@ fp_cmd_run_state (FpiSsm *ssm, } break; - case FP_CMD_GET: + case ELAN_MOC_CMD_GET: if (self->cmd_len_in == 0) { CommandData *data = fpi_ssm_get_data (ssm); @@ -229,7 +229,7 @@ elanmoc_get_cmd (FpDevice *device, guint8 *buffer_out, self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self), fp_cmd_run_state, - FP_CMD_NUM_STATES); + ELAN_MOC_CMD_NUM_STATES); fpi_ssm_set_data (self->cmd_ssm, data, (GDestroyNotify) fp_cmd_ssm_done_data_free); diff --git a/libfprint/drivers/focaltech_moc/focaltech_moc.c b/libfprint/drivers/focaltech_moc/focaltech_moc.c index 17f5363f..38992dd8 100644 --- a/libfprint/drivers/focaltech_moc/focaltech_moc.c +++ b/libfprint/drivers/focaltech_moc/focaltech_moc.c @@ -235,10 +235,10 @@ fp_cmd_receive_cb (FpiUsbTransfer *transfer, } typedef enum { - FP_CMD_SEND = 0, - FP_CMD_GET, - FP_CMD_NUM_STATES, -} FpCmdState; + FOCALTECH_CMD_SEND = 0, + FOCALTECH_CMD_GET, + FOCALTECH_CMD_NUM_STATES, +} FocaltechCmdState; static void fp_cmd_run_state (FpiSsm *ssm, @@ -249,7 +249,7 @@ fp_cmd_run_state (FpiSsm *ssm, switch (fpi_ssm_get_cur_state (ssm)) { - case FP_CMD_SEND: + case FOCALTECH_CMD_SEND: if (self->cmd_transfer) { self->cmd_transfer->ssm = ssm; @@ -266,7 +266,7 @@ fp_cmd_run_state (FpiSsm *ssm, break; - case FP_CMD_GET: + case FOCALTECH_CMD_GET: if (self->cmd_len_in == 0) { CommandData *data = fpi_ssm_get_data (ssm); @@ -334,7 +334,7 @@ focaltech_moc_get_cmd (FpDevice *device, guint8 *buffer_out, self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self), fp_cmd_run_state, - FP_CMD_NUM_STATES); + FOCALTECH_CMD_NUM_STATES); fpi_ssm_set_data (self->cmd_ssm, data, (GDestroyNotify) fp_cmd_ssm_done_data_free); diff --git a/libfprint/drivers/fpcmoc/fpc.c b/libfprint/drivers/fpcmoc/fpc.c index b67c4ff2..1df4f0ce 100644 --- a/libfprint/drivers/fpcmoc/fpc.c +++ b/libfprint/drivers/fpcmoc/fpc.c @@ -86,7 +86,7 @@ fpc_suspend_resume_cb (FpiUsbTransfer *transfer, fp_dbg ("%s current ssm state: %d", G_STRFUNC, ssm_state); - if (ssm_state == FP_CMD_SUSPENDED) + if (ssm_state == FPC_CMD_SUSPENDED) { if (error) fpi_ssm_mark_failed (transfer->ssm, error); @@ -94,12 +94,12 @@ fpc_suspend_resume_cb (FpiUsbTransfer *transfer, fpi_device_suspend_complete (device, error); /* The resume handler continues to the next state! */ } - else if (ssm_state == FP_CMD_RESUME) + else if (ssm_state == FPC_CMD_RESUME) { if (error) fpi_ssm_mark_failed (transfer->ssm, error); else - fpi_ssm_jump_to_state (transfer->ssm, FP_CMD_GET_DATA); + fpi_ssm_jump_to_state (transfer->ssm, FPC_CMD_GET_DATA); fpi_device_resume_complete (device, error); } @@ -118,7 +118,7 @@ fpc_cmd_receive_cb (FpiUsbTransfer *transfer, if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && (self->cmd_suspended)) { g_error_free (error); - fpi_ssm_jump_to_state (transfer->ssm, FP_CMD_SUSPENDED); + fpi_ssm_jump_to_state (transfer->ssm, FPC_CMD_SUSPENDED); return; } @@ -139,7 +139,7 @@ fpc_cmd_receive_cb (FpiUsbTransfer *transfer, fp_dbg ("%s current ssm request: %d state: %d", G_STRFUNC, data->request, ssm_state); /* clean cmd_ssm except capture command for suspend/resume case */ - if (ssm_state != FP_CMD_SEND || data->request != FPC_CMD_ARM) + if (ssm_state != FPC_CMD_SEND || data->request != FPC_CMD_ARM) self->cmd_ssm = NULL; if (data->cmdtype == FPC_CMDTYPE_TO_DEVICE) @@ -153,13 +153,13 @@ fpc_cmd_receive_cb (FpiUsbTransfer *transfer, } else if (data->cmdtype == FPC_CMDTYPE_TO_DEVICE_EVTDATA) { - if (ssm_state == FP_CMD_SEND) + if (ssm_state == FPC_CMD_SEND) { fpi_ssm_next_state (transfer->ssm); return; } - if (ssm_state == FP_CMD_GET_DATA) + if (ssm_state == FPC_CMD_GET_DATA) { fpc_cmd_response_t evt_data = {0}; fp_dbg ("%s recv evt data length: %ld", G_STRFUNC, transfer->actual_length); @@ -292,11 +292,11 @@ fpc_cmd_run_state (FpiSsm *ssm, switch (fpi_ssm_get_cur_state (ssm)) { - case FP_CMD_SEND: + case FPC_CMD_SEND: fpc_send_ctrl_cmd (dev); break; - case FP_CMD_GET_DATA: + case FPC_CMD_GET_DATA: transfer = fpi_usb_transfer_new (dev); transfer->ssm = ssm; fpi_usb_transfer_fill_bulk (transfer, EP_IN, EP_IN_MAX_BUF_SIZE); @@ -307,7 +307,7 @@ fpc_cmd_run_state (FpiSsm *ssm, fpi_ssm_get_data (ssm)); break; - case FP_CMD_SUSPENDED: + case FPC_CMD_SUSPENDED: transfer = fpi_usb_transfer_new (dev); transfer->ssm = ssm; fpi_usb_transfer_fill_control (transfer, @@ -323,7 +323,7 @@ fpc_cmd_run_state (FpiSsm *ssm, fpc_suspend_resume_cb, NULL); break; - case FP_CMD_RESUME: + case FPC_CMD_RESUME: transfer = fpi_usb_transfer_new (dev); transfer->ssm = ssm; fpi_usb_transfer_fill_control (transfer, @@ -368,7 +368,7 @@ fpc_sensor_cmd (FpiDeviceFpcMoc *self, g_assert (self->cmd_ssm == NULL); self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self), fpc_cmd_run_state, - FP_CMD_NUM_STATES); + FPC_CMD_NUM_STATES); fpi_ssm_set_data (self->cmd_ssm, data, g_free); fpi_ssm_start (self->cmd_ssm, fpc_cmd_ssm_done); @@ -743,7 +743,7 @@ fpc_enroll_update_cb (FpiDeviceFpcMoc *self, case FPC_ENROL_STATUS_COMPLETED: self->enroll_stage++; fpi_device_enroll_progress (FP_DEVICE (self), self->enroll_stage, NULL, NULL); - fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_COMPLETE); + fpi_ssm_jump_to_state (self->task_ssm, FPC_ENROLL_COMPLETE); return; case FPC_ENROL_STATUS_IMAGE_TOO_SIMILAR: @@ -762,7 +762,7 @@ fpc_enroll_update_cb (FpiDeviceFpcMoc *self, /* Used for customer enrollment scheme */ if (self->enroll_stage >= (self->max_enroll_stage - self->max_immobile_stage)) { - fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_COMPLETE); + fpi_ssm_jump_to_state (self->task_ssm, FPC_ENROLL_COMPLETE); return; } break; @@ -780,7 +780,7 @@ fpc_enroll_update_cb (FpiDeviceFpcMoc *self, /* Used for customer enrollment scheme */ if (self->enroll_stage >= (self->max_enroll_stage - self->max_immobile_stage)) { - fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_COMPLETE); + fpi_ssm_jump_to_state (self->task_ssm, FPC_ENROLL_COMPLETE); return; } break; @@ -815,7 +815,7 @@ fpc_enroll_update_cb (FpiDeviceFpcMoc *self, } else { - fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_CAPTURE); + fpi_ssm_jump_to_state (self->task_ssm, FPC_ENROLL_CAPTURE); } } @@ -943,7 +943,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) switch (fpi_ssm_get_cur_state (ssm)) { - case FP_ENROLL_ENUM: + case FPC_ENROLL_ENUM: { FPC_FID_DATA pquery_data = {0}; gsize query_data_len = 0; @@ -968,7 +968,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_CREATE: + case FPC_ENROLL_CREATE: { recv_data_len = sizeof (FPC_BEGIN_ENROL); cmd_data.cmdtype = FPC_CMDTYPE_FROM_DEVICE; @@ -983,7 +983,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_CAPTURE: + case FPC_ENROLL_CAPTURE: { guint32 capture_id = FPC_CAPTUREID_RESERVED; fpi_device_report_finger_status_changes (device, @@ -1001,7 +1001,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_GET_IMG: + case FPC_ENROLL_GET_IMG: { cmd_data.cmdtype = FPC_CMDTYPE_TO_DEVICE_EVTDATA; cmd_data.request = FPC_CMD_GET_IMG; @@ -1015,7 +1015,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_UPDATE: + case FPC_ENROLL_UPDATE: { recv_data_len = sizeof (FPC_ENROL); cmd_data.cmdtype = FPC_CMDTYPE_FROM_DEVICE; @@ -1030,7 +1030,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_COMPLETE: + case FPC_ENROLL_COMPLETE: { recv_data_len = sizeof (FPC_END_ENROL); cmd_data.cmdtype = FPC_CMDTYPE_FROM_DEVICE; @@ -1045,7 +1045,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_CHECK_DUPLICATE: + case FPC_ENROLL_CHECK_DUPLICATE: { recv_data_len = sizeof (FPC_IDENTIFY); cmd_data.cmdtype = FPC_CMDTYPE_FROM_DEVICE; @@ -1060,7 +1060,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_BINDID: + case FPC_ENROLL_BINDID: { FPC_FID_DATA data = {0}; gsize data_len = 0; @@ -1114,7 +1114,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_COMMIT: + case FPC_ENROLL_COMMIT: { recv_data_len = sizeof (gint32); cmd_data.cmdtype = FPC_CMDTYPE_FROM_DEVICE; @@ -1129,7 +1129,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_DICARD: + case FPC_ENROLL_DICARD: { cmd_data.cmdtype = FPC_CMDTYPE_TO_DEVICE; cmd_data.request = FPC_CMD_ABORT; @@ -1142,7 +1142,7 @@ fpc_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_CLEANUP: + case FPC_ENROLL_CLEANUP: { if (self->do_cleanup == TRUE) { @@ -1287,7 +1287,7 @@ fpc_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) switch (fpi_ssm_get_cur_state (ssm)) { - case FP_VERIFY_CAPTURE: + case FPC_VERIFY_CAPTURE: { guint32 capture_id = FPC_CAPTUREID_RESERVED; fpi_device_report_finger_status_changes (device, @@ -1305,7 +1305,7 @@ fpc_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_VERIFY_GET_IMG: + case FPC_VERIFY_GET_IMG: { cmd_data.cmdtype = FPC_CMDTYPE_TO_DEVICE_EVTDATA; cmd_data.request = FPC_CMD_GET_IMG; @@ -1319,7 +1319,7 @@ fpc_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_VERIFY_IDENTIFY: + case FPC_VERIFY_IDENTIFY: { gsize recv_data_len = sizeof (FPC_IDENTIFY); cmd_data.cmdtype = FPC_CMDTYPE_FROM_DEVICE; @@ -1334,7 +1334,7 @@ fpc_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_VERIFY_CANCEL: + case FPC_VERIFY_CANCEL: { cmd_data.cmdtype = FPC_CMDTYPE_TO_DEVICE; cmd_data.request = FPC_CMD_ABORT; @@ -1404,7 +1404,7 @@ fpc_clear_sm_run_state (FpiSsm *ssm, FpDevice *device) switch (fpi_ssm_get_cur_state (ssm)) { - case FP_CLEAR_DELETE_DB: + case FPC_CLEAR_DELETE_DB: { if (self->dbid) { @@ -1431,7 +1431,7 @@ fpc_clear_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_CLEAR_CREATE_DB: + case FPC_CLEAR_CREATE_DB: { if (self->dbid) { @@ -1536,7 +1536,7 @@ fpc_init_sm_run_state (FpiSsm *ssm, FpDevice *device) switch (fpi_ssm_get_cur_state (ssm)) { - case FP_INIT: + case FPC_INIT: cmd_data.cmdtype = FPC_CMDTYPE_TO_DEVICE_EVTDATA; cmd_data.request = FPC_CMD_INIT; cmd_data.value = 0x1; @@ -1548,7 +1548,7 @@ fpc_init_sm_run_state (FpiSsm *ssm, FpDevice *device) fpc_sensor_cmd (self, FALSE, &cmd_data); break; - case FP_LOAD_DB: + case FPC_INIT_LOAD_DB: { gsize recv_data_len = sizeof (FPC_LOAD_DB); cmd_data.cmdtype = FPC_CMDTYPE_FROM_DEVICE; @@ -1682,7 +1682,7 @@ fpc_dev_open (FpDevice *device) } self->task_ssm = fpi_ssm_new (device, fpc_init_sm_run_state, - FP_INIT_NUM_STATES); + FPC_INIT_NUM_STATES); fpi_ssm_start (self->task_ssm, fpc_init_ssm_done); } @@ -1705,8 +1705,8 @@ fpc_dev_verify_identify (FpDevice *device) fp_dbg ("%s enter -->", G_STRFUNC); self->task_ssm = fpi_ssm_new_full (device, fpc_verify_sm_run_state, - FP_VERIFY_NUM_STATES, - FP_VERIFY_CANCEL, + FPC_VERIFY_NUM_STATES, + FPC_VERIFY_CANCEL, "verify_identify"); fpi_ssm_start (self->task_ssm, fpc_verify_ssm_done); @@ -1722,8 +1722,8 @@ fpc_dev_enroll (FpDevice *device) self->enroll_stage = 0; self->immobile_stage = 0; self->task_ssm = fpi_ssm_new_full (device, fpc_enroll_sm_run_state, - FP_ENROLL_NUM_STATES, - FP_ENROLL_DICARD, + FPC_ENROLL_NUM_STATES, + FPC_ENROLL_DICARD, "enroll"); fpi_ssm_start (self->task_ssm, fpc_enroll_ssm_done); @@ -1774,7 +1774,7 @@ fpc_dev_suspend (FpDevice *device) } g_assert (self->cmd_ssm); - g_assert (fpi_ssm_get_cur_state (self->cmd_ssm) == FP_CMD_GET_DATA); + g_assert (fpi_ssm_get_cur_state (self->cmd_ssm) == FPC_CMD_GET_DATA); self->cmd_suspended = TRUE; g_cancellable_cancel (self->interrupt_cancellable); } @@ -1796,10 +1796,10 @@ fpc_dev_resume (FpDevice *device) g_assert (self->cmd_ssm); g_assert (self->cmd_suspended); - g_assert (fpi_ssm_get_cur_state (self->cmd_ssm) == FP_CMD_SUSPENDED); + g_assert (fpi_ssm_get_cur_state (self->cmd_ssm) == FPC_CMD_SUSPENDED); self->cmd_suspended = FALSE; g_set_object (&self->interrupt_cancellable, g_cancellable_new ()); - fpi_ssm_jump_to_state (self->cmd_ssm, FP_CMD_RESUME); + fpi_ssm_jump_to_state (self->cmd_ssm, FPC_CMD_RESUME); } static void @@ -1863,8 +1863,8 @@ fpc_dev_clear_storage (FpDevice *device) fp_dbg ("%s enter -->", G_STRFUNC); self->task_ssm = fpi_ssm_new_full (device, fpc_clear_sm_run_state, - FP_CLEAR_NUM_STATES, - FP_CLEAR_NUM_STATES, + FPC_CLEAR_NUM_STATES, + FPC_CLEAR_NUM_STATES, "Clear storage"); fpi_ssm_start (self->task_ssm, fpc_clear_ssm_done); diff --git a/libfprint/drivers/fpcmoc/fpc.h b/libfprint/drivers/fpcmoc/fpc.h index 389c63ff..70d64320 100644 --- a/libfprint/drivers/fpcmoc/fpc.h +++ b/libfprint/drivers/fpcmoc/fpc.h @@ -178,44 +178,44 @@ typedef enum { } FpcCmdType; typedef enum { - FP_CMD_SEND = 0, - FP_CMD_GET_DATA, - FP_CMD_SUSPENDED, - FP_CMD_RESUME, - FP_CMD_NUM_STATES, -} FpCmdState; + FPC_CMD_SEND = 0, + FPC_CMD_GET_DATA, + FPC_CMD_SUSPENDED, + FPC_CMD_RESUME, + FPC_CMD_NUM_STATES, +} FpcCmdState; typedef enum { - FP_INIT = 0, - FP_LOAD_DB, - FP_INIT_NUM_STATES, -} FpInitState; + FPC_INIT = 0, + FPC_INIT_LOAD_DB, + FPC_INIT_NUM_STATES, +} FpcInitState; typedef enum { - FP_ENROLL_ENUM = 0, - FP_ENROLL_CREATE, - FP_ENROLL_CAPTURE, - FP_ENROLL_GET_IMG, - FP_ENROLL_UPDATE, - FP_ENROLL_COMPLETE, - FP_ENROLL_CHECK_DUPLICATE, - FP_ENROLL_BINDID, - FP_ENROLL_COMMIT, - FP_ENROLL_DICARD, - FP_ENROLL_CLEANUP, - FP_ENROLL_NUM_STATES, -} FpEnrollState; + FPC_ENROLL_ENUM = 0, + FPC_ENROLL_CREATE, + FPC_ENROLL_CAPTURE, + FPC_ENROLL_GET_IMG, + FPC_ENROLL_UPDATE, + FPC_ENROLL_COMPLETE, + FPC_ENROLL_CHECK_DUPLICATE, + FPC_ENROLL_BINDID, + FPC_ENROLL_COMMIT, + FPC_ENROLL_DICARD, + FPC_ENROLL_CLEANUP, + FPC_ENROLL_NUM_STATES, +} FpcEnrollState; typedef enum { - FP_VERIFY_CAPTURE = 0, - FP_VERIFY_GET_IMG, - FP_VERIFY_IDENTIFY, - FP_VERIFY_CANCEL, - FP_VERIFY_NUM_STATES, -} FpVerifyState; + FPC_VERIFY_CAPTURE = 0, + FPC_VERIFY_GET_IMG, + FPC_VERIFY_IDENTIFY, + FPC_VERIFY_CANCEL, + FPC_VERIFY_NUM_STATES, +} FpcVerifyState; typedef enum { - FP_CLEAR_DELETE_DB = 0, - FP_CLEAR_CREATE_DB, - FP_CLEAR_NUM_STATES, + FPC_CLEAR_DELETE_DB = 0, + FPC_CLEAR_CREATE_DB, + FPC_CLEAR_NUM_STATES, } FpClearState; diff --git a/libfprint/drivers/goodixmoc/goodix.c b/libfprint/drivers/goodixmoc/goodix.c index cccbf4de..976675b4 100644 --- a/libfprint/drivers/goodixmoc/goodix.c +++ b/libfprint/drivers/goodixmoc/goodix.c @@ -238,7 +238,7 @@ fp_cmd_run_state (FpiSsm *ssm, switch (fpi_ssm_get_cur_state (ssm)) { - case FP_CMD_SEND: + case GOODIX_CMD_SEND: if (self->cmd_transfer) { self->cmd_transfer->ssm = ssm; @@ -254,7 +254,7 @@ fp_cmd_run_state (FpiSsm *ssm, } break; - case FP_CMD_GET_ACK: + case GOODIX_CMD_GET_ACK: transfer = fpi_usb_transfer_new (dev); transfer->ssm = ssm; fpi_usb_transfer_fill_bulk (transfer, EP_IN, EP_IN_MAX_BUF_SIZE); @@ -266,7 +266,7 @@ fp_cmd_run_state (FpiSsm *ssm, break; - case FP_CMD_GET_DATA: + case GOODIX_CMD_GET_DATA: transfer = fpi_usb_transfer_new (dev); transfer->ssm = ssm; fpi_usb_transfer_fill_bulk (transfer, EP_IN, EP_IN_MAX_BUF_SIZE); @@ -354,7 +354,7 @@ goodix_sensor_cmd (FpiDeviceGoodixMoc *self, self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self), fp_cmd_run_state, - FP_CMD_NUM_STATES); + GOODIX_CMD_NUM_STATES); fpi_ssm_set_data (self->cmd_ssm, data, (GDestroyNotify) fp_cmd_ssm_done_data_free); @@ -493,7 +493,7 @@ fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) switch (fpi_ssm_get_cur_state (ssm)) { - case FP_VERIFY_PWR_BTN_SHIELD_ON: + case GOODIX_VERIFY_PWR_BTN_SHIELD_ON: goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_ON, false, NULL, @@ -501,7 +501,7 @@ fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_pwr_btn_shield_cb); break; - case FP_VERIFY_CAPTURE: + case GOODIX_VERIFY_CAPTURE: fpi_device_report_finger_status_changes (device, FP_FINGER_STATUS_NEEDED, FP_FINGER_STATUS_NONE); @@ -512,7 +512,7 @@ fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_verify_capture_cb); break; - case FP_VERIFY_IDENTIFY: + case GOODIX_VERIFY_IDENTIFY: goodix_sensor_cmd (self, MOC_CMD0_IDENTIFY, MOC_CMD1_DEFAULT, false, (const guint8 *) nonce, @@ -520,7 +520,7 @@ fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_verify_cb); break; - case FP_VERIFY_PWR_BTN_SHIELD_OFF: + case GOODIX_VERIFY_PWR_BTN_SHIELD_OFF: goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_OFF, false, NULL, @@ -682,7 +682,7 @@ fp_enroll_capture_cb (FpiDeviceGoodixMoc *self, self->enroll_stage, NULL, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); - fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_CAPTURE); + fpi_ssm_jump_to_state (self->task_ssm, GOODIX_ENROLL_CAPTURE); return; } fpi_device_report_finger_status_changes (FP_DEVICE (self), @@ -700,7 +700,7 @@ fp_enroll_capture_cb (FpiDeviceGoodixMoc *self, self->enroll_stage, NULL, fpi_device_retry_new (FP_DEVICE_RETRY_CENTER_FINGER)); - fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_CAPTURE); + fpi_ssm_jump_to_state (self->task_ssm, GOODIX_ENROLL_CAPTURE); return; } else @@ -746,7 +746,7 @@ fp_enroll_update_cb (FpiDeviceGoodixMoc *self, /* if enroll complete, no need to wait finger up */ if (self->enroll_stage >= self->max_enroll_stage) { - fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_CHECK_DUPLICATE); + fpi_ssm_jump_to_state (self->task_ssm, GOODIX_ENROLL_CHECK_DUPLICATE); return; } @@ -812,7 +812,7 @@ fp_finger_mode_cb (FpiDeviceGoodixMoc *self, /* if reach max timeout(5sec) finger not up, switch to finger up again */ if (resp->finger_status.status == GX_ERROR_WAIT_FINGER_UP_TIMEOUT) { - fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_WAIT_FINGER_UP); + fpi_ssm_jump_to_state (self->task_ssm, GOODIX_ENROLL_WAIT_FINGER_UP); return; } else if (resp->finger_status.status != GX_SUCCESS) @@ -827,7 +827,7 @@ fp_finger_mode_cb (FpiDeviceGoodixMoc *self, FP_FINGER_STATUS_PRESENT); if (self->enroll_stage < self->max_enroll_stage) { - fpi_ssm_jump_to_state (self->task_ssm, FP_ENROLL_CAPTURE); + fpi_ssm_jump_to_state (self->task_ssm, GOODIX_ENROLL_CAPTURE); return; } fpi_ssm_next_state (self->task_ssm); @@ -853,7 +853,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) switch (fpi_ssm_get_cur_state (ssm)) { - case FP_ENROLL_PWR_BTN_SHIELD_ON: + case GOODIX_ENROLL_PWR_BTN_SHIELD_ON: { goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_ON, false, @@ -863,7 +863,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_ENUM: + case GOODIX_ENROLL_ENUM: { goodix_sensor_cmd (self, MOC_CMD0_GETFINGERLIST, MOC_CMD1_DEFAULT, false, @@ -873,7 +873,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_CREATE: + case GOODIX_ENROLL_CREATE: { goodix_sensor_cmd (self, MOC_CMD0_ENROLL_INIT, MOC_CMD1_DEFAULT, false, @@ -883,7 +883,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_CAPTURE: + case GOODIX_ENROLL_CAPTURE: fpi_device_report_finger_status_changes (device, FP_FINGER_STATUS_NEEDED, FP_FINGER_STATUS_NONE); @@ -894,7 +894,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_enroll_capture_cb); break; - case FP_ENROLL_UPDATE: + case GOODIX_ENROLL_UPDATE: dummy[0] = 1; dummy[1] = self->sensorcfg->config[2]; dummy[2] = self->sensorcfg->config[3]; @@ -905,7 +905,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_enroll_update_cb); break; - case FP_ENROLL_WAIT_FINGER_UP: + case GOODIX_ENROLL_WAIT_FINGER_UP: dummy[0] = 0; goodix_sensor_cmd (self, MOC_CMD0_FINGER_MODE, MOC_CMD1_SET_FINGER_UP, true, @@ -914,7 +914,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_finger_mode_cb); break; - case FP_ENROLL_CHECK_DUPLICATE: + case GOODIX_ENROLL_CHECK_DUPLICATE: goodix_sensor_cmd (self, MOC_CMD0_CHECK4DUPLICATE, MOC_CMD1_DEFAULT, false, (const guint8 *) &dummy, @@ -922,7 +922,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_enroll_check_duplicate_cb); break; - case FP_ENROLL_COMMIT: + case GOODIX_ENROLL_COMMIT: { fpi_device_get_enroll_data (device, &print); user_id = fpi_print_generate_user_id (print); @@ -975,7 +975,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) } break; - case FP_ENROLL_PWR_BTN_SHIELD_OFF: + case GOODIX_ENROLL_PWR_BTN_SHIELD_OFF: { goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_OFF, false, @@ -1066,7 +1066,7 @@ fp_init_cb_reset_or_complete (FpiDeviceGoodixMoc *self, { fp_warn ("Template storage appears to have been corrupted! Error was: %s", error->message); fp_warn ("A known reason for this to happen is a firmware bug triggered by another storage area being initialized."); - fpi_ssm_jump_to_state (self->task_ssm, FP_INIT_RESET_DEVICE); + fpi_ssm_jump_to_state (self->task_ssm, GOODIX_INIT_RESET_DEVICE); } else { @@ -1107,7 +1107,7 @@ fp_init_sm_run_state (FpiSsm *ssm, FpDevice *device) switch (fpi_ssm_get_cur_state (ssm)) { - case FP_INIT_VERSION: + case GOODIX_INIT_VERSION: goodix_sensor_cmd (self, MOC_CMD0_GET_VERSION, MOC_CMD1_DEFAULT, false, &dummy, @@ -1115,7 +1115,7 @@ fp_init_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_init_version_cb); break; - case FP_INIT_CONFIG: + case GOODIX_INIT_CONFIG: goodix_sensor_cmd (self, MOC_CMD0_UPDATE_CONFIG, MOC_CMD1_WRITE_CFG_TO_FLASH, false, (guint8 *) self->sensorcfg, @@ -1123,7 +1123,7 @@ fp_init_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_init_config_cb); break; - case FP_INIT_TEMPLATE_LIST: + case GOODIX_INIT_TEMPLATE_LIST: /* List prints to check whether the template DB was corrupted. * As of 2022-06-13 there is a known firmware issue that can cause the * stored templates for Linux to be corrupted when the Windows storage @@ -1138,7 +1138,7 @@ fp_init_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_init_cb_reset_or_complete); break; - case FP_INIT_RESET_DEVICE: + case GOODIX_INIT_RESET_DEVICE: fp_warn ("Resetting device storage, you will need to enroll all prints again!"); goodix_sensor_cmd (self, MOC_CMD0_DELETETEMPLATE, MOC_CMD1_DELETE_ALL, FALSE, @@ -1452,7 +1452,7 @@ gx_fp_init (FpDevice *device) } self->task_ssm = fpi_ssm_new (device, fp_init_sm_run_state, - FP_INIT_NUM_STATES); + GOODIX_INIT_NUM_STATES); fpi_ssm_start (self->task_ssm, fp_init_ssm_done); @@ -1518,8 +1518,8 @@ gx_fp_verify_identify (FpDevice *device) FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device); self->task_ssm = fpi_ssm_new_full (device, fp_verify_sm_run_state, - FP_VERIFY_NUM_STATES, - FP_VERIFY_PWR_BTN_SHIELD_OFF, + GOODIX_VERIFY_NUM_STATES, + GOODIX_VERIFY_PWR_BTN_SHIELD_OFF, "verify"); fpi_ssm_start (self->task_ssm, fp_verify_ssm_done); @@ -1535,8 +1535,8 @@ gx_fp_enroll (FpDevice *device) self->enroll_stage = 0; self->task_ssm = fpi_ssm_new_full (device, fp_enroll_sm_run_state, - FP_ENROLL_NUM_STATES, - FP_ENROLL_PWR_BTN_SHIELD_OFF, + GOODIX_ENROLL_NUM_STATES, + GOODIX_ENROLL_PWR_BTN_SHIELD_OFF, "enroll"); fpi_ssm_start (self->task_ssm, fp_enroll_ssm_done); diff --git a/libfprint/drivers/goodixmoc/goodix.h b/libfprint/drivers/goodixmoc/goodix.h index 4ec5511f..1a04d0f4 100644 --- a/libfprint/drivers/goodixmoc/goodix.h +++ b/libfprint/drivers/goodixmoc/goodix.h @@ -25,39 +25,39 @@ G_DECLARE_FINAL_TYPE (FpiDeviceGoodixMoc, fpi_device_goodixmoc, FPI, DEVICE_GOODIXMOC, FpDevice) typedef enum { - FP_CMD_SEND = 0, - FP_CMD_GET_ACK, - FP_CMD_GET_DATA, - FP_CMD_NUM_STATES, -} FpCmdState; + GOODIX_CMD_SEND = 0, + GOODIX_CMD_GET_ACK, + GOODIX_CMD_GET_DATA, + GOODIX_CMD_NUM_STATES, +} GoodixCmdState; typedef enum { - FP_INIT_VERSION = 0, - FP_INIT_CONFIG, - FP_INIT_TEMPLATE_LIST, - FP_INIT_RESET_DEVICE, - FP_INIT_NUM_STATES, -} FpInitState; + GOODIX_INIT_VERSION = 0, + GOODIX_INIT_CONFIG, + GOODIX_INIT_TEMPLATE_LIST, + GOODIX_INIT_RESET_DEVICE, + GOODIX_INIT_NUM_STATES, +} GoodixInitState; typedef enum { - FP_ENROLL_PWR_BTN_SHIELD_ON = 0, - FP_ENROLL_ENUM, - FP_ENROLL_CREATE, - FP_ENROLL_CAPTURE, - FP_ENROLL_UPDATE, - FP_ENROLL_WAIT_FINGER_UP, - FP_ENROLL_CHECK_DUPLICATE, - FP_ENROLL_COMMIT, - FP_ENROLL_PWR_BTN_SHIELD_OFF, - FP_ENROLL_NUM_STATES, -} FpEnrollState; + GOODIX_ENROLL_PWR_BTN_SHIELD_ON = 0, + GOODIX_ENROLL_ENUM, + GOODIX_ENROLL_CREATE, + GOODIX_ENROLL_CAPTURE, + GOODIX_ENROLL_UPDATE, + GOODIX_ENROLL_WAIT_FINGER_UP, + GOODIX_ENROLL_CHECK_DUPLICATE, + GOODIX_ENROLL_COMMIT, + GOODIX_ENROLL_PWR_BTN_SHIELD_OFF, + GOODIX_ENROLL_NUM_STATES, +} GoodixEnrollState; typedef enum { - FP_VERIFY_PWR_BTN_SHIELD_ON = 0, - FP_VERIFY_CAPTURE, - FP_VERIFY_IDENTIFY, - FP_VERIFY_PWR_BTN_SHIELD_OFF, - FP_VERIFY_NUM_STATES, -} FpVerifyState; + GOODIX_VERIFY_PWR_BTN_SHIELD_ON = 0, + GOODIX_VERIFY_CAPTURE, + GOODIX_VERIFY_IDENTIFY, + GOODIX_VERIFY_PWR_BTN_SHIELD_OFF, + GOODIX_VERIFY_NUM_STATES, +} GoodixVerifyState; From b0394697119e452fd03cd881e531245aca9645ff Mon Sep 17 00:00:00 2001 From: Hao Chen Date: Mon, 1 Dec 2025 22:09:31 +0800 Subject: [PATCH 08/27] focaltech: add support for 2808:1579 --- data/autosuspend.hwdb | 1 + libfprint/drivers/focaltech_moc/focaltech_moc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index e1c6be3b..a1359f72 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -179,6 +179,7 @@ usb:v2808pA959* usb:v2808pA99A* usb:v2808pA57A* usb:v2808pA78A* +usb:v2808p1579* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/focaltech_moc/focaltech_moc.c b/libfprint/drivers/focaltech_moc/focaltech_moc.c index 38992dd8..fb8a8213 100644 --- a/libfprint/drivers/focaltech_moc/focaltech_moc.c +++ b/libfprint/drivers/focaltech_moc/focaltech_moc.c @@ -33,6 +33,7 @@ static const FpIdEntry id_table[] = { { .vid = 0x2808, .pid = 0xa99a, }, { .vid = 0x2808, .pid = 0xa57a, }, { .vid = 0x2808, .pid = 0xa78a, }, + { .vid = 0x2808, .pid = 0x1579, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; From 0202368f1712fbcb7bec6df80718609ba18654bd Mon Sep 17 00:00:00 2001 From: herman lin Date: Thu, 8 May 2025 15:34:37 +0800 Subject: [PATCH 09/27] elanmoc: Add new PID 0x0CA8 --- data/autosuspend.hwdb | 1 + libfprint/drivers/elanmoc/elanmoc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index a1359f72..c5db2a21 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -164,6 +164,7 @@ usb:v04F3p0C99* usb:v04F3p0C9D* usb:v04F3p0C9F* usb:v04F3p0CA3* +usb:v04F3p0CA8* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/elanmoc/elanmoc.c b/libfprint/drivers/elanmoc/elanmoc.c index fe2f9fab..bcabd8fb 100644 --- a/libfprint/drivers/elanmoc/elanmoc.c +++ b/libfprint/drivers/elanmoc/elanmoc.c @@ -36,6 +36,7 @@ static const FpIdEntry id_table[] = { { .vid = 0x04f3, .pid = 0x0c9d, }, { .vid = 0x04f3, .pid = 0x0c9f, }, { .vid = 0x04f3, .pid = 0x0ca3, }, + { .vid = 0x04f3, .pid = 0x0ca8, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; From 3460b020e1b509089630452b0ac4752ed910e7cb Mon Sep 17 00:00:00 2001 From: Himura Kazuto Date: Wed, 29 Oct 2025 11:31:05 +0400 Subject: [PATCH 10/27] egismoc: support ETU905A88-E device (1c7a:0584) --- data/autosuspend.hwdb | 2 +- libfprint/drivers/egismoc/egismoc.c | 1 + libfprint/fprint-list-udev-hwdb.c | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index c5db2a21..0e352d93 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -80,6 +80,7 @@ usb:v1C7Ap0571* # Supported by libfprint driver egismoc usb:v1C7Ap0582* usb:v1C7Ap0583* +usb:v1C7Ap0584* usb:v1C7Ap0586* usb:v1C7Ap0587* usb:v1C7Ap0588* @@ -434,7 +435,6 @@ usb:v16D1p1027* usb:v1C7Ap0300* usb:v1C7Ap0575* usb:v1C7Ap0576* -usb:v1C7Ap0584* usb:v1C7Ap0577* usb:v1C7Ap057E* usb:v2541p0236* diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index f68398e9..06f445d4 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -52,6 +52,7 @@ G_DEFINE_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FP_TYPE_DEVICE); static const FpIdEntry egismoc_id_table[] = { { .vid = 0x1c7a, .pid = 0x0582, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 }, { .vid = 0x1c7a, .pid = 0x0583, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 }, + { .vid = 0x1c7a, .pid = 0x0584, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, { .vid = 0x1c7a, .pid = 0x0586, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, { .vid = 0x1c7a, .pid = 0x0587, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, { .vid = 0x1c7a, .pid = 0x0588, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c index 5cb8d68a..65854f36 100644 --- a/libfprint/fprint-list-udev-hwdb.c +++ b/libfprint/fprint-list-udev-hwdb.c @@ -120,7 +120,6 @@ static const FpIdEntry allowlist_id_table[] = { { .vid = 0x1c7a, .pid = 0x0300 }, { .vid = 0x1c7a, .pid = 0x0575 }, { .vid = 0x1c7a, .pid = 0x0576 }, - { .vid = 0x1c7a, .pid = 0x0584 }, { .vid = 0x1c7a, .pid = 0x0577 }, { .vid = 0x1c7a, .pid = 0x057e }, { .vid = 0x2541, .pid = 0x0236 }, From 3cd4f4d80d9cb0076b2e5c005aeacdaf729f0e33 Mon Sep 17 00:00:00 2001 From: Ryan Wong Date: Sun, 20 Jul 2025 16:57:48 +0800 Subject: [PATCH 11/27] Realtek: Add new ID 2541:fa03 This FPreader was found on Minisforum X1 Pro MiniPC, and it is listed by lsusb as "Generic Realtek USB2.0 Finger Print Bridge". Though the VID is not the same as the well-known "0bda", we can confirm it's from Realtek by analyzing Windows version driver. Signed-off-by: Ryan Wong --- data/autosuspend.hwdb | 1 + libfprint/drivers/realtek/realtek.c | 1 + 2 files changed, 2 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 0e352d93..5907cf62 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -243,6 +243,7 @@ usb:v298Dp1010* # Supported by libfprint driver realtek usb:v0BDAp5813* usb:v0BDAp5816* +usb:v2541pFA03* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/realtek/realtek.c b/libfprint/drivers/realtek/realtek.c index 425aadfa..4a0ddbc2 100644 --- a/libfprint/drivers/realtek/realtek.c +++ b/libfprint/drivers/realtek/realtek.c @@ -29,6 +29,7 @@ G_DEFINE_TYPE (FpiDeviceRealtek, fpi_device_realtek, FP_TYPE_DEVICE) static const FpIdEntry id_table[] = { { .vid = 0x0bda, .pid = 0x5813, }, { .vid = 0x0bda, .pid = 0x5816, }, + { .vid = 0x2541, .pid = 0xfa03, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; From a9c6621119e9eeeacf5d67cfb25ee22c36274d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 6 Dec 2025 02:38:04 +0100 Subject: [PATCH 12/27] focaltech_moc: add new pid:0xA27A --- data/autosuspend.hwdb | 1 + libfprint/drivers/focaltech_moc/focaltech_moc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 5907cf62..1035e943 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -177,6 +177,7 @@ usb:v1C7Ap0603* # Supported by libfprint driver focaltech_moc usb:v2808p9E48* usb:v2808pD979* +usb:v2808pA27A* usb:v2808pA959* usb:v2808pA99A* usb:v2808pA57A* diff --git a/libfprint/drivers/focaltech_moc/focaltech_moc.c b/libfprint/drivers/focaltech_moc/focaltech_moc.c index fb8a8213..2d410f88 100644 --- a/libfprint/drivers/focaltech_moc/focaltech_moc.c +++ b/libfprint/drivers/focaltech_moc/focaltech_moc.c @@ -29,6 +29,7 @@ G_DEFINE_TYPE (FpiDeviceFocaltechMoc, fpi_device_focaltech_moc, FP_TYPE_DEVICE) static const FpIdEntry id_table[] = { { .vid = 0x2808, .pid = 0x9e48, }, { .vid = 0x2808, .pid = 0xd979, }, + { .vid = 0x2808, .pid = 0xa27a, }, { .vid = 0x2808, .pid = 0xa959, }, { .vid = 0x2808, .pid = 0xa99a, }, { .vid = 0x2808, .pid = 0xa57a, }, From aa010b5c3bda52eed864cfeb3efbd2f119859f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 6 Dec 2025 03:00:48 +0100 Subject: [PATCH 13/27] fp-device: Clarify getter for open property --- libfprint/fp-device.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index ab06e7f5..6040c1be 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -483,6 +483,9 @@ fp_device_class_init (FpDeviceClass *klass) NULL, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); + /** + * FpDevice:open: (getter is_open): + */ properties[PROP_OPEN] = g_param_spec_boolean ("open", "Opened", From bd7c745b021dda20391ad6626f7f8d44bdb644dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 6 Dec 2025 02:49:46 +0100 Subject: [PATCH 14/27] ci: Use rawhide for testing again Reverts commit: 9141014456273b845ea8d4255f63f22a300aff59 --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index af7733a8..878038b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ default: variables: extends: .libfprint_common_variables FDO_DISTRIBUTION_TAG: $LIBFPRINT_IMAGE_TAG - FDO_DISTRIBUTION_VERSION: 41 + FDO_DISTRIBUTION_VERSION: rawhide FDO_UPSTREAM_REPO: "libfprint/$CI_PROJECT_NAME" FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG" LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" From b069c78348ff4b84fa520e874e2711f410130ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 9 Dec 2025 20:15:06 +0100 Subject: [PATCH 15/27] fpcmoc: Do not leak the interrupt cancellable g_set_object adds a reference already so we need to cleanup the original reference first if we do not want to leak it --- libfprint/drivers/fpcmoc/fpc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libfprint/drivers/fpcmoc/fpc.c b/libfprint/drivers/fpcmoc/fpc.c index 1df4f0ce..6547a64e 100644 --- a/libfprint/drivers/fpcmoc/fpc.c +++ b/libfprint/drivers/fpcmoc/fpc.c @@ -354,15 +354,16 @@ fpc_sensor_cmd (FpiDeviceFpcMoc *self, data = g_memdup2 (cmd_data, sizeof (CommandData)); + g_clear_object (&self->interrupt_cancellable); + if (wait_data_delay) { self->cmd_data_timeout = 0; - g_set_object (&self->interrupt_cancellable, g_cancellable_new ()); + self->interrupt_cancellable = g_cancellable_new (); } else { self->cmd_data_timeout = DATA_TIMEOUT; - g_clear_object (&self->interrupt_cancellable); } g_assert (self->cmd_ssm == NULL); @@ -1694,6 +1695,7 @@ fpc_dev_close (FpDevice *device) fp_dbg ("%s enter -->", G_STRFUNC); g_clear_pointer (&self->dbid, g_free); + g_cancellable_cancel (self->interrupt_cancellable); g_clear_object (&self->interrupt_cancellable); fpc_dev_release_interface (self, NULL); } @@ -1798,7 +1800,9 @@ fpc_dev_resume (FpDevice *device) g_assert (self->cmd_suspended); g_assert (fpi_ssm_get_cur_state (self->cmd_ssm) == FPC_CMD_SUSPENDED); self->cmd_suspended = FALSE; - g_set_object (&self->interrupt_cancellable, g_cancellable_new ()); + + g_clear_object (&self->interrupt_cancellable); + self->interrupt_cancellable = g_cancellable_new (); fpi_ssm_jump_to_state (self->cmd_ssm, FPC_CMD_RESUME); } From df67c08a97f2600674426e5f5f377a0b6519b654 Mon Sep 17 00:00:00 2001 From: herman lin Date: Tue, 16 Dec 2025 16:20:35 +0800 Subject: [PATCH 16/27] elanmoc: Add new PID 0xCB0 --- data/autosuspend.hwdb | 1 + libfprint/drivers/elanmoc/elanmoc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 1035e943..ec31906c 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -166,6 +166,7 @@ usb:v04F3p0C9D* usb:v04F3p0C9F* usb:v04F3p0CA3* usb:v04F3p0CA8* +usb:v04F3p0CB0* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/elanmoc/elanmoc.c b/libfprint/drivers/elanmoc/elanmoc.c index bcabd8fb..bcbb03c3 100644 --- a/libfprint/drivers/elanmoc/elanmoc.c +++ b/libfprint/drivers/elanmoc/elanmoc.c @@ -37,6 +37,7 @@ static const FpIdEntry id_table[] = { { .vid = 0x04f3, .pid = 0x0c9f, }, { .vid = 0x04f3, .pid = 0x0ca3, }, { .vid = 0x04f3, .pid = 0x0ca8, }, + { .vid = 0x04f3, .pid = 0x0cb0, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; From 87092d74ff7a1f5e61a419856742482c4d31880e Mon Sep 17 00:00:00 2001 From: Ankur Date: Sun, 7 Dec 2025 22:25:11 -0800 Subject: [PATCH 17/27] Add support for 04f3:0ca7 Elan Microelectronics Corp. ELAN:ARM-M4 --- data/autosuspend.hwdb | 1 + libfprint/drivers/elanmoc/elanmoc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index ec31906c..e40c77bd 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -165,6 +165,7 @@ usb:v04F3p0C99* usb:v04F3p0C9D* usb:v04F3p0C9F* usb:v04F3p0CA3* +usb:v04F3p0CA7* usb:v04F3p0CA8* usb:v04F3p0CB0* ID_AUTOSUSPEND=1 diff --git a/libfprint/drivers/elanmoc/elanmoc.c b/libfprint/drivers/elanmoc/elanmoc.c index bcbb03c3..1b441bdc 100644 --- a/libfprint/drivers/elanmoc/elanmoc.c +++ b/libfprint/drivers/elanmoc/elanmoc.c @@ -36,6 +36,7 @@ static const FpIdEntry id_table[] = { { .vid = 0x04f3, .pid = 0x0c9d, }, { .vid = 0x04f3, .pid = 0x0c9f, }, { .vid = 0x04f3, .pid = 0x0ca3, }, + { .vid = 0x04f3, .pid = 0x0ca7, }, { .vid = 0x04f3, .pid = 0x0ca8, }, { .vid = 0x04f3, .pid = 0x0cb0, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ From 5f1da71e708deeb9bb746f50f98c0cf184985319 Mon Sep 17 00:00:00 2001 From: Petko Bordjukov Date: Sun, 18 May 2025 20:25:12 +0300 Subject: [PATCH 18/27] synaptics: Add 06cb:00e9 Tested working on an HP x360 1040 G8 Reverts: 0edae7b64157b27ef918104886bf4ef7f72068a5 --- data/autosuspend.hwdb | 2 +- libfprint/drivers/synaptics/synaptics.c | 1 + libfprint/fprint-list-udev-hwdb.c | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index e40c77bd..6c15fb94 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -256,6 +256,7 @@ usb:v06CBp00C2* usb:v06CBp00C4* usb:v06CBp00C6* usb:v06CBp00DF* +usb:v06CBp00E9* usb:v06CBp00F0* usb:v06CBp00F9* usb:v06CBp00FC* @@ -395,7 +396,6 @@ usb:v06CBp00DA* usb:v06CBp00DC* usb:v06CBp00E4* usb:v06CBp00E7* -usb:v06CBp00E9* usb:v06CBp00FD* usb:v06CBp00FF* usb:v0A5Cp5801* diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index e37a6062..0d918053 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -36,6 +36,7 @@ static const FpIdEntry id_table[] = { { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00C4, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00C6, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00DF, }, + { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00E9, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00F0, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00F9, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00FC, }, diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c index 65854f36..0527db1e 100644 --- a/libfprint/fprint-list-udev-hwdb.c +++ b/libfprint/fprint-list-udev-hwdb.c @@ -76,7 +76,6 @@ static const FpIdEntry allowlist_id_table[] = { { .vid = 0x06cb, .pid = 0x00dc }, { .vid = 0x06cb, .pid = 0x00e4 }, { .vid = 0x06cb, .pid = 0x00e7 }, - { .vid = 0x06cb, .pid = 0x00e9 }, { .vid = 0x06cb, .pid = 0x00fd }, { .vid = 0x06cb, .pid = 0x00ff }, { .vid = 0x0a5c, .pid = 0x5801 }, From 0c97a47d8ef405cd577b87058c1e89cae9d242e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Feb 2026 03:18:14 +0100 Subject: [PATCH 19/27] Release 1.94.10 --- NEWS | 16 ++++++++++++++++ meson.build | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index b945e9f5..fb9c2290 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,22 @@ This file lists notable changes in each release. For the full history of all changes, see ChangeLog. +2026-02-10: v1.94.10 release + +Highlights: + * synaptics: Add USB reset in probe to recover stall condition + * synaptics: New PIDs 0x0169, 0x019F, 0x00E9, 0x01A0, 0x0109, 0x010A + * goodixmoc: New PID 0x66A9 + * goodixmoc: Fix crash in exit callback handler + * fpcmoc: New PID 0xA306 + * fpcmoc: Fix interrupt cancellable leak + * elanmoc: New PIDs 0x0CA7, 0x0CA8, 0x0CB0 + * egismoc: New PIDs 0x0584, 0x0588 + * focaltech_moc: New PIDs 0xA27A, 0x1579 + * realtek: New PID 0xFA03 + * fp-device: Clarify getter for open property + * libfprint: Use fatal-warnings on g-i-scanner + 2025-02-20: v1.94.9 release Highlights: diff --git a/meson.build b/meson.build index baafa19c..14fb11f2 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('libfprint', [ 'c', 'cpp' ], - version: '1.94.9', + version: '1.94.10', license: 'LGPLv2.1+', default_options: [ 'buildtype=debugoptimized', From 3f43c9356c0d63a8a446dee9de8523da1c5386cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Feb 2026 05:09:52 +0100 Subject: [PATCH 20/27] build: Ignore -Werror=unused-but-set-variable for nbis --- libfprint/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/meson.build b/libfprint/meson.build index 34494813..ae0f6e24 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -255,6 +255,7 @@ libnbis = static_library('nbis', '-Wno-discarded-qualifiers', '-Wno-array-bounds', '-Wno-array-parameter', + '-Wno-unused-but-set-variable', ]), install: false) From 1ee7df6cf2d2ac298324db7b0c3b47f65a724cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Feb 2026 05:52:40 +0100 Subject: [PATCH 21/27] ci: Add PyGObject symbols --- .gitlab-ci/libfprint-templates.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci/libfprint-templates.yaml b/.gitlab-ci/libfprint-templates.yaml index 6401cfac..60b666d4 100644 --- a/.gitlab-ci/libfprint-templates.yaml +++ b/.gitlab-ci/libfprint-templates.yaml @@ -44,4 +44,6 @@ libgusb \ libusb \ openssl \ - pixman + pixman \ + python3-gobject \ + python3-gobject-base From cc41f805f2824c4228e695c3d86c76ef09961b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Feb 2026 06:28:14 +0100 Subject: [PATCH 22/27] tests/valgrind-python.supp: Ignore PyGObject leaks They are unrelated to fprint itself, so let's ignore them --- tests/valgrind-python.supp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/valgrind-python.supp b/tests/valgrind-python.supp index ff7d7797..65bcc51a 100644 --- a/tests/valgrind-python.supp +++ b/tests/valgrind-python.supp @@ -95,3 +95,24 @@ fun:realloc obj:/usr/lib/*/libpython3*.so.* } + +{ + ignore__pygobject_possible_leaks + Memcheck:Leak + match-leak-kinds: possible + ... + obj:/usr/lib*/python*/site-packages/gi/_gi.cpython-*.so + obj:/usr/lib*/libpython3.*.so.* +} + +{ + ignore__pygobject_instance_leaks + Memcheck:Leak + match-leak-kinds: definite + ... + fun:g_type_create_instance + fun:gi_info_new_full + ... + obj:/usr/lib*/python*/site-packages/gi/_gi.cpython-*.so + obj:/usr/lib*/libpython3.*.so.* +} From fedb62f0916b908eca4c1842d04967a9c905d723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Feb 2026 06:35:26 +0100 Subject: [PATCH 23/27] ci: Make jobs interrumpible --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 878038b2..c3b2fc93 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,7 @@ include: - remote: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/71e636e012ae0ab04c5e0fe40ca73ada91ae6bde/flatpak/flatpak_ci_initiative.yml' default: + interruptible: true # Auto-retry jobs in case of infra failures retry: max: 1 From e8c55c59396a83431e91387623f527c4ad86b839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Feb 2026 05:26:37 +0100 Subject: [PATCH 24/27] data/autosuspend.hwdb: Sync with unsupported devices wiki --- data/autosuspend.hwdb | 6 ++++++ libfprint/fprint-list-udev-hwdb.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 6c15fb94..f49e17d6 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -384,10 +384,12 @@ usb:v06CBp0088* usb:v06CBp008A* usb:v06CBp009A* usb:v06CBp009B* +usb:v06CBp00A1* usb:v06CBp00A2* usb:v06CBp00A8* usb:v06CBp00B7* usb:v06CBp00BB* +usb:v06CBp00BC* usb:v06CBp00BE* usb:v06CBp00CB* usb:v06CBp00C9* @@ -416,9 +418,11 @@ usb:v0A5Cp5867* usb:v0BDAp5812* usb:v10A5p0007* usb:v10A5p9200* +usb:v10A5p9201* usb:v10A5p9800* usb:v10A5pA120* usb:v10A5pA900* +usb:v10A5pA921* usb:v10A5pE340* usb:v1188p9545* usb:v138Ap0007* @@ -472,12 +476,14 @@ usb:v27C6p5E0A* usb:v27C6p581A* usb:v27C6p589A* usb:v27C6p5F10* +usb:v27C6p5F91* usb:v27C6p6382* usb:v2808p9338* usb:v2808p9348* usb:v2808p93A9* usb:v2808pA658* usb:v2808pC652* +usb:v2808pA553* usb:v298Dp2020* usb:v298Dp2033* usb:v2DF0p0003* diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c index 0527db1e..6e2adb04 100644 --- a/libfprint/fprint-list-udev-hwdb.c +++ b/libfprint/fprint-list-udev-hwdb.c @@ -64,10 +64,12 @@ static const FpIdEntry allowlist_id_table[] = { { .vid = 0x06cb, .pid = 0x008a }, { .vid = 0x06cb, .pid = 0x009a }, { .vid = 0x06cb, .pid = 0x009b }, + { .vid = 0x06cb, .pid = 0x00a1 }, { .vid = 0x06cb, .pid = 0x00a2 }, { .vid = 0x06cb, .pid = 0x00a8 }, { .vid = 0x06cb, .pid = 0x00b7 }, { .vid = 0x06cb, .pid = 0x00bb }, + { .vid = 0x06cb, .pid = 0x00bc }, { .vid = 0x06cb, .pid = 0x00be }, { .vid = 0x06cb, .pid = 0x00cb }, { .vid = 0x06cb, .pid = 0x00c9 }, @@ -96,9 +98,11 @@ static const FpIdEntry allowlist_id_table[] = { { .vid = 0x0bda, .pid = 0x5812 }, { .vid = 0x10a5, .pid = 0x0007 }, { .vid = 0x10a5, .pid = 0x9200 }, + { .vid = 0x10a5, .pid = 0x9201 }, { .vid = 0x10a5, .pid = 0x9800 }, { .vid = 0x10a5, .pid = 0xa120 }, { .vid = 0x10a5, .pid = 0xa900 }, + { .vid = 0x10a5, .pid = 0xa921 }, { .vid = 0x10a5, .pid = 0xe340 }, { .vid = 0x1188, .pid = 0x9545 }, { .vid = 0x138a, .pid = 0x0007 }, @@ -152,12 +156,14 @@ static const FpIdEntry allowlist_id_table[] = { { .vid = 0x27c6, .pid = 0x581a }, { .vid = 0x27c6, .pid = 0x589a }, { .vid = 0x27c6, .pid = 0x5f10 }, + { .vid = 0x27c6, .pid = 0x5f91 }, { .vid = 0x27c6, .pid = 0x6382 }, { .vid = 0x2808, .pid = 0x9338 }, { .vid = 0x2808, .pid = 0x9348 }, { .vid = 0x2808, .pid = 0x93a9 }, { .vid = 0x2808, .pid = 0xa658 }, { .vid = 0x2808, .pid = 0xc652 }, + { .vid = 0x2808, .pid = 0xa553 }, { .vid = 0x298d, .pid = 0x2020 }, { .vid = 0x298d, .pid = 0x2033 }, { .vid = 0x2df0, .pid = 0x0003 }, From 2c7842c905147a2d127c1b168b2e9d432b8c91a4 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 12 Feb 2026 16:08:31 +0800 Subject: [PATCH 25/27] focaltech_moc: add new pid:0x077A pid:0x079A Tested on working hardware with examples/enroll and examples/verify Signed-off-by: Daniel Schaefer --- data/autosuspend.hwdb | 2 ++ libfprint/drivers/focaltech_moc/focaltech_moc.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index f49e17d6..9d1fd351 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -185,6 +185,8 @@ usb:v2808pA99A* usb:v2808pA57A* usb:v2808pA78A* usb:v2808p1579* +usb:v2808p077A* +usb:v2808p079A* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/focaltech_moc/focaltech_moc.c b/libfprint/drivers/focaltech_moc/focaltech_moc.c index 2d410f88..8bd93e4b 100644 --- a/libfprint/drivers/focaltech_moc/focaltech_moc.c +++ b/libfprint/drivers/focaltech_moc/focaltech_moc.c @@ -35,6 +35,8 @@ static const FpIdEntry id_table[] = { { .vid = 0x2808, .pid = 0xa57a, }, { .vid = 0x2808, .pid = 0xa78a, }, { .vid = 0x2808, .pid = 0x1579, }, + { .vid = 0x2808, .pid = 0x077A, }, + { .vid = 0x2808, .pid = 0x079A, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; From 7893c2cb36fedee295bb15caaf28bc2fa0cc08ef Mon Sep 17 00:00:00 2001 From: FIM43-Redeye <14264359+FIM43-Redeye@users.noreply.github.com> Date: Wed, 18 Feb 2026 17:14:43 -0500 Subject: [PATCH 26/27] goodixmoc: Wait for finger removal before completing verify The goodixmoc driver's verify state machine was missing a WAIT_FINGER_UP state between IDENTIFY and PWR_BTN_SHIELD_OFF. After the sensor reported a match/no-match result, the driver immediately completed the verify operation without waiting for the finger to be lifted from the sensor. This caused problems when fprintd retried verification after a no-match: the finger was still on the sensor, so the same (wrong) finger was scanned again instantly. On fast match-on-chip readers, all retry attempts could be exhausted in under a second, giving the user no time to reposition their finger. Add GOODIX_VERIFY_WAIT_FINGER_UP to the verify state machine, mirroring the existing GOODIX_ENROLL_WAIT_FINGER_UP in the enroll flow. The new state sends MOC_CMD0_FINGER_MODE / MOC_CMD1_SET_FINGER_UP to the sensor and waits for the finger to be removed before advancing to PWR_BTN_SHIELD_OFF. The callback handles the same cases as the enroll equivalent: - Timeout (5s): retry the finger-up wait - Protocol error: fail the SSM - Success: clear FINGER_STATUS_PRESENT and advance Link: https://gitlab.freedesktop.org/libfprint/fprintd/-/issues/209 --- libfprint/drivers/goodixmoc/goodix.c | 40 ++++++++++++++++++++++++++++ libfprint/drivers/goodixmoc/goodix.h | 1 + 2 files changed, 41 insertions(+) diff --git a/libfprint/drivers/goodixmoc/goodix.c b/libfprint/drivers/goodixmoc/goodix.c index 976675b4..27302193 100644 --- a/libfprint/drivers/goodixmoc/goodix.c +++ b/libfprint/drivers/goodixmoc/goodix.c @@ -480,6 +480,35 @@ fp_verify_cb (FpiDeviceGoodixMoc *self, } +static void +fp_verify_finger_mode_cb (FpiDeviceGoodixMoc *self, + gxfp_cmd_response_t *resp, + GError *error) +{ + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + /* if reach max timeout(5sec) finger not up, try again */ + if (resp->finger_status.status == GX_ERROR_WAIT_FINGER_UP_TIMEOUT) + { + fpi_ssm_jump_to_state (self->task_ssm, GOODIX_VERIFY_WAIT_FINGER_UP); + return; + } + else if (resp->finger_status.status != GX_SUCCESS) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Switch finger mode failed")); + return; + } + fpi_device_report_finger_status_changes (FP_DEVICE (self), + FP_FINGER_STATUS_NONE, + FP_FINGER_STATUS_PRESENT); + fpi_ssm_next_state (self->task_ssm); +} + static void fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) { @@ -520,6 +549,17 @@ fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) fp_verify_cb); break; + case GOODIX_VERIFY_WAIT_FINGER_UP: + { + guint8 dummy = 0; + goodix_sensor_cmd (self, MOC_CMD0_FINGER_MODE, MOC_CMD1_SET_FINGER_UP, + true, + &dummy, + 1, + fp_verify_finger_mode_cb); + } + break; + case GOODIX_VERIFY_PWR_BTN_SHIELD_OFF: goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_OFF, false, diff --git a/libfprint/drivers/goodixmoc/goodix.h b/libfprint/drivers/goodixmoc/goodix.h index 1a04d0f4..ba7fce27 100644 --- a/libfprint/drivers/goodixmoc/goodix.h +++ b/libfprint/drivers/goodixmoc/goodix.h @@ -58,6 +58,7 @@ typedef enum { GOODIX_VERIFY_PWR_BTN_SHIELD_ON = 0, GOODIX_VERIFY_CAPTURE, GOODIX_VERIFY_IDENTIFY, + GOODIX_VERIFY_WAIT_FINGER_UP, GOODIX_VERIFY_PWR_BTN_SHIELD_OFF, GOODIX_VERIFY_NUM_STATES, } GoodixVerifyState; From 66b1ada0d6b509d76ff7f4031ca34777c384d34b Mon Sep 17 00:00:00 2001 From: FIM43-Redeye <14264359+FIM43-Redeye@users.noreply.github.com> Date: Fri, 20 Feb 2026 15:00:18 -0500 Subject: [PATCH 27/27] tests: Update goodixmoc capture for verify finger-up wait Re-record the umockdev USB capture to include the new GOODIX_VERIFY_WAIT_FINGER_UP state's USB exchanges during verify and identify operations. Captured on a Goodix MOC sensor (27c6:609c, Framework Laptop 13) with FP_DEVICE_EMULATION=1 as required by the test framework. --- tests/goodixmoc/custom.pcapng | Bin 64852 -> 73264 bytes tests/goodixmoc/device | 418 +++++++++++++++++++++++++--------- 2 files changed, 315 insertions(+), 103 deletions(-) diff --git a/tests/goodixmoc/custom.pcapng b/tests/goodixmoc/custom.pcapng index 91925244702c6a94ad5524bbb0ae5a3c4e42b1d8..79c549f2927db3b3010cb35f42f86555b0ae0fa7 100644 GIT binary patch literal 73264 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