From 1372d6f0815f3358ddc1cf4ec831ed2b254a1efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 17 Aug 2023 21:17:26 +0200 Subject: [PATCH 01/64] tests: Use native GTest utils to generate assets names --- tests/meson.build | 14 +++++++++----- tests/test-fpi-assembling.c | 4 +--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index b172c1ae..31040d1d 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -6,6 +6,8 @@ envs.set('G_MESSAGES_DEBUG', 'all') # Setup paths envs.set('MESON_SOURCE_ROOT', meson.project_source_root()) envs.set('MESON_BUILD_ROOT', meson.project_build_root()) +envs.set('G_TEST_SRCDIR', meson.current_source_dir()) +envs.set('G_TEST_BUILDDIR', meson.current_build_dir()) envs.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libfprint') # Set FP_DEVICE_EMULATION so that drivers can adapt (e.g. to use fixed @@ -71,6 +73,12 @@ envs_str = run_command(python3, '-c', env_parser_cmd, env: envs, check: installed_tests).stdout().strip() +envs_str = ' '.join([ + envs_str, + 'G_TEST_SRCDIR=' + installed_tests_testdir, + 'G_TEST_BUILDDIR=' + installed_tests_execdir, +]) + if get_option('introspection') envs.prepend('GI_TYPELIB_PATH', meson.project_build_root() / 'libfprint') virtual_devices_tests = [ @@ -247,10 +255,6 @@ endif unit_tests_deps = { 'fpi-assembling' : [cairo_dep] } -test_config = configuration_data() -test_config.set_quoted('SOURCE_ROOT', meson.project_source_root()) -test_config_h = configure_file(output: 'test-config.h', configuration: test_config) - foreach test_name: unit_tests if unit_tests_deps.has_key(test_name) missing_deps = false @@ -278,7 +282,7 @@ foreach test_name: unit_tests basename = 'test-' + test_name test_exe = executable(basename, - sources: [basename + '.c', test_config_h], + sources: basename + '.c', dependencies: [ libfprint_private_dep ] + extra_deps, c_args: common_cflags, link_whole: test_utils, diff --git a/tests/test-fpi-assembling.c b/tests/test-fpi-assembling.c index 94b8fe5e..c6dae6fe 100644 --- a/tests/test-fpi-assembling.c +++ b/tests/test-fpi-assembling.c @@ -22,7 +22,6 @@ #include #include "fpi-assembling.h" #include "fpi-image.h" -#include "test-config.h" typedef struct { @@ -67,8 +66,7 @@ test_frame_assembling (void) g_autoptr(FpImage) fp_img = NULL; GSList *frames = NULL; - g_assert_false (SOURCE_ROOT == NULL); - path = g_build_path (G_DIR_SEPARATOR_S, SOURCE_ROOT, "tests", "vfs5011", "capture.png", NULL); + path = g_test_build_filename (G_TEST_DIST, "vfs5011", "capture.png", NULL); img = cairo_image_surface_create_from_png (path); data = cairo_image_surface_get_data (img); From b924d715c964eb229bb1d0fd21d7084f4b4efdec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 17 Aug 2023 20:38:26 +0200 Subject: [PATCH 02/64] ci: Move build dir during installed tests so we check that we don't use built artifacts --- .gitlab-ci.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fd03751e..590eb203 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -125,17 +125,19 @@ test_installed: script: - meson setup _build --prefix=/usr -Ddrivers=all - meson install -C _build + - mv _build _build_dir + - rm -rf tests - gnome-desktop-testing-runner --list libfprint-2 - gnome-desktop-testing-runner libfprint-2 - --report-directory=_build/installed-tests-report/failed/ - --log-directory=_build/installed-tests-report/logs/ + --report-directory=_installed-tests-report/failed/ + --log-directory=_installed-tests-report/logs/ --parallel=0 artifacts: expose_as: 'GNOME Tests Runner logs' when: always paths: - - _build/meson-logs - - _build/installed-tests-report + - _build_dir/meson-logs + - _installed-tests-report expire_in: 1 week From af3dca90031b39d657b6fc1cc06f25498bbad292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 23 Aug 2023 01:04:33 +0200 Subject: [PATCH 03/64] tests/egis0570/capture.pcapng: Remove execution permission --- tests/egis0570/capture.pcapng | Bin 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tests/egis0570/capture.pcapng diff --git a/tests/egis0570/capture.pcapng b/tests/egis0570/capture.pcapng old mode 100755 new mode 100644 From 206e92218c0209d70f156af08a17f9d945fa1b63 Mon Sep 17 00:00:00 2001 From: Vincent Huang Date: Tue, 4 Jul 2023 15:45:36 +0800 Subject: [PATCH 04/64] synaptics: fix enroll_identify problem after user reset database --- libfprint/drivers/synaptics/synaptics.c | 23 ++++++++++++++--------- libfprint/drivers/synaptics/synaptics.h | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index f13b820b..99717c1e 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -106,7 +106,11 @@ cmd_receive_cb (FpiUsbTransfer *transfer, if (self->cmd_complete_on_removal) { - fpi_ssm_mark_completed (transfer->ssm); + if (self->delay_error) + fpi_ssm_mark_failed (transfer->ssm, + g_steal_pointer (&self->delay_error)); + else + fpi_ssm_mark_completed (transfer->ssm); return; } } @@ -641,18 +645,20 @@ verify (FpDevice *device) } static void -identify_complete_after_finger_removal (FpiDeviceSynaptics *self) +identify_complete_after_finger_removal (FpiDeviceSynaptics *self, GError *error) { FpDevice *device = FP_DEVICE (self); if (self->finger_on_sensor) { fp_dbg ("delaying identify report until after finger removal!"); + g_propagate_error (&self->delay_error, error); + self->cmd_complete_on_removal = TRUE; } else { - fpi_device_identify_complete (device, NULL); + fpi_device_identify_complete (device, error); } } @@ -702,19 +708,18 @@ identify_msg_cb (FpiDeviceSynaptics *self, fp_info ("Match error occurred"); fpi_device_identify_report (device, NULL, NULL, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); - identify_complete_after_finger_removal (self); + identify_complete_after_finger_removal (self, NULL); } else if (resp->result == BMKT_FP_NO_MATCH) { fp_info ("Print didn't match"); fpi_device_identify_report (device, NULL, NULL, NULL); - identify_complete_after_finger_removal (self); + identify_complete_after_finger_removal (self, NULL); } - else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS) + else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS || resp->result == BMKT_FP_DATABASE_EMPTY) { fp_info ("Print is not in database"); - fpi_device_identify_complete (device, - fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND)); + identify_complete_after_finger_removal (self, fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND)); } else { @@ -750,7 +755,7 @@ identify_msg_cb (FpiDeviceSynaptics *self, else fpi_device_identify_report (device, NULL, print, NULL); - identify_complete_after_finger_removal (self); + identify_complete_after_finger_removal (self, NULL); } } } diff --git a/libfprint/drivers/synaptics/synaptics.h b/libfprint/drivers/synaptics/synaptics.h index 5fc0a192..6138db3f 100644 --- a/libfprint/drivers/synaptics/synaptics.h +++ b/libfprint/drivers/synaptics/synaptics.h @@ -127,4 +127,5 @@ struct _FpiDeviceSynaptics struct syna_enroll_resp_data enroll_resp_data; syna_state_t state; + GError *delay_error; }; From 5bff5bfea6fd28a63277d4132dbd086c84bcf2c2 Mon Sep 17 00:00:00 2001 From: Vincent Huang Date: Fri, 8 Sep 2023 12:47:53 +0800 Subject: [PATCH 05/64] synaptics: Add null check to prevent g_propagate_error assertion failure --- libfprint/drivers/synaptics/synaptics.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 99717c1e..98ef199d 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -652,7 +652,8 @@ identify_complete_after_finger_removal (FpiDeviceSynaptics *self, GError *error) if (self->finger_on_sensor) { fp_dbg ("delaying identify report until after finger removal!"); - g_propagate_error (&self->delay_error, error); + if (error) + g_propagate_error (&self->delay_error, error); self->cmd_complete_on_removal = TRUE; } From eb01d7c97d3299f3577e8fc6d0a1e2043518a642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Sep 2023 16:21:04 +0200 Subject: [PATCH 06/64] ci: Fix building flatpak using GNOME 42 runtime Use versioned ci template so we are sure what we're using --- .gitlab-ci.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 590eb203..acd0b6db 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ include: - project: 'freedesktop/ci-templates' ref: master file: '/templates/fedora.yml' - - remote: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/master/flatpak/flatpak_ci_initiative.yml' + - remote: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/21e7c107/flatpak/flatpak_ci_initiative.yml' default: # Auto-retry jobs in case of infra failures @@ -22,7 +22,6 @@ variables: 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" - BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" workflow: @@ -177,14 +176,18 @@ test_unsupported_list: - tests/hwdb-check-unsupported.py flatpak: - stage: flatpak - extends: .flatpak + #stage: flatpak + stage: image-build + extends: .flatpak@x86_64 # From https://gitlab.gnome.org/GNOME/gnome-runtime-images/container_registry - image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:42 + image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/x86_64/gnome:42 variables: MANIFEST_PATH: "demo/org.freedesktop.libfprint.Demo.json" FLATPAK_MODULE: "libfprint" APP_ID: "org.freedesktop.libfprint.Demo" + BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" + # Build with any builder + tags: [] rules: - if: '$CI_PROJECT_PATH != "libfprint/libfprint"' when: never From 26d2c77c3df6f0a3367f27e9b802a094bef0f492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Sep 2023 16:25:50 +0200 Subject: [PATCH 07/64] ci: Allow manual flatpak build in any fork and MR --- .gitlab-ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index acd0b6db..9ddc7260 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -176,8 +176,7 @@ test_unsupported_list: - tests/hwdb-check-unsupported.py flatpak: - #stage: flatpak - stage: image-build + stage: flatpak extends: .flatpak@x86_64 # From https://gitlab.gnome.org/GNOME/gnome-runtime-images/container_registry image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/x86_64/gnome:42 @@ -190,7 +189,8 @@ flatpak: tags: [] rules: - if: '$CI_PROJECT_PATH != "libfprint/libfprint"' - when: never + when: manual + allow_failure: true - if: '$CI_PIPELINE_SOURCE == "schedule"' when: never - if: '$CI_COMMIT_BRANCH == "master"' @@ -202,6 +202,9 @@ flatpak: - if: '$CI_COMMIT_BRANCH' when: manual allow_failure: true + - if: '$CI_MERGE_REQUEST_ID' + when: manual + allow_failure: true # CONTAINERS creation stage .container_fedora_build_base: From efba965b0cfd0b4bbf1608e604a5201edebb9168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Sep 2023 17:18:42 +0200 Subject: [PATCH 08/64] ci: Build flatpak using gnome master Also bump dependencies versions --- .gitlab-ci.yml | 5 ++--- demo/org.freedesktop.libfprint.Demo.json | 14 +++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9ddc7260..8bea6726 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ include: - project: 'freedesktop/ci-templates' ref: master file: '/templates/fedora.yml' - - remote: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/21e7c107/flatpak/flatpak_ci_initiative.yml' + - remote: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/master/flatpak/flatpak_ci_initiative.yml' default: # Auto-retry jobs in case of infra failures @@ -178,13 +178,12 @@ test_unsupported_list: flatpak: stage: flatpak extends: .flatpak@x86_64 - # From https://gitlab.gnome.org/GNOME/gnome-runtime-images/container_registry - image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/x86_64/gnome:42 variables: MANIFEST_PATH: "demo/org.freedesktop.libfprint.Demo.json" FLATPAK_MODULE: "libfprint" APP_ID: "org.freedesktop.libfprint.Demo" BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" + RUNTIME_REPO: "https://nightly.gnome.org/gnome-nightly.flatpakrepo" # Build with any builder tags: [] rules: diff --git a/demo/org.freedesktop.libfprint.Demo.json b/demo/org.freedesktop.libfprint.Demo.json index 8fd52f56..55e6a278 100644 --- a/demo/org.freedesktop.libfprint.Demo.json +++ b/demo/org.freedesktop.libfprint.Demo.json @@ -1,7 +1,7 @@ { "app-id": "org.freedesktop.libfprint.Demo", "runtime": "org.gnome.Platform", - "runtime-version": "42", + "runtime-version": "master", "sdk": "org.gnome.Sdk", "command": "gtk-libfprint-test", "finish-args": [ @@ -38,24 +38,24 @@ { "name": "libgusb", "buildsystem": "meson", - "config-opts": [ "-Dtests=false", "-Dvapi=false", "-Ddocs=false", "-Dintrospection=false" ], + "config-opts": [ "-Dtests=false", "-Dvapi=false", "-Ddocs=false" ], "sources": [ { "type": "archive", - "url": "https://github.com/hughsie/libgusb/archive/0.3.0.tar.gz", - "sha256": "b36310f8405d5fd68f6caf4a829f7ab4c627b38fd3d02a139d411fce0f3a49f1" + "url": "https://github.com/hughsie/libgusb/releases/download/0.4.6/libgusb-0.4.6.tar.xz", + "sha256": "1b0422bdcd72183272ac42eec9398c5a0bc48a02f618fa3242c468cbbd003049" } ] }, { "name": "gudev", "buildsystem": "meson", - "config-opts": [ "-Dtests=disabled", "-Dintrospection=disabled" ], + "config-opts": [ "-Dtests=disabled", "-Dintrospection=disabled", "-Dvapi=disabled" ], "sources": [ { "type": "archive", - "url": "https://download.gnome.org/sources/libgudev/236/libgudev-236.tar.xz", - "sha256": "e50369d06d594bae615eb7aeb787de304ebaad07a26d1043cef8e9c7ab7c9524" + "url": "https://download.gnome.org/sources/libgudev/238/libgudev-238.tar.xz", + "sha256": "61266ab1afc9d73dbc60a8b2af73e99d2fdff47d99544d085760e4fa667b5dd1" } ] }, From 0eae0e8cc0ebc23dabfe2048345e26c46460f993 Mon Sep 17 00:00:00 2001 From: swbgdx Date: Thu, 21 Sep 2023 18:11:23 +0800 Subject: [PATCH 09/64] goodixmoc: Add PID 0x6582 --- data/autosuspend.hwdb | 1 + libfprint/drivers/goodixmoc/goodix.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 603c5c1f..c9746756 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -186,6 +186,7 @@ usb:v27C6p63AC* usb:v27C6p63BC* usb:v27C6p63CC* usb:v27C6p6496* +usb:v27C6p6582* usb:v27C6p6584* usb:v27C6p658C* usb:v27C6p6592* diff --git a/libfprint/drivers/goodixmoc/goodix.c b/libfprint/drivers/goodixmoc/goodix.c index bacb4845..5a3fface 100644 --- a/libfprint/drivers/goodixmoc/goodix.c +++ b/libfprint/drivers/goodixmoc/goodix.c @@ -1374,6 +1374,7 @@ gx_fp_probe (FpDevice *device) case 0x63AC: case 0x63BC: case 0x63CC: + case 0x6582: case 0x6A94: case 0x659A: self->max_enroll_stage = 12; @@ -1623,6 +1624,7 @@ static const FpIdEntry id_table[] = { { .vid = 0x27c6, .pid = 0x63BC, }, { .vid = 0x27c6, .pid = 0x63CC, }, { .vid = 0x27c6, .pid = 0x6496, }, + { .vid = 0x27c6, .pid = 0x6582, }, { .vid = 0x27c6, .pid = 0x6584, }, { .vid = 0x27c6, .pid = 0x658C, }, { .vid = 0x27c6, .pid = 0x6592, }, From 2414dbdbd46e762b944c2dc421c5fe820e8d4e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Sep 2023 19:37:15 +0200 Subject: [PATCH 10/64] libfprint/fprint-list-udev-hwdb: Update unsupported devices from wiki --- data/autosuspend.hwdb | 10 ++++++++++ libfprint/fprint-list-udev-hwdb.c | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index c9746756..eb703a79 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -285,6 +285,7 @@ usb:v138Ap0091* ID_PERSIST=0 # Known unsupported devices +usb:v047Dp00F2* usb:v04E8p730B* usb:v04F3p036B* usb:v04F3p0C00* @@ -292,9 +293,12 @@ usb:v04F3p0C4C* usb:v04F3p0C57* usb:v04F3p0C5E* usb:v04F3p0C5A* +usb:v04F3p0C6C* usb:v04F3p0C70* usb:v04F3p0C72* +usb:v04F3p0C77* usb:v04F3p2706* +usb:v04F3p3032* usb:v04F3p3057* usb:v04F3p3104* usb:v04F3p310D* @@ -343,12 +347,16 @@ usb:v138Ap0094* usb:v138Ap0097* usb:v138Ap009D* usb:v138Ap00AB* +usb:v138Ap00A6* usb:v147Ep1002* usb:v1491p0088* usb:v16D1p1027* usb:v1C7Ap0300* usb:v1C7Ap0575* usb:v1C7Ap0576* +usb:v1C7Ap0577* +usb:v1C7Ap0582* +usb:v1C7Ap05A1* usb:v27C6p5042* usb:v27C6p5110* usb:v27C6p5117* @@ -376,6 +384,8 @@ usb:v27C6p55B4* usb:v27C6p5740* usb:v27C6p5E0A* usb:v27C6p581A* +usb:v27C6p589A* +usb:v27C6p6382* usb:v2808p9338* usb:v2808p93A9* usb:v298Dp2020* diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c index cf1c4ecf..7c41050b 100644 --- a/libfprint/fprint-list-udev-hwdb.c +++ b/libfprint/fprint-list-udev-hwdb.c @@ -29,6 +29,7 @@ static const FpIdEntry whitelist_id_table[] = { * You can generate this list from the wiki page using e.g.: * gio cat https://gitlab.freedesktop.org/libfprint/wiki/-/wikis/Unsupported-Devices.md | sed -n 's!|.*\([0-9a-fA-F]\{4\}\):\([0-9a-fA-F]\{4\}\).*|.*! { .vid = 0x\1, .pid = 0x\2 },!p' */ + { .vid = 0x047d, .pid = 0x00f2 }, { .vid = 0x04e8, .pid = 0x730b }, { .vid = 0x04f3, .pid = 0x036b }, { .vid = 0x04f3, .pid = 0x0c00 }, @@ -36,9 +37,12 @@ static const FpIdEntry whitelist_id_table[] = { { .vid = 0x04f3, .pid = 0x0c57 }, { .vid = 0x04f3, .pid = 0x0c5e }, { .vid = 0x04f3, .pid = 0x0c5a }, + { .vid = 0x04f3, .pid = 0x0c6c }, { .vid = 0x04f3, .pid = 0x0c70 }, { .vid = 0x04f3, .pid = 0x0c72 }, + { .vid = 0x04f3, .pid = 0x0c77 }, { .vid = 0x04f3, .pid = 0x2706 }, + { .vid = 0x04f3, .pid = 0x3032 }, { .vid = 0x04f3, .pid = 0x3057 }, { .vid = 0x04f3, .pid = 0x3104 }, { .vid = 0x04f3, .pid = 0x310d }, @@ -87,12 +91,16 @@ static const FpIdEntry whitelist_id_table[] = { { .vid = 0x138a, .pid = 0x0097 }, { .vid = 0x138a, .pid = 0x009d }, { .vid = 0x138a, .pid = 0x00ab }, + { .vid = 0x138a, .pid = 0x00a6 }, { .vid = 0x147e, .pid = 0x1002 }, { .vid = 0x1491, .pid = 0x0088 }, { .vid = 0x16d1, .pid = 0x1027 }, { .vid = 0x1c7a, .pid = 0x0300 }, { .vid = 0x1c7a, .pid = 0x0575 }, { .vid = 0x1c7a, .pid = 0x0576 }, + { .vid = 0x1c7a, .pid = 0x0577 }, + { .vid = 0x1c7a, .pid = 0x0582 }, + { .vid = 0x1c7a, .pid = 0x05a1 }, { .vid = 0x27c6, .pid = 0x5042 }, { .vid = 0x27c6, .pid = 0x5110 }, { .vid = 0x27c6, .pid = 0x5117 }, @@ -120,6 +128,8 @@ static const FpIdEntry whitelist_id_table[] = { { .vid = 0x27c6, .pid = 0x5740 }, { .vid = 0x27c6, .pid = 0x5e0a }, { .vid = 0x27c6, .pid = 0x581a }, + { .vid = 0x27c6, .pid = 0x589a }, + { .vid = 0x27c6, .pid = 0x6382 }, { .vid = 0x2808, .pid = 0x9338 }, { .vid = 0x2808, .pid = 0x93a9 }, { .vid = 0x298d, .pid = 0x2020 }, From 3ebd2c3f9745b80d02e5724c5737ff023be04fae Mon Sep 17 00:00:00 2001 From: Aris Lin Date: Thu, 19 Oct 2023 14:25:09 +0800 Subject: [PATCH 11/64] synaptics: Add new PID 0x0173 --- 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 eb703a79..6f5fcf20 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -217,6 +217,7 @@ usb:v06CBp0129* usb:v06CBp0168* usb:v06CBp015F* usb:v06CBp0104* +usb:v06CBp0173* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 98ef199d..c178abbb 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -45,6 +45,7 @@ static const FpIdEntry id_table[] = { { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0168, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x015F, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0104, }, + { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0173, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; From 427139f347c576e08d6656142ba39d06c7be2b15 Mon Sep 17 00:00:00 2001 From: Aris Lin Date: Tue, 24 Oct 2023 14:56:15 +0800 Subject: [PATCH 12/64] synaptics: Add new PID 0x0106 --- 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 6f5fcf20..35abc907 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -218,6 +218,7 @@ usb:v06CBp0168* usb:v06CBp015F* usb:v06CBp0104* usb:v06CBp0173* +usb:v06CBp0106* ID_AUTOSUSPEND=1 ID_PERSIST=0 diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index c178abbb..55e4d44c 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -46,6 +46,7 @@ static const FpIdEntry id_table[] = { { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x015F, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0104, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0173, }, + { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0106, }, { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ }; From 79be91831c564f6c8b1af4c0c52c330632923acb Mon Sep 17 00:00:00 2001 From: huan_huang Date: Fri, 4 Aug 2023 10:21:17 +0800 Subject: [PATCH 13/64] drivers: add realtek rts5813 driver --- data/autosuspend.hwdb | 5 + libfprint/drivers/realtek/realtek.c | 1220 +++++++++++++++++++++++++++ libfprint/drivers/realtek/realtek.h | 220 +++++ libfprint/meson.build | 2 + meson.build | 1 + tests/meson.build | 1 + tests/realtek/custom.pcapng | Bin 0 -> 29128 bytes tests/realtek/custom.py | 110 +++ tests/realtek/device | 240 ++++++ 9 files changed, 1799 insertions(+) create mode 100644 libfprint/drivers/realtek/realtek.c create mode 100644 libfprint/drivers/realtek/realtek.h create mode 100644 tests/realtek/custom.pcapng create mode 100755 tests/realtek/custom.py create mode 100644 tests/realtek/device diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 35abc907..2b4dc706 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -202,6 +202,11 @@ usb:v298Dp1010* ID_AUTOSUSPEND=1 ID_PERSIST=0 +# Supported by libfprint driver realtek +usb:v0BDAp5813* + ID_AUTOSUSPEND=1 + ID_PERSIST=0 + # Supported by libfprint driver synaptics usb:v06CBp00BD* usb:v06CBp00DF* diff --git a/libfprint/drivers/realtek/realtek.c b/libfprint/drivers/realtek/realtek.c new file mode 100644 index 00000000..87b8c805 --- /dev/null +++ b/libfprint/drivers/realtek/realtek.c @@ -0,0 +1,1220 @@ +/* + * Copyright (C) 2022-2023 Realtek Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FP_COMPONENT "realtek" + +#include "drivers_api.h" + +#include "fpi-byte-reader.h" + +#include "realtek.h" + +G_DEFINE_TYPE (FpiDeviceRealtek, fpi_device_realtek, FP_TYPE_DEVICE) + +static const FpIdEntry id_table[] = { + { .vid = 0x0bda, .pid = 0x5813, }, + { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ +}; + +static gboolean +parse_print_data (GVariant *data, + guint8 *finger, + const guint8 **user_id, + gsize *user_id_len) +{ + g_autoptr(GVariant) user_id_var = NULL; + + g_return_val_if_fail (data, FALSE); + g_return_val_if_fail (finger, FALSE); + g_return_val_if_fail (user_id, FALSE); + g_return_val_if_fail (user_id_len, FALSE); + + *user_id = NULL; + *user_id_len = 0; + *finger = 0; + + if (!g_variant_check_format_string (data, "(y@ay)", FALSE)) + return FALSE; + + g_variant_get (data, + "(y@ay)", + finger, + &user_id_var); + + *user_id = g_variant_get_fixed_array (user_id_var, user_id_len, 1); + + if (*user_id_len <= 0 || *user_id_len > DEFAULT_UID_LEN) + return FALSE; + + if (*user_id[0] == '\0' || *user_id[0] == ' ') + return FALSE; + + if (*finger != SUB_FINGER_01) + return FALSE; + + return TRUE; +} + +static void +fp_cmd_ssm_done_data_free (CommandData *data) +{ + g_free (data); +} + +/* data callbacks */ + +static void +fp_task_ssm_generic_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fpi_ssm_next_state (self->task_ssm); +} + +static void +fp_finish_capture_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + gint capture_status = buffer_in[0]; + if (capture_status == 0) + { + fpi_device_report_finger_status_changes (FP_DEVICE (self), + FP_FINGER_STATUS_PRESENT, + FP_FINGER_STATUS_NEEDED); + fpi_ssm_next_state (self->task_ssm); + } + else + { + fpi_ssm_jump_to_state (self->task_ssm, + fpi_ssm_get_cur_state (self->task_ssm)); + } +} + +static void +fp_accept_sample_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + fpi_device_report_finger_status_changes (FP_DEVICE (self), + FP_FINGER_STATUS_NONE, + FP_FINGER_STATUS_PRESENT); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + gint in_status = buffer_in[0]; + + if (self->fp_purpose != FP_RTK_PURPOSE_ENROLL) + { + /* verify or identify purpose process */ + fpi_ssm_next_state (self->task_ssm); + return; + } + else + { + /* enroll purpose process */ + if (in_status == FP_RTK_CMD_ERR) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Command error!")); + return; + } + + if (self->enroll_stage < self->max_enroll_stage) + { + if (in_status == FP_RTK_SUCCESS) + { + self->enroll_stage++; + fpi_device_enroll_progress (FP_DEVICE (self), self->enroll_stage, NULL, NULL); + fpi_ssm_jump_to_state (self->task_ssm, FP_RTK_ENROLL_CAPTURE); + } + else if (in_status == FP_RTK_MATCH_FAIL) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID, + "InStatus invalid!")); + } + else + { + fpi_device_enroll_progress (FP_DEVICE (self), + self->enroll_stage, + NULL, + fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); + + fpi_ssm_jump_to_state (self->task_ssm, FP_RTK_ENROLL_CAPTURE); + } + return; + } + fpi_ssm_next_state (self->task_ssm); + } +} + +static FpPrint * +fp_print_from_data (FpiDeviceRealtek *self, uint8_t *buffer) +{ + FpPrint *print; + GVariant *data; + GVariant *uid; + guint finger; + gsize userid_len; + g_autofree gchar *userid = NULL; + + userid = g_strndup ((gchar *) buffer + 1, DEFAULT_UID_LEN); + finger = *(buffer); + + print = fp_print_new (FP_DEVICE (self)); + userid_len = MIN (DEFAULT_UID_LEN, strlen (userid)); + + uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + userid, + userid_len, + 1); + + data = g_variant_new ("(y@ay)", + finger, + uid); + + fpi_print_set_type (print, FPI_PRINT_RAW); + fpi_print_set_device_stored (print, TRUE); + g_object_set (print, "fpi-data", data, NULL); + g_object_set (print, "description", userid, NULL); + fpi_print_fill_from_user_id (print, userid); + + return print; +} + +static void +fp_identify_feature_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + FpDevice *device = FP_DEVICE (self); + FpPrint *match = NULL; + FpPrint *print = NULL; + FpiDeviceAction current_action; + + g_autoptr(GPtrArray) templates = NULL; + gboolean found = FALSE; + + current_action = fpi_device_get_current_action (device); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + gint in_status = buffer_in[0]; + if (in_status == FP_RTK_CMD_ERR) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Command error!")); + return; + } + + if (in_status >= FP_RTK_TOO_HIGH && in_status <= FP_RTK_MERGE_FAILURE) + { + GError *retry_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + retry_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + fpi_ssm_mark_failed (self->task_ssm, retry_error); + return; + } + + if (in_status == FP_RTK_SUCCESS) + { + match = fp_print_from_data (self, buffer_in + 1); + + if (current_action == FPI_DEVICE_ACTION_VERIFY) + { + templates = g_ptr_array_sized_new (1); + fpi_device_get_verify_data (device, &print); + g_ptr_array_add (templates, print); + } + else + { + fpi_device_get_identify_data (device, &templates); + g_ptr_array_ref (templates); + } + + for (gint cnt = 0; cnt < templates->len; cnt++) + { + print = g_ptr_array_index (templates, cnt); + + if (fp_print_equal (print, match)) + { + found = TRUE; + break; + } + } + + if (found) + { + if (current_action == FPI_DEVICE_ACTION_VERIFY) + { + fpi_device_verify_report (device, FPI_MATCH_SUCCESS, match, error); + fpi_ssm_next_state (self->task_ssm); + } + else + { + fpi_device_identify_report (device, print, match, error); + fpi_ssm_mark_completed (self->task_ssm); + } + return; + } + } + + if (!found) + { + if (current_action == FPI_DEVICE_ACTION_VERIFY) + fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, error); + else + fpi_device_identify_report (device, NULL, NULL, error); + + fpi_ssm_jump_to_state (self->task_ssm, FP_RTK_VERIFY_NUM_STATES); + } +} + +static void +fp_get_delete_pos_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + FpPrint *print = NULL; + + g_autoptr(GVariant) data = NULL; + gsize user_id_len = 0; + const guint8 *user_id; + guint8 finger; + gboolean found = FALSE; + gchar temp_userid[DEFAULT_UID_LEN + 1] = {0}; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fpi_device_get_delete_data (FP_DEVICE (self), &print); + g_object_get (print, "fpi-data", &data, NULL); + + if (!parse_print_data (data, &finger, &user_id, &user_id_len)) + { + fpi_device_delete_complete (FP_DEVICE (self), + fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + return; + } + + for (gint i = 0; i < self->template_num; i++) + { + if (buffer_in[i * TEMPLATE_LEN] != 0) + { + memcpy (temp_userid, buffer_in + i * TEMPLATE_LEN + UID_OFFSET, DEFAULT_UID_LEN); + if (g_strcmp0 (fp_print_get_description (print), (const char *) temp_userid) == 0) + { + self->pos_index = i; + found = TRUE; + break; + } + } + } + + if (!found) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Get template position failed!")); + return; + } + + fpi_ssm_next_state (self->task_ssm); +} + +static void +fp_get_enroll_num_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + self->template_num = buffer_in[1]; + + fpi_ssm_next_state (self->task_ssm); +} + +static void +fp_get_template_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + gboolean found = FALSE; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + for (gint i = 0; i < self->template_num; i++) + { + if (buffer_in[i * TEMPLATE_LEN] == 0) + { + self->pos_index = i; + found = TRUE; + break; + } + } + + if (!found) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "No free template was found!")); + return; + } + + fpi_ssm_next_state (self->task_ssm); +} + +static void +fp_check_duplicate_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + gint in_status = buffer_in[0]; + if (in_status == FP_RTK_CMD_ERR) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Command error!")); + return; + } + + if (in_status == FP_RTK_SUCCESS) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Current fingerprint is duplicate!")); + } + else if (in_status == FP_RTK_MATCH_FAIL) + { + fpi_ssm_next_state (self->task_ssm); + } + else + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID, + "InStatus invalid!")); + } +} + +static void +fp_list_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + gboolean found = FALSE; + + g_autoptr(GPtrArray) list_result = NULL; + + if (error) + { + fpi_device_list_complete (FP_DEVICE (self), NULL, error); + return; + } + + list_result = g_ptr_array_new_with_free_func (g_object_unref); + + for (gint i = 0; i < self->template_num; i++) + { + if (buffer_in[i * TEMPLATE_LEN] != 0) + { + FpPrint *print = NULL; + print = fp_print_from_data (self, buffer_in + i * TEMPLATE_LEN + SUBFACTOR_OFFSET); + g_ptr_array_add (list_result, g_object_ref_sink (print)); + found = TRUE; + } + } + + if (!found) + { + fpi_device_list_complete (FP_DEVICE (self), + g_steal_pointer (&list_result), + fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_FULL, + "Database is empty")); + return; + } + + fp_info ("Query templates complete!"); + fpi_device_list_complete (FP_DEVICE (self), + g_steal_pointer (&list_result), + NULL); +} + +static void +fp_clear_storage_cb (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error) +{ + FpDevice *device = FP_DEVICE (self); + + if (error) + { + fpi_device_clear_storage_complete (device, error); + return; + } + + fp_info ("Successfully cleared storage"); + fpi_device_clear_storage_complete (device, NULL); +} + + +static gint +parse_status (guint8 *buffer, gint status_type) +{ + switch (status_type) + { + case FP_RTK_MSG_PLAINTEXT_NO_STATUS: + return 0; + break; + + case FP_RTK_MSG_PLAINTEXT: + return buffer[0]; + break; + + default: + return 1; + break; + } +} + +static void +fp_cmd_receive_cb (FpiUsbTransfer *transfer, + FpDevice *device, + gpointer user_data, + GError *error) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + CommandData *data = user_data; + gint ssm_state = 0; + gint status_flag = 1; + + if (error) + { + fpi_ssm_mark_failed (transfer->ssm, error); + return; + } + if (data == NULL) + { + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + return; + } + + ssm_state = fpi_ssm_get_cur_state (transfer->ssm); + + /* skip zero length package */ + if (transfer->actual_length == 0) + { + fpi_ssm_jump_to_state (transfer->ssm, ssm_state); + return; + } + + /* get data */ + if (ssm_state == FP_RTK_CMD_TRANS_DATA) + { + g_autofree guchar *read_buf = NULL; + + read_buf = g_malloc0 (sizeof (guchar) * (self->trans_data_len)); + memcpy (read_buf, transfer->buffer, self->trans_data_len); + self->read_data = g_steal_pointer (&read_buf); + + fpi_ssm_next_state (transfer->ssm); + return; + } + + /* get status */ + status_flag = parse_status (transfer->buffer, self->message_type); + if (status_flag != 0) + { + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Status check failed")); + return; + } + + if (data->callback) + data->callback (self, self->read_data, NULL); + + if (self->read_data) + g_clear_pointer (&self->read_data, g_free); + + fpi_ssm_mark_completed (transfer->ssm); +} + +static void +fp_cmd_run_state (FpiSsm *ssm, FpDevice *dev) +{ + FpiUsbTransfer *transfer = NULL; + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (dev); + + switch (fpi_ssm_get_cur_state (ssm)) + { + case FP_RTK_CMD_SEND: + if (self->cmd_transfer) + { + self->cmd_transfer->ssm = ssm; + fpi_usb_transfer_submit (g_steal_pointer (&self->cmd_transfer), + CMD_TIMEOUT, + NULL, + fpi_ssm_usb_transfer_cb, + NULL); + } + else + { + fpi_ssm_next_state (ssm); + } + break; + + case FP_RTK_CMD_TRANS_DATA: + if (self->cmd_type == FP_RTK_CMD_ONLY) + { + fpi_ssm_jump_to_state (ssm, FP_RTK_CMD_GET_STATUS); + break; + } + + if (self->cmd_type == FP_RTK_CMD_WRITE) + { + if (self->data_transfer) + { + self->data_transfer->ssm = ssm; + fpi_usb_transfer_submit (g_steal_pointer (&self->data_transfer), + DATA_TIMEOUT, + NULL, + fpi_ssm_usb_transfer_cb, + NULL); + } + else + { + fpi_ssm_next_state (ssm); + } + } + else /* CMD_READ */ + { + transfer = fpi_usb_transfer_new (dev); + transfer->ssm = ssm; + fpi_usb_transfer_fill_bulk (transfer, EP_IN, EP_IN_MAX_BUF_SIZE); + + fpi_usb_transfer_submit (transfer, + self->cmd_cancellable ? 0 : DATA_TIMEOUT, + self->cmd_cancellable ? fpi_device_get_cancellable (dev) : NULL, + fp_cmd_receive_cb, + fpi_ssm_get_data (ssm)); + } + break; + + case FP_RTK_CMD_GET_STATUS: + transfer = fpi_usb_transfer_new (dev); + transfer->ssm = ssm; + fpi_usb_transfer_fill_bulk (transfer, EP_IN, EP_IN_MAX_BUF_SIZE); + fpi_usb_transfer_submit (transfer, + STATUS_TIMEOUT, + NULL, + fp_cmd_receive_cb, + fpi_ssm_get_data (ssm)); + break; + } +} + +static void +fp_cmd_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (dev); + CommandData *data = fpi_ssm_get_data (ssm); + + self->cmd_ssm = NULL; + + if (error) + { + if (data->callback) + data->callback (self, NULL, error); + else + g_error_free (error); + } +} + +static FpiUsbTransfer * +prepare_transfer (FpDevice *dev, + guint8 *data, + gsize data_len, + GDestroyNotify free_func) +{ + g_autoptr(FpiUsbTransfer) transfer = NULL; + + g_return_val_if_fail (data || data_len == 0, NULL); + + transfer = fpi_usb_transfer_new (dev); + + fpi_usb_transfer_fill_bulk_full (transfer, + EP_OUT, + data, + data_len, + free_func); + + return g_steal_pointer (&transfer); +} + +static void +realtek_sensor_cmd (FpiDeviceRealtek *self, + guint8 *cmd, + guint8 *trans_data, + FpRtkMsgType message_type, + gboolean bwait_data_delay, + SynCmdMsgCallback callback) +{ + g_autoptr(FpiUsbTransfer) cmd_transfer = NULL; + g_autoptr(FpiUsbTransfer) data_transfer = NULL; + CommandData *data = g_new0 (CommandData, 1); + + self->cmd_type = GET_CMD_TYPE (cmd[0]); + self->message_type = message_type; + self->trans_data_len = GET_TRANS_DATA_LEN (cmd[11], cmd[10]); + self->cmd_cancellable = bwait_data_delay; + + cmd_transfer = prepare_transfer (FP_DEVICE (self), cmd, FP_RTK_CMD_TOTAL_LEN, NULL); + self->cmd_transfer = g_steal_pointer (&cmd_transfer); + + if ((self->cmd_type == FP_RTK_CMD_WRITE) && trans_data) + { + data_transfer = prepare_transfer (FP_DEVICE (self), trans_data, self->trans_data_len, g_free); + self->data_transfer = g_steal_pointer (&data_transfer); + } + + self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self), + fp_cmd_run_state, + FP_RTK_CMD_NUM_STATES); + + data->callback = callback; + fpi_ssm_set_data (self->cmd_ssm, data, (GDestroyNotify) fp_cmd_ssm_done_data_free); + + fpi_ssm_start (self->cmd_ssm, fp_cmd_ssm_done); +} + +static void +fp_verify_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (dev); + + fp_info ("Verify complete!"); + + if (fpi_ssm_get_error (ssm)) + error = fpi_ssm_get_error (ssm); + + if (error && error->domain == FP_DEVICE_RETRY) + { + if (fpi_device_get_current_action (dev) == FPI_DEVICE_ACTION_VERIFY) + fpi_device_verify_report (dev, FPI_MATCH_ERROR, NULL, g_steal_pointer (&error)); + else + fpi_device_identify_report (dev, NULL, NULL, g_steal_pointer (&error)); + } + + if (fpi_device_get_current_action (dev) == FPI_DEVICE_ACTION_VERIFY) + fpi_device_verify_complete (dev, error); + else + fpi_device_identify_complete (dev, error); + + self->task_ssm = NULL; +} + +static void +fp_enroll_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (dev); + FpPrint *print = NULL; + + fp_info ("Enrollment complete!"); + + if (fpi_ssm_get_error (ssm)) + error = fpi_ssm_get_error (ssm); + + if (error) + { + fpi_device_enroll_complete (dev, NULL, error); + self->task_ssm = NULL; + return; + } + + fpi_device_get_enroll_data (FP_DEVICE (self), &print); + fpi_device_enroll_complete (FP_DEVICE (self), g_object_ref (print), NULL); + self->task_ssm = NULL; +} + +static void +fp_init_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (dev); + + fp_info ("Init complete!"); + + if (fpi_ssm_get_error (ssm)) + error = fpi_ssm_get_error (ssm); + + fpi_device_open_complete (dev, error); + self->task_ssm = NULL; +} + +static void +fp_delete_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (dev); + + fp_info ("Delete print complete!"); + + if (fpi_ssm_get_error (ssm)) + error = fpi_ssm_get_error (ssm); + + fpi_device_delete_complete (dev, error); + self->task_ssm = NULL; +} + +static void +fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + guint8 *cmd_buf = NULL; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case FP_RTK_VERIFY_CAPTURE: + fpi_device_report_finger_status_changes (device, + FP_FINGER_STATUS_NEEDED, + FP_FINGER_STATUS_NONE); + + cmd_buf = (guint8 *) &co_start_capture; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 1, fp_task_ssm_generic_cb); + break; + + case FP_RTK_VERIFY_FINISH_CAPTURE: + cmd_buf = (guint8 *) &co_finish_capture; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 1, fp_finish_capture_cb); + break; + + case FP_RTK_VERIFY_ACCEPT_SAMPLE: + co_accept_sample.param[0] = self->fp_purpose; + cmd_buf = (guint8 *) &co_accept_sample; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT_NO_STATUS, 1, fp_accept_sample_cb); + break; + + case FP_RTK_VERIFY_INDENTIFY_FEATURE: + cmd_buf = (guint8 *) &tls_identify_feature; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT_NO_STATUS, 0, fp_identify_feature_cb); + break; + + case FP_RTK_VERIFY_UPDATE_TEMPLATE: + cmd_buf = (guint8 *) &co_update_template; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 0, fp_task_ssm_generic_cb); + break; + } +} + +static void +fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + FpPrint *print = NULL; + guint8 *cmd_buf = NULL; + + g_autofree gchar *user_id = NULL; + g_autofree guint8 *payload = NULL; + + GVariant *uid = NULL; + GVariant *data = NULL; + gsize user_id_len; + guint finger; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case FP_RTK_ENROLL_GET_TEMPLATE: + g_assert (self->template_num > 0); + + co_get_template.data_len[0] = GET_LEN_L (TEMPLATE_LEN * self->template_num); + co_get_template.data_len[1] = GET_LEN_H (TEMPLATE_LEN * self->template_num); + + cmd_buf = (guint8 *) &co_get_template; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 0, fp_get_template_cb); + break; + + case FP_RTK_ENROLL_BEGIN_POS: + tls_enroll_begin.param[0] = self->pos_index; + cmd_buf = (guint8 *) &tls_enroll_begin; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 0, fp_task_ssm_generic_cb); + break; + + case FP_RTK_ENROLL_CAPTURE: + fpi_device_report_finger_status_changes (device, + FP_FINGER_STATUS_NEEDED, + FP_FINGER_STATUS_NONE); + + cmd_buf = (guint8 *) &co_start_capture; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 1, fp_task_ssm_generic_cb); + break; + + case FP_RTK_ENROLL_FINISH_CAPTURE: + cmd_buf = (guint8 *) &co_finish_capture; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 1, fp_finish_capture_cb); + break; + + case FP_RTK_ENROLL_ACCEPT_SAMPLE: + co_accept_sample.param[0] = self->fp_purpose; + cmd_buf = (guint8 *) &co_accept_sample; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT_NO_STATUS, 1, fp_accept_sample_cb); + break; + + case FP_RTK_ENROLL_CHECK_DUPLICATE: + cmd_buf = (guint8 *) &co_check_duplicate; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT_NO_STATUS, 1, fp_check_duplicate_cb); + break; + + case FP_RTK_ENROLL_COMMIT: + guint8 *trans_id = NULL; + + fpi_device_get_enroll_data (device, &print); + user_id = fpi_print_generate_user_id (print); + user_id_len = strlen (user_id); + user_id_len = MIN (DEFAULT_UID_LEN, user_id_len); + + payload = g_malloc0 (UID_PAYLOAD_LEN); + memcpy (payload, user_id, user_id_len); + + trans_id = g_steal_pointer (&payload); + + finger = SUB_FINGER_01; + uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + user_id, + user_id_len, + 1); + data = g_variant_new ("(y@ay)", + finger, + uid); + + fpi_print_set_type (print, FPI_PRINT_RAW); + fpi_print_set_device_stored (print, TRUE); + g_object_set (print, "fpi-data", data, NULL); + g_object_set (print, "description", user_id, NULL); + + g_debug ("user_id: %s, finger: 0x%x", user_id, finger); + + tls_enroll_commit.param[0] = SUB_FINGER_01; + cmd_buf = (guint8 *) &tls_enroll_commit; + realtek_sensor_cmd (self, cmd_buf, trans_id, FP_RTK_MSG_PLAINTEXT, 0, fp_task_ssm_generic_cb); + break; + } +} + +static void +fp_init_sm_run_state (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + guint8 *cmd_buf = NULL; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case FP_RTK_INIT_SELECT_OS: + co_select_system.param[0] = 0x01; + cmd_buf = (guint8 *) &co_select_system; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 0, fp_task_ssm_generic_cb); + break; + + case FP_RTK_INIT_GET_ENROLL_NUM: + cmd_buf = (guint8 *) &co_get_enroll_num; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 0, fp_get_enroll_num_cb); + break; + } +} + +static void +fp_delete_sm_run_state (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + guint8 *cmd_buf = NULL; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case FP_RTK_DELETE_GET_POS: + g_assert (self->template_num > 0); + + co_get_template.data_len[0] = GET_LEN_L (TEMPLATE_LEN * self->template_num); + co_get_template.data_len[1] = GET_LEN_H (TEMPLATE_LEN * self->template_num); + + cmd_buf = (guint8 *) &co_get_template; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 0, fp_get_delete_pos_cb); + break; + + case FP_RTK_DELETE_PRINT: + co_delete_record.param[0] = self->pos_index; + cmd_buf = (guint8 *) &co_delete_record; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 0, fp_task_ssm_generic_cb); + break; + } +} + + +static void +identify_verify (FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + FpiDeviceAction current_action; + + G_DEBUG_HERE (); + current_action = fpi_device_get_current_action (device); + + g_assert (current_action == FPI_DEVICE_ACTION_VERIFY || + current_action == FPI_DEVICE_ACTION_IDENTIFY); + + if (current_action == FPI_DEVICE_ACTION_IDENTIFY) + self->fp_purpose = FP_RTK_PURPOSE_IDENTIFY; + else + self->fp_purpose = FP_RTK_PURPOSE_VERIFY; + + g_assert (!self->task_ssm); + + self->task_ssm = fpi_ssm_new_full (device, + fp_verify_sm_run_state, + FP_RTK_VERIFY_NUM_STATES, + FP_RTK_VERIFY_NUM_STATES, + "Verify & Identify"); + + fpi_ssm_start (self->task_ssm, fp_verify_ssm_done); +} + +static void +enroll (FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + + G_DEBUG_HERE (); + self->enroll_stage = 0; + self->fp_purpose = FP_RTK_PURPOSE_ENROLL; + + g_assert (!self->task_ssm); + + self->task_ssm = fpi_ssm_new_full (device, + fp_enroll_sm_run_state, + FP_RTK_ENROLL_NUM_STATES, + FP_RTK_ENROLL_NUM_STATES, + "Enroll"); + + fpi_ssm_start (self->task_ssm, fp_enroll_ssm_done); +} + +static void +dev_probe (FpDevice *device) +{ + GUsbDevice *usb_dev; + GError *error = NULL; + g_autofree gchar *product = NULL; + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + + G_DEBUG_HERE (); + /* Claim usb interface */ + usb_dev = fpi_device_get_usb_device (device); + if (!g_usb_device_open (usb_dev, &error)) + { + fpi_device_probe_complete (device, NULL, NULL, error); + return; + } + + if (!g_usb_device_reset (usb_dev, &error)) + { + g_usb_device_close (usb_dev, NULL); + fpi_device_probe_complete (device, NULL, NULL, error); + return; + } + + if (!g_usb_device_claim_interface (usb_dev, 0, 0, &error)) + { + g_usb_device_close (usb_dev, NULL); + fpi_device_probe_complete (device, NULL, NULL, error); + return; + } + + product = g_usb_device_get_string_descriptor (usb_dev, + g_usb_device_get_product_index (usb_dev), + &error); + + if (product) + fp_dbg ("Device name: %s", product); + + self->max_enroll_stage = MAX_ENROLL_SAMPLES; + fpi_device_set_nr_enroll_stages (device, self->max_enroll_stage); + + g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (device)), 0, 0, NULL); + g_usb_device_close (usb_dev, NULL); + + fpi_device_probe_complete (device, NULL, product, error); +} + +static void +dev_init (FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + GError *error = NULL; + + G_DEBUG_HERE (); + if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) + { + fpi_device_open_complete (FP_DEVICE (self), error); + return; + } + + /* Claim usb interface */ + if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error)) + { + fpi_device_open_complete (FP_DEVICE (self), error); + return; + } + + g_assert (!self->task_ssm); + + self->task_ssm = fpi_ssm_new_full (device, + fp_init_sm_run_state, + FP_RTK_INIT_NUM_STATES, + FP_RTK_INIT_NUM_STATES, + "Init"); + + fpi_ssm_start (self->task_ssm, fp_init_ssm_done); +} + +static void +dev_exit (FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + + g_autoptr(GError) release_error = NULL; + + G_DEBUG_HERE (); + + g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (self)), 0, 0, &release_error); + + fpi_device_close_complete (device, release_error); +} + +static void +delete_print (FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + + G_DEBUG_HERE (); + + g_assert (!self->task_ssm); + + self->task_ssm = fpi_ssm_new_full (device, + fp_delete_sm_run_state, + FP_RTK_DELETE_NUM_STATES, + FP_RTK_DELETE_NUM_STATES, + "Delete print"); + + fpi_ssm_start (self->task_ssm, fp_delete_ssm_done); +} + +static void +clear_storage (FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + guint8 *cmd_buf = NULL; + + G_DEBUG_HERE (); + co_delete_record.param[0] = 0xff; + cmd_buf = (guint8 *) &co_delete_record; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 0, fp_clear_storage_cb); +} + +static void +list_print (FpDevice *device) +{ + FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); + guint8 *cmd_buf = NULL; + + G_DEBUG_HERE (); + g_assert (self->template_num > 0); + + co_get_template.data_len[0] = GET_LEN_L (TEMPLATE_LEN * self->template_num); + co_get_template.data_len[1] = GET_LEN_H (TEMPLATE_LEN * self->template_num); + + cmd_buf = (guint8 *) &co_get_template; + realtek_sensor_cmd (self, cmd_buf, NULL, FP_RTK_MSG_PLAINTEXT, 1, fp_list_cb); +} + +static void +fpi_device_realtek_init (FpiDeviceRealtek *self) +{ +} + +static void +fpi_device_realtek_class_init (FpiDeviceRealtekClass *klass) +{ + FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); + + dev_class->id = FP_COMPONENT; + dev_class->full_name = "Realtek MOC Fingerprint Sensor"; + + dev_class->type = FP_DEVICE_TYPE_USB; + dev_class->scan_type = FP_SCAN_TYPE_PRESS; + dev_class->id_table = id_table; + dev_class->nr_enroll_stages = MAX_ENROLL_SAMPLES; + dev_class->temp_hot_seconds = -1; + + dev_class->open = dev_init; + dev_class->close = dev_exit; + dev_class->probe = dev_probe; + dev_class->verify = identify_verify; + dev_class->identify = identify_verify; + dev_class->enroll = enroll; + dev_class->delete = delete_print; + dev_class->clear_storage = clear_storage; + dev_class->list = list_print; + + fpi_device_class_auto_initialize_features (dev_class); +} diff --git a/libfprint/drivers/realtek/realtek.h b/libfprint/drivers/realtek/realtek.h new file mode 100644 index 00000000..803922fe --- /dev/null +++ b/libfprint/drivers/realtek/realtek.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2022-2023 Realtek Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "fpi-device.h" +#include "fpi-ssm.h" + +#include +#include +#include + +#define EP_IN (2 | FPI_USB_ENDPOINT_IN) +#define EP_OUT (1 | FPI_USB_ENDPOINT_OUT) + +#define EP_IN_MAX_BUF_SIZE 2048 + +#define FP_RTK_CMD_TOTAL_LEN 12 +#define FP_RTK_CMD_LEN 2 +#define FP_RTK_CMD_PARAM_LEN 4 +#define FP_RTK_CMD_ADDR_LEN 4 +#define FP_RTK_CMD_DATA_LEN 2 + +#define TEMPLATE_LEN 35 +#define SUBFACTOR_OFFSET 2 +#define UID_OFFSET 3 +#define UID_PAYLOAD_LEN 32 + +/* Command transfer timeout :ms*/ +#define CMD_TIMEOUT 1000 +#define DATA_TIMEOUT 5000 +#define STATUS_TIMEOUT 2000 + +#define MAX_ENROLL_SAMPLES 8 +#define DEFAULT_UID_LEN 28 +#define SUB_FINGER_01 0xFF + +#define GET_CMD_TYPE(val) ((val & 0xC0) >> 6) +#define GET_TRANS_DATA_LEN(len_h, len_l) ((len_h << 8) | len_l) +#define GET_LEN_L(total_data_len) ((total_data_len) & 0xff) +#define GET_LEN_H(total_data_len) ((total_data_len) >> 8) + +G_DECLARE_FINAL_TYPE (FpiDeviceRealtek, fpi_device_realtek, FPI, DEVICE_REALTEK, FpDevice) + +typedef void (*SynCmdMsgCallback) (FpiDeviceRealtek *self, + uint8_t *buffer_in, + GError *error); + +typedef struct +{ + SynCmdMsgCallback callback; +} CommandData; + +typedef enum { + FP_RTK_CMD_ONLY = 0, + FP_RTK_CMD_READ, + FP_RTK_CMD_WRITE, +} FpRtkCmdType; + +typedef enum { + FP_RTK_MSG_PLAINTEXT = 0, + FP_RTK_MSG_PLAINTEXT_NO_STATUS, +} FpRtkMsgType; + +typedef enum { + FP_RTK_PURPOSE_IDENTIFY = 0x01, /* identify before enroll */ + FP_RTK_PURPOSE_VERIFY = 0x02, + FP_RTK_PURPOSE_ENROLL = 0x04, +} FpRtkPurpose; + +typedef enum { + FP_RTK_SUCCESS = 0x0, + FP_RTK_TOO_HIGH, + FP_RTK_TOO_LOW, + FP_RTK_TOO_LEFT, + FP_RTK_TOO_RIGHT, + FP_RTK_TOO_FAST, + FP_RTK_TOO_SLOW, + FP_RTK_POOR_QUALITY, + FP_RTK_TOO_SKEWED, + FP_RTK_TOO_SHORT, + FP_RTK_MERGE_FAILURE, + FP_RTK_MATCH_FAIL, + FP_RTK_CMD_ERR, +} FpRtkInStatus; + +typedef enum { + FP_RTK_ENROLL_GET_TEMPLATE = 0, + FP_RTK_ENROLL_BEGIN_POS, + FP_RTK_ENROLL_CAPTURE, + FP_RTK_ENROLL_FINISH_CAPTURE, + FP_RTK_ENROLL_ACCEPT_SAMPLE, + FP_RTK_ENROLL_CHECK_DUPLICATE, + FP_RTK_ENROLL_COMMIT, + FP_RTK_ENROLL_NUM_STATES, +} FpRtkEnrollState; + +typedef enum { + FP_RTK_VERIFY_CAPTURE = 0, + FP_RTK_VERIFY_FINISH_CAPTURE, + FP_RTK_VERIFY_ACCEPT_SAMPLE, + FP_RTK_VERIFY_INDENTIFY_FEATURE, + FP_RTK_VERIFY_UPDATE_TEMPLATE, + FP_RTK_VERIFY_NUM_STATES, +} FpRtkVerifyState; + +typedef enum { + FP_RTK_DELETE_GET_POS = 0, + FP_RTK_DELETE_PRINT, + FP_RTK_DELETE_NUM_STATES, +} FpRtkDeleteState; + +typedef enum { + FP_RTK_INIT_SELECT_OS = 0, + FP_RTK_INIT_GET_ENROLL_NUM, + FP_RTK_INIT_NUM_STATES, +} FpRtkInitState; + +typedef enum { + FP_RTK_CMD_SEND = 0, + FP_RTK_CMD_TRANS_DATA, + FP_RTK_CMD_GET_STATUS, + FP_RTK_CMD_NUM_STATES, +} FpRtkCmdState; + +struct _FpiDeviceRealtek +{ + FpDevice parent; + FpiSsm *task_ssm; + FpiSsm *cmd_ssm; + FpiUsbTransfer *cmd_transfer; + FpiUsbTransfer *data_transfer; + gint cmd_type; + FpRtkMsgType message_type; + gboolean cmd_cancellable; + gint enroll_stage; + gint max_enroll_stage; + guchar *read_data; + gsize trans_data_len; + FpRtkPurpose fp_purpose; + gint pos_index; + gint template_num; +}; + +struct realtek_fp_cmd +{ + uint8_t cmd[FP_RTK_CMD_LEN]; + uint8_t param[FP_RTK_CMD_PARAM_LEN]; + uint8_t addr[FP_RTK_CMD_ADDR_LEN]; + uint8_t data_len[FP_RTK_CMD_DATA_LEN]; +}; + +static struct realtek_fp_cmd co_start_capture = { + .cmd = {0x05, 0x05}, +}; + +static struct realtek_fp_cmd co_finish_capture = { + .cmd = {0x45, 0x06}, + .data_len = {0x05}, +}; + +static struct realtek_fp_cmd co_accept_sample = { + .cmd = {0x45, 0x08}, + .data_len = {0x09}, +}; + +static struct realtek_fp_cmd tls_identify_feature = { + .cmd = {0x45, 0x22}, + .data_len = {0x2A}, +}; + +static struct realtek_fp_cmd co_get_enroll_num = { + .cmd = {0x45, 0x0d}, + .data_len = {0x02}, +}; + +static struct realtek_fp_cmd co_get_template = { + .cmd = {0x45, 0x0E}, +}; + +static struct realtek_fp_cmd tls_enroll_begin = { + .cmd = {0x05, 0x20}, +}; + +static struct realtek_fp_cmd co_check_duplicate = { + .cmd = {0x45, 0x10}, + .data_len = {0x22}, +}; + +static struct realtek_fp_cmd tls_enroll_commit = { + .cmd = {0x85, 0x21}, + .data_len = {0x20}, +}; + +static struct realtek_fp_cmd co_update_template = { + .cmd = {0x05, 0x11}, +}; + +static struct realtek_fp_cmd co_delete_record = { + .cmd = {0x05, 0x0F}, +}; + +static struct realtek_fp_cmd co_select_system = { + .cmd = {0x05, 0x13}, +}; \ No newline at end of file diff --git a/libfprint/meson.build b/libfprint/meson.build index 7e3b771f..c2ebd8c1 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -141,6 +141,8 @@ driver_sources = { [ 'drivers/goodixmoc/goodix.c', 'drivers/goodixmoc/goodix_proto.c' ], 'fpcmoc' : [ 'drivers/fpcmoc/fpc.c' ], + 'realtek' : + [ 'drivers/realtek/realtek.c' ], } helper_sources = { diff --git a/meson.build b/meson.build index 0b7569bd..9fc10315 100644 --- a/meson.build +++ b/meson.build @@ -131,6 +131,7 @@ default_drivers = [ 'goodixmoc', 'nb1010', 'fpcmoc', + 'realtek', # SPI 'elanspi', diff --git a/tests/meson.build b/tests/meson.build index 31040d1d..f4f15979 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -52,6 +52,7 @@ drivers_tests = [ 'nb1010', 'egis0570', 'fpcmoc', + 'realtek', ] if get_option('introspection') diff --git a/tests/realtek/custom.pcapng b/tests/realtek/custom.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..0fe6dc7b05d6bdd1008665467bffca83d04835b1 GIT binary patch literal 29128 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 literal 0 HcmV?d00001 diff --git a/tests/realtek/custom.py b/tests/realtek/custom.py new file mode 100755 index 00000000..15a71e2c --- /dev/null +++ b/tests/realtek/custom.py @@ -0,0 +1,110 @@ +#!/usr/bin/python3 + +import traceback +import sys +import gi + +gi.require_version('FPrint', '2.0') +from gi.repository import FPrint, GLib + +# Exit with error on any exception, included those happening in async callbacks +sys.excepthook = lambda *args: (traceback.print_exception(*args), sys.exit(1)) + +ctx = GLib.main_context_default() + +c = FPrint.Context() +c.enumerate() +devices = c.get_devices() + +d = devices[0] +del devices + +assert d.get_driver() == "realtek" +assert not d.has_feature(FPrint.DeviceFeature.CAPTURE) +assert d.has_feature(FPrint.DeviceFeature.IDENTIFY) +assert d.has_feature(FPrint.DeviceFeature.VERIFY) +assert not d.has_feature(FPrint.DeviceFeature.DUPLICATES_CHECK) +assert d.has_feature(FPrint.DeviceFeature.STORAGE) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_LIST) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_DELETE) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR) + +d.open_sync() + +# 1. verify clear storage command, 2. make sure later asserts are good +d.clear_storage_sync() + +template = FPrint.Print.new(d) + +def enroll_progress(*args): + # assert d.get_finger_status() & FPrint.FingerStatusFlags.NEEDED + print('enroll progress: ' + str(args)) + +def identify_done(dev, res): + global identified + identified = True + try: + identify_match, identify_print = dev.identify_finish(res) + except gi.repository.GLib.GError as e: + print("Please try again") + else: + print('indentification_done: ', identify_match, identify_print) + assert identify_match.equal(identify_print) + +def start_identify_async(prints): + global identified + print('async identifying') + d.identify(prints, callback=identify_done) + del prints + + while not identified: + ctx.iteration(True) + + identified = False + +# List, enroll, list, verify, identify, delete +print("enrolling") +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +p = d.enroll_sync(template, None, enroll_progress, None) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("enroll done") + +print("listing") +stored = d.list_prints_sync() +print("listing done") +assert len(stored) == 1 +assert stored[0].equal(p) +print("verifying") +try: + assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE + verify_res, verify_print = d.verify_sync(p) + assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +except gi.repository.GLib.GError as e: + print("Please try again") +else: + print("verify done") + del p + assert verify_res == True + +identified = False +deserialized_prints = [] +for p in stored: + deserialized_prints.append(FPrint.Print.deserialize(p.serialize())) + assert deserialized_prints[-1].equal(p) +del stored + +print('async identifying') +d.identify(deserialized_prints, callback=identify_done) +del deserialized_prints + +while not identified: + ctx.iteration(True) + +print("deleting") +d.delete_print_sync(p) +print("delete done") + +d.close_sync() + +del d +del c diff --git a/tests/realtek/device b/tests/realtek/device new file mode 100644 index 00000000..c4f1c85e --- /dev/null +++ b/tests/realtek/device @@ -0,0 +1,240 @@ +P: /devices/pci0000:00/0000:00:14.0/usb1/1-4 +N: bus/usb/001/005=12010102EF020140DA0B135801210301020109022E00010104A0FA0904000004FF02000507050102000200070583031000080705840310000807058202000200 +E: DEVNAME=/dev/bus/usb/001/005 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=bda/5813/2101 +E: TYPE=239/2/1 +E: BUSNUM=001 +E: DEVNUM=005 +E: MAJOR=189 +E: MINOR=4 +E: SUBSYSTEM=usb +E: ID_VENDOR=Generic +E: ID_VENDOR_ENC=Generic +E: ID_VENDOR_ID=0bda +E: ID_MODEL=Realtek_USB2.0_Finger_Print_Bridge +E: ID_MODEL_ENC=Realtek\x20USB2.0\x20Finger\x20Print\x20Bridge +E: ID_MODEL_ID=5813 +E: ID_REVISION=2101 +E: ID_SERIAL=Generic_Realtek_USB2.0_Finger_Print_Bridge_201801010001 +E: ID_SERIAL_SHORT=201801010001 +E: ID_BUS=usb +E: ID_USB_INTERFACES=:ff0200: +E: ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Corp. +E: ID_PATH=pci-0000:00:14.0-usb-0:4 +E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_4 +A: authorized=1\n +A: avoid_reset_quirk=0\n +A: bConfigurationValue=1\n +A: bDeviceClass=ef\n +A: bDeviceProtocol=01\n +A: bDeviceSubClass=02\n +A: bMaxPacketSize0=64\n +A: bMaxPower=500mA\n +A: bNumConfigurations=1\n +A: bNumInterfaces= 1\n +A: bcdDevice=2101\n +A: bmAttributes=a0\n +A: busnum=1\n +A: configuration=Realtek USB2.0 Finger Print Bridge\n +H: descriptors=12010102EF020140DA0B135801210301020109022E00010104A0FA0904000004FF02000507050102000200070583031000080705840310000807058202000200 +A: dev=189:4\n +A: devnum=5\n +A: devpath=4\n +L: driver=../../../../../bus/usb/drivers/usb +L: firmware_node=../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4b/device:4c/device:50 +A: idProduct=5813\n +A: idVendor=0bda\n +A: ltm_capable=no\n +A: manufacturer=Generic\n +A: maxchild=0\n +A: physical_location/dock=no\n +A: physical_location/horizontal_position=left\n +A: physical_location/lid=no\n +A: physical_location/panel=top\n +A: physical_location/vertical_position=upper\n +L: port=../1-0:1.0/usb1-port4 +A: power/active_duration=91232868\n +A: power/async=enabled\n +A: power/autosuspend=2\n +A: power/autosuspend_delay_ms=2000\n +A: power/connected_duration=91232868\n +A: power/control=on\n +A: power/level=on\n +A: power/persist=1\n +A: power/runtime_active_kids=0\n +A: power/runtime_active_time=91232594\n +A: power/runtime_enabled=forbidden\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=0\n +A: power/runtime_usage=7\n +A: power/wakeup=disabled\n +A: power/wakeup_abort_count=\n +A: power/wakeup_active=\n +A: power/wakeup_active_count=\n +A: power/wakeup_count=\n +A: power/wakeup_expire_count=\n +A: power/wakeup_last_time_ms=\n +A: power/wakeup_max_time_ms=\n +A: power/wakeup_total_time_ms=\n +A: product=Realtek USB2.0 Finger Print Bridge\n +A: quirks=0x0\n +A: removable=removable\n +A: rx_lanes=1\n +A: serial=201801010001\n +A: speed=480\n +A: tx_lanes=1\n +A: urbnum=15076313\n +A: version= 2.01\n + +P: /devices/pci0000:00/0000:00:14.0/usb1 +N: bus/usb/001/001=12010002090001406B1D020002060302010109021900010100E0000904000001090000000705810304000C +E: DEVNAME=/dev/bus/usb/001/001 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=1d6b/2/602 +E: TYPE=9/0/1 +E: BUSNUM=001 +E: DEVNUM=001 +E: MAJOR=189 +E: MINOR=0 +E: SUBSYSTEM=usb +E: ID_VENDOR=Linux_6.2.0-35-generic_xhci-hcd +E: ID_VENDOR_ENC=Linux\x206.2.0-35-generic\x20xhci-hcd +E: ID_VENDOR_ID=1d6b +E: ID_MODEL=xHCI_Host_Controller +E: ID_MODEL_ENC=xHCI\x20Host\x20Controller +E: ID_MODEL_ID=0002 +E: ID_REVISION=0602 +E: ID_SERIAL=Linux_6.2.0-35-generic_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 +E: ID_SERIAL_SHORT=0000:00:14.0 +E: ID_BUS=usb +E: ID_USB_INTERFACES=:090000: +E: ID_VENDOR_FROM_DATABASE=Linux Foundation +E: ID_AUTOSUSPEND=1 +E: ID_MODEL_FROM_DATABASE=2.0 root hub +E: ID_PATH=pci-0000:00:14.0 +E: ID_PATH_TAG=pci-0000_00_14_0 +E: ID_FOR_SEAT=usb-pci-0000_00_14_0 +E: TAGS=:seat: +E: CURRENT_TAGS=:seat: +A: authorized=1\n +A: authorized_default=1\n +A: avoid_reset_quirk=0\n +A: bConfigurationValue=1\n +A: bDeviceClass=09\n +A: bDeviceProtocol=01\n +A: bDeviceSubClass=00\n +A: bMaxPacketSize0=64\n +A: bMaxPower=0mA\n +A: bNumConfigurations=1\n +A: bNumInterfaces= 1\n +A: bcdDevice=0602\n +A: bmAttributes=e0\n +A: busnum=1\n +A: configuration= +H: descriptors=12010002090001406B1D020002060302010109021900010100E0000904000001090000000705810304000C +A: dev=189:0\n +A: devnum=1\n +A: devpath=0\n +L: driver=../../../../bus/usb/drivers/usb +L: firmware_node=../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4b/device:4c +A: idProduct=0002\n +A: idVendor=1d6b\n +A: interface_authorized_default=1\n +A: ltm_capable=no\n +A: manufacturer=Linux 6.2.0-35-generic xhci-hcd\n +A: maxchild=16\n +A: power/active_duration=264747968\n +A: power/async=enabled\n +A: power/autosuspend=0\n +A: power/autosuspend_delay_ms=0\n +A: power/connected_duration=264747968\n +A: power/control=auto\n +A: power/level=auto\n +A: power/runtime_active_kids=3\n +A: power/runtime_active_time=264747968\n +A: power/runtime_enabled=enabled\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=0\n +A: power/runtime_usage=0\n +A: power/wakeup=disabled\n +A: power/wakeup_abort_count=\n +A: power/wakeup_active=\n +A: power/wakeup_active_count=\n +A: power/wakeup_count=\n +A: power/wakeup_expire_count=\n +A: power/wakeup_last_time_ms=\n +A: power/wakeup_max_time_ms=\n +A: power/wakeup_total_time_ms=\n +A: product=xHCI Host Controller\n +A: quirks=0x0\n +A: removable=unknown\n +A: rx_lanes=1\n +A: serial=0000:00:14.0\n +A: speed=480\n +A: tx_lanes=1\n +A: urbnum=3177\n +A: version= 2.00\n + +P: /devices/pci0000:00/0000:00:14.0 +E: DRIVER=xhci_hcd +E: PCI_CLASS=C0330 +E: PCI_ID=8086:A36D +E: PCI_SUBSYS_ID=1028:085C +E: PCI_SLOT_NAME=0000:00:14.0 +E: MODALIAS=pci:v00008086d0000A36Dsv00001028sd0000085Cbc0Csc03i30 +E: SUBSYSTEM=pci +E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller +E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller +E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI +E: ID_VENDOR_FROM_DATABASE=Intel Corporation +E: ID_MODEL_FROM_DATABASE=Cannon Lake PCH USB 3.1 xHCI Host Controller +A: ari_enabled=0\n +A: broken_parity_status=0\n +A: class=0x0c0330\n +H: config=86806DA3060590021030030C00008000040030D200000000000000000000000000000000000000000000000028105C08000000007000000000000000FF010000FD0134808FC6FF8300000000000000007F6DDC0F000000005919041B00000000316000000000000000000000000000000180C2C108000000000000000000000005908700D802E0FE0000000000000000090014F01000400100000000C10A080000080E00001800008F40020000010000030000000C00000000000000C000000000000000000100003000000000000000030000000C0000000000000000000000000000000000000000000000000000000000000000000000B50F120112000000 +A: consistent_dma_mask_bits=64\n +A: d3cold_allowed=1\n +A: dbc=disabled\n +A: device=0xa36d\n +A: dma_mask_bits=64\n +L: driver=../../../bus/pci/drivers/xhci_hcd +A: driver_override=(null)\n +A: enable=1\n +L: firmware_node=../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4b +A: index=4\n +A: irq=125\n +A: label=Onboard - Other\n +A: local_cpulist=0-3\n +A: local_cpus=f\n +A: modalias=pci:v00008086d0000A36Dsv00001028sd0000085Cbc0Csc03i30\n +A: msi_bus=1\n +A: msi_irqs/125=msi\n +A: numa_node=-1\n +A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 6 7 2112 7\nxHCI ring segments 24 24 4096 24\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 12 32 128 1\nbuffer-32 0 0 32 0\n +A: power/async=enabled\n +A: power/control=on\n +A: power/runtime_active_kids=1\n +A: power/runtime_active_time=264748677\n +A: power/runtime_enabled=forbidden\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=0\n +A: power/runtime_usage=1\n +A: power/wakeup=enabled\n +A: power/wakeup_abort_count=0\n +A: power/wakeup_active=0\n +A: power/wakeup_active_count=0\n +A: power/wakeup_count=0\n +A: power/wakeup_expire_count=0\n +A: power/wakeup_last_time_ms=0\n +A: power/wakeup_max_time_ms=0\n +A: power/wakeup_total_time_ms=0\n +A: power_state=D0\n +A: resource=0x00000000d2300000 0x00000000d230ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n +A: revision=0x10\n +A: subsystem_device=0x085c\n +A: subsystem_vendor=0x1028\n +A: vendor=0x8086\n + From 16d02b3ed5f77c40e231a2049b492641b6b797ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Oct 2023 16:48:37 +0200 Subject: [PATCH 14/64] fp-image: Remove unused ref_count flag It's an object so we already ref-count it. --- libfprint/fpi-image.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libfprint/fpi-image.h b/libfprint/fpi-image.h index 3554bb7b..5cdf4e82 100644 --- a/libfprint/fpi-image.h +++ b/libfprint/fpi-image.h @@ -67,7 +67,6 @@ struct _FpImage guint8 *binarized; GPtrArray *minutiae; - guint ref_count; }; gint fpi_std_sq_dev (const guint8 *buf, From 83939abe10ea7d9f3b851a1d8455072d63521768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Oct 2023 16:49:16 +0200 Subject: [PATCH 15/64] fp-image: Add FP_IMAGE_NONE flags definition --- libfprint/fpi-image.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libfprint/fpi-image.h b/libfprint/fpi-image.h index 5cdf4e82..e9ff803d 100644 --- a/libfprint/fpi-image.h +++ b/libfprint/fpi-image.h @@ -33,6 +33,7 @@ * rely on the image to be normalized by libfprint before further processing. */ typedef enum { + FPI_IMAGE_NONE = 0, FPI_IMAGE_V_FLIPPED = 1 << 0, FPI_IMAGE_H_FLIPPED = 1 << 1, FPI_IMAGE_COLORS_INVERTED = 1 << 2, From 2b008b52d707004f6fae15ab7f14a278277ab530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 10 Oct 2023 16:51:09 +0200 Subject: [PATCH 16/64] fp-image: Simplify minutiae detection tasks We can just use a GTask to handle the detection while using the finish function to process the results to the image, so that it is more predictable when this happens and it does not depend on a thread returning. Also remove data duplication when possible, this class wasn't fully safe anyway to be used concurrently, so there's no point to copy data when not needed. Also added the hard constraint to not proceed with minutiae detection if something else is already doing this. At the same time we can mark the task to finish early on cancellation. --- libfprint/fp-image.c | 189 +++++++++++++++++++++++++----------------- libfprint/fpi-image.h | 2 + 2 files changed, 116 insertions(+), 75 deletions(-) diff --git a/libfprint/fp-image.c b/libfprint/fp-image.c index 8870cfab..4c4864fd 100644 --- a/libfprint/fp-image.c +++ b/libfprint/fp-image.c @@ -160,60 +160,65 @@ fp_image_init (FpImage *self) typedef struct { - GAsyncReadyCallback user_cb; struct fp_minutiae *minutiae; - gint width, height; - gdouble ppmm; - FpiImageFlags flags; - guchar *image; guchar *binarized; -} DetectMinutiaeData; + FpiImageFlags flags; + unsigned char *image; + gboolean image_changed; +} DetectMinutiaeNbisData; static void -fp_image_detect_minutiae_free (DetectMinutiaeData *data) +fp_image_detect_minutiae_free (DetectMinutiaeNbisData *data) { - g_clear_pointer (&data->image, g_free); g_clear_pointer (&data->minutiae, free_minutiae); g_clear_pointer (&data->binarized, g_free); + + if (data->image_changed) + g_clear_pointer (&data->image, g_free); + g_free (data); } -static void -fp_image_detect_minutiae_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) +G_DEFINE_AUTOPTR_CLEANUP_FUNC (DetectMinutiaeNbisData, fp_image_detect_minutiae_free) + + +static gboolean +fp_image_detect_minutiae_nbis_finish (FpImage *self, + GTask *task, + GError **error) { - GTask *task = G_TASK (res); - FpImage *image; - DetectMinutiaeData *data = g_task_get_task_data (task); + g_autoptr(DetectMinutiaeNbisData) data = NULL; - if (!g_task_had_error (task)) + data = g_task_propagate_pointer (task, error); + + if (data != NULL) { - gint i; - image = FP_IMAGE (source_object); + self->flags = data->flags; - image->flags = data->flags; + if (data->image_changed) + { + g_clear_pointer (&self->data, g_free); + self->data = g_steal_pointer (&data->image); + } - g_clear_pointer (&image->data, g_free); - image->data = g_steal_pointer (&data->image); + g_clear_pointer (&self->binarized, g_free); + self->binarized = g_steal_pointer (&data->binarized); - g_clear_pointer (&image->binarized, g_free); - image->binarized = g_steal_pointer (&data->binarized); + g_clear_pointer (&self->minutiae, g_ptr_array_unref); + self->minutiae = g_ptr_array_new_full (data->minutiae->num, + (GDestroyNotify) free_minutia); - g_clear_pointer (&image->minutiae, g_ptr_array_unref); - image->minutiae = g_ptr_array_new_full (data->minutiae->num, - (GDestroyNotify) free_minutia); - - for (i = 0; i < data->minutiae->num; i++) - g_ptr_array_add (image->minutiae, + for (int i = 0; i < data->minutiae->num; i++) + g_ptr_array_add (self->minutiae, g_steal_pointer (&data->minutiae->list[i])); - /* Don't let it delete anything. */ + /* Don't let free_minutiae delete the minutiae that we now own. */ data->minutiae->num = 0; + + return TRUE; } - if (data->user_cb) - data->user_cb (source_object, res, user_data); + return FALSE; } static void @@ -266,70 +271,83 @@ invert_colors (guint8 *data, gint width, gint height) } static void -fp_image_detect_minutiae_thread_func (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) +fp_image_detect_minutiae_nbis_thread_func (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) { g_autoptr(GTimer) timer = NULL; - DetectMinutiaeData *data = task_data; - struct fp_minutiae *minutiae = NULL; + g_autoptr(DetectMinutiaeNbisData) ret_data = NULL; + g_autoptr(GTask) thread_task = g_steal_pointer (&task); g_autofree gint *direction_map = NULL; g_autofree gint *low_contrast_map = NULL; g_autofree gint *low_flow_map = NULL; g_autofree gint *high_curve_map = NULL; g_autofree gint *quality_map = NULL; - g_autofree guchar *bdata = NULL; + g_autofree LFSPARMS *lfsparms = NULL; + FpImage *self = source_object; + FpiImageFlags minutiae_flags; + unsigned char *image; gint map_w, map_h; gint bw, bh, bd; gint r; - g_autofree LFSPARMS *lfsparms = NULL; + + image = self->data; + minutiae_flags = self->flags & ~(FPI_IMAGE_H_FLIPPED | + FPI_IMAGE_V_FLIPPED | + FPI_IMAGE_COLORS_INVERTED); + + if (minutiae_flags != FPI_IMAGE_NONE) + image = g_memdup2 (self->data, self->width * self->height); + + ret_data = g_new0 (DetectMinutiaeNbisData, 1); + ret_data->flags = minutiae_flags; + ret_data->image = image; + ret_data->image_changed = image != self->data; /* Normalize the image first */ - if (data->flags & FPI_IMAGE_H_FLIPPED) - hflip (data->image, data->width, data->height); + if (self->flags & FPI_IMAGE_H_FLIPPED) + hflip (image, self->width, self->height); - if (data->flags & FPI_IMAGE_V_FLIPPED) - vflip (data->image, data->width, data->height); + if (self->flags & FPI_IMAGE_V_FLIPPED) + vflip (image, self->width, self->height); - if (data->flags & FPI_IMAGE_COLORS_INVERTED) - invert_colors (data->image, data->width, data->height); - - data->flags &= ~(FPI_IMAGE_H_FLIPPED | FPI_IMAGE_V_FLIPPED | FPI_IMAGE_COLORS_INVERTED); + if (self->flags & FPI_IMAGE_COLORS_INVERTED) + invert_colors (image, self->width, self->height); lfsparms = g_memdup2 (&g_lfsparms_V2, sizeof (LFSPARMS)); - lfsparms->remove_perimeter_pts = data->flags & FPI_IMAGE_PARTIAL ? TRUE : FALSE; + lfsparms->remove_perimeter_pts = minutiae_flags & FPI_IMAGE_PARTIAL ? TRUE : FALSE; timer = g_timer_new (); - r = get_minutiae (&minutiae, &quality_map, &direction_map, + r = get_minutiae (&ret_data->minutiae, &quality_map, &direction_map, &low_contrast_map, &low_flow_map, &high_curve_map, - &map_w, &map_h, &bdata, &bw, &bh, &bd, - data->image, data->width, data->height, 8, - data->ppmm, lfsparms); + &map_w, &map_h, &ret_data->binarized, &bw, &bh, &bd, + image, self->width, self->height, 8, + self->ppmm, lfsparms); g_timer_stop (timer); fp_dbg ("Minutiae scan completed in %f secs", g_timer_elapsed (timer, NULL)); - data->binarized = g_steal_pointer (&bdata); - data->minutiae = minutiae; + if (g_task_had_error (thread_task)) + return; if (r) { fp_err ("get minutiae failed, code %d", r); - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Minutiae scan failed with code %d", r); - g_object_unref (task); + g_task_return_new_error (thread_task, G_IO_ERROR, + G_IO_ERROR_FAILED, + "Minutiae scan failed with code %d", r); return; } - if (!data->minutiae || data->minutiae->num == 0) + if (!ret_data->minutiae || ret_data->minutiae->num == 0) { - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, + g_task_return_new_error (thread_task, G_IO_ERROR, G_IO_ERROR_FAILED, "No minutiae found"); - g_object_unref (task); return; } - g_task_return_boolean (task, TRUE); - g_object_unref (task); + g_task_return_pointer (thread_task, g_steal_pointer (&ret_data), + (GDestroyNotify) fp_image_detect_minutiae_free); } /** @@ -445,21 +463,22 @@ fp_image_detect_minutiae (FpImage *self, GAsyncReadyCallback callback, gpointer user_data) { - GTask *task; - DetectMinutiaeData *data = g_new0 (DetectMinutiaeData, 1); + g_autoptr(GTask) task = NULL; - task = g_task_new (self, cancellable, fp_image_detect_minutiae_cb, user_data); + g_return_if_fail (FP_IS_IMAGE (self)); + g_return_if_fail (callback != NULL); - data->image = g_malloc (self->width * self->height); - memcpy (data->image, self->data, self->width * self->height); - data->flags = self->flags; - data->width = self->width; - data->height = self->height; - data->ppmm = self->ppmm; - data->user_cb = callback; + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, fp_image_detect_minutiae); + g_task_set_check_cancellable (task, TRUE); - g_task_set_task_data (task, data, (GDestroyNotify) fp_image_detect_minutiae_free); - g_task_run_in_thread (task, fp_image_detect_minutiae_thread_func); + if (!g_atomic_int_compare_and_exchange (&self->detection_in_progress, + FALSE, TRUE)) + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE, + "Minutiae detection is already in progress"); + + g_task_run_in_thread (g_steal_pointer (&task), + fp_image_detect_minutiae_nbis_thread_func); } /** @@ -477,7 +496,27 @@ fp_image_detect_minutiae_finish (FpImage *self, GAsyncResult *result, GError **error) { - return g_task_propagate_boolean (G_TASK (result), error); + GTask *task; + gboolean changed; + + g_return_val_if_fail (FP_IS_IMAGE (self), FALSE); + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == + fp_image_detect_minutiae, FALSE); + + task = G_TASK (result); + changed = g_atomic_int_compare_and_exchange (&self->detection_in_progress, + TRUE, FALSE); + g_assert (changed); + + if (g_task_had_error (task)) + { + gpointer data = g_task_propagate_pointer (task, error); + g_assert (data == NULL); + return FALSE; + } + + return fp_image_detect_minutiae_nbis_finish (self, task, error); } /** diff --git a/libfprint/fpi-image.h b/libfprint/fpi-image.h index e9ff803d..0c703fb1 100644 --- a/libfprint/fpi-image.h +++ b/libfprint/fpi-image.h @@ -68,6 +68,8 @@ struct _FpImage guint8 *binarized; GPtrArray *minutiae; + + gboolean detection_in_progress; }; gint fpi_std_sq_dev (const guint8 *buf, From 5a1253e37c52f0aef50ccc8ac8bd61af8d123f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 24 Nov 2023 21:02:50 +0100 Subject: [PATCH 17/64] fp-image: Do not start thread detection task thread if already running --- libfprint/fp-image.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libfprint/fp-image.c b/libfprint/fp-image.c index 4c4864fd..f9c60b37 100644 --- a/libfprint/fp-image.c +++ b/libfprint/fp-image.c @@ -474,8 +474,11 @@ fp_image_detect_minutiae (FpImage *self, if (!g_atomic_int_compare_and_exchange (&self->detection_in_progress, FALSE, TRUE)) - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE, - "Minutiae detection is already in progress"); + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE, + "Minutiae detection is already in progress"); + return; + } g_task_run_in_thread (g_steal_pointer (&task), fp_image_detect_minutiae_nbis_thread_func); From d3ec9a80d34bed983fa1b8cd1bd6a16896272ec8 Mon Sep 17 00:00:00 2001 From: Mohammed Anas Date: Mon, 22 Jan 2024 15:27:33 +0000 Subject: [PATCH 18/64] tests: remove Bash dependency in favor of `sh` The script works just fine with `sh`. Also replace nonstandard `test` operator `==` with the standard `=`. The other changes are mostly cosmetic. --- tests/test-generated-hwdb.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test-generated-hwdb.sh b/tests/test-generated-hwdb.sh index 7e1af144..b23f17c2 100755 --- a/tests/test-generated-hwdb.sh +++ b/tests/test-generated-hwdb.sh @@ -1,12 +1,11 @@ -#!/usr/bin/env bash -set -e +#!/bin/sh -e if [ ! -x "$UDEV_HWDB" ]; then echo "E: UDEV_HWDB (${UDEV_HWDB}) unset or not executable." exit 1 fi -if [ "$UDEV_HWDB_CHECK_CONTENTS" == 1 ]; then +if [ "$UDEV_HWDB_CHECK_CONTENTS" = 1 ]; then generated_rules=$(mktemp "${TMPDIR:-/tmp}/libfprint-XXXXXX.hwdb") else generated_rules=/dev/null From 1701d72ff9663b63f363e0c9feeb55bad6bcbe25 Mon Sep 17 00:00:00 2001 From: Mohammed Anas Date: Mon, 22 Jan 2024 15:18:27 +0000 Subject: [PATCH 19/64] tests: make `mktemp` command call work with Chimera Linux's `mktemp` On Chimera Linux, which uses FreeBSD's userland tools, the original call fails with the following error: mktemp: mkstemp failed on /tmp/libfprint-XXXXXX.hwdb: Invalid argument Moving the X's to the end of the template passed to `mktemp` fixes the error, and also works with GNU's `mktemp`. --- tests/test-generated-hwdb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-generated-hwdb.sh b/tests/test-generated-hwdb.sh index b23f17c2..b1031802 100755 --- a/tests/test-generated-hwdb.sh +++ b/tests/test-generated-hwdb.sh @@ -6,7 +6,7 @@ if [ ! -x "$UDEV_HWDB" ]; then fi if [ "$UDEV_HWDB_CHECK_CONTENTS" = 1 ]; then - generated_rules=$(mktemp "${TMPDIR:-/tmp}/libfprint-XXXXXX.hwdb") + generated_rules=$(mktemp "${TMPDIR:-/tmp}/libfprint.hwdb.XXXXXX") else generated_rules=/dev/null fi From 631da4654f1312b2336b0474502386cf46c6e08a Mon Sep 17 00:00:00 2001 From: xiaofei Date: Tue, 8 Nov 2022 10:43:15 +0800 Subject: [PATCH 20/64] focaltechmoc:Support FocalTech moc devices Supported UID: 0x2808 Supported PIDs: 0x9E48, 0xD979, 0xa959 --- data/autosuspend.hwdb | 7 + .../drivers/focaltech_moc/focaltech_moc.c | 1875 +++++++++++++++++ .../drivers/focaltech_moc/focaltech_moc.h | 52 + libfprint/meson.build | 2 + meson.build | 1 + tests/focaltech_moc/custom.pcapng | Bin 0 -> 38864 bytes tests/focaltech_moc/custom.py | 89 + tests/focaltech_moc/device | 385 ++++ tests/meson.build | 1 + 9 files changed, 2412 insertions(+) create mode 100644 libfprint/drivers/focaltech_moc/focaltech_moc.c create mode 100644 libfprint/drivers/focaltech_moc/focaltech_moc.h create mode 100644 tests/focaltech_moc/custom.pcapng create mode 100755 tests/focaltech_moc/custom.py create mode 100644 tests/focaltech_moc/device diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 2b4dc706..44dbbcd3 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -158,6 +158,13 @@ usb:v1C7Ap0603* ID_AUTOSUSPEND=1 ID_PERSIST=0 +# Supported by libfprint driver focaltech_moc +usb:v2808p9E48* +usb:v2808pD979* +usb:v2808pA959* + ID_AUTOSUSPEND=1 + ID_PERSIST=0 + # Supported by libfprint driver fpcmoc usb:v10A5pFFE0* usb:v10A5pA305* diff --git a/libfprint/drivers/focaltech_moc/focaltech_moc.c b/libfprint/drivers/focaltech_moc/focaltech_moc.c new file mode 100644 index 00000000..190ab436 --- /dev/null +++ b/libfprint/drivers/focaltech_moc/focaltech_moc.c @@ -0,0 +1,1875 @@ +/* + * Copyright (C) 2022 FocalTech Electronics Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "focaltech_moc.h" + +#include + +#define FP_COMPONENT "focaltech_moc" + +#include "drivers_api.h" + +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 = 0xa959, }, + { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ +}; + +typedef void (*SynCmdMsgCallback) (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error); + +typedef struct +{ + SynCmdMsgCallback callback; +} CommandData; + +typedef struct +{ + uint8_t h; + uint8_t l; +} FpCmdLen; + +typedef struct +{ + uint8_t magic; + FpCmdLen len; +} FpCmdHeader; + +typedef struct +{ + FpCmdHeader header; + uint8_t code; + uint8_t payload[0]; +} FpCmd; + +typedef struct +{ +#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) + uint8_t b0; + uint8_t b1; +#else + uint8_t b1; + uint8_t b0; +#endif +} u16_bytes_t; + +typedef union +{ + u16_bytes_t s; + uint16_t v; +} u_u16_bytes_t; + +static inline uint16_t +get_u16_from_u8_lh (uint8_t l, uint8_t h) +{ + u_u16_bytes_t u_u16_bytes; + + u_u16_bytes.v = 0; + u_u16_bytes.s.b0 = l; + u_u16_bytes.s.b1 = h; + + return u_u16_bytes.v; +} + +static inline uint8_t +get_u8_l_from_u16 (uint16_t v) +{ + u_u16_bytes_t u_u16_bytes; + + u_u16_bytes.v = v; + + return u_u16_bytes.s.b0; +} + +static inline uint8_t +get_u8_h_from_u16 (uint16_t v) +{ + u_u16_bytes_t u_u16_bytes; + + u_u16_bytes.v = v; + + return u_u16_bytes.s.b1; +} + +static uint8_t +fp_cmd_bcc (uint8_t *data, uint16_t len) +{ + int i; + uint8_t bcc = 0; + + for (i = 0; i < len; i++) + bcc ^= data[i]; + + return bcc; +} + +static uint8_t * +focaltech_moc_compose_cmd (uint8_t cmd, const uint8_t *data, uint16_t len) +{ + g_autofree char *cmd_buf = NULL; + FpCmd *fp_cmd = NULL; + uint8_t *bcc = NULL; + uint16_t header_len = len + sizeof (*bcc); + + cmd_buf = g_malloc0 (sizeof (FpCmd) + header_len); + + fp_cmd = (FpCmd *) cmd_buf; + + fp_cmd->header.magic = 0x02; + fp_cmd->header.len.l = get_u8_l_from_u16 (header_len); + fp_cmd->header.len.h = get_u8_h_from_u16 (header_len); + fp_cmd->code = cmd; + + if (data != NULL) + memcpy (fp_cmd->payload, data, len); + + bcc = fp_cmd->payload + len; + *bcc = fp_cmd_bcc ((uint8_t *) &fp_cmd->header.len, bcc - (uint8_t *) &fp_cmd->header.len); + + return g_steal_pointer (&cmd_buf); +} + +static int +focaltech_moc_check_cmd (uint8_t *response_buf, uint16_t len) +{ + int ret = -1; + FpCmd *fp_cmd = NULL; + uint8_t *bcc = NULL; + uint16_t header_len; + uint16_t data_len; + + fp_cmd = (FpCmd *) response_buf; + + if (len < sizeof (FpCmd) + sizeof (*bcc)) + return ret; + + if (fp_cmd->header.magic != 0x02) + return ret; + + header_len = get_u16_from_u8_lh (fp_cmd->header.len.l, fp_cmd->header.len.h); + + if (header_len < sizeof (*bcc)) + return ret; + + if ((sizeof (FpCmd) + header_len) > len) + return ret; + + data_len = header_len - sizeof (*bcc); + + bcc = fp_cmd->payload + data_len; + + if (fp_cmd_bcc ((uint8_t *) &fp_cmd->header.len, + bcc - (uint8_t *) &fp_cmd->header.len) != *bcc) + return ret; + + ret = 0; + return ret; +} + +static void +fp_cmd_receive_cb (FpiUsbTransfer *transfer, + FpDevice *device, + gpointer userdata, + GError *error) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + CommandData *data = userdata; + int ssm_state = 0; + + if (error) + { + fpi_ssm_mark_failed (transfer->ssm, g_steal_pointer (&error)); + return; + } + + if (data == NULL) + { + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + return; + } + + ssm_state = fpi_ssm_get_cur_state (transfer->ssm); + + /* skip zero length package */ + if (transfer->actual_length == 0) + { + fpi_ssm_jump_to_state (transfer->ssm, ssm_state); + return; + } + + if (focaltech_moc_check_cmd (transfer->buffer, transfer->actual_length) != 0) + { + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + return; + } + + if (data->callback) + data->callback (self, transfer->buffer, transfer->actual_length, NULL); + + fpi_ssm_mark_completed (transfer->ssm); +} + +typedef enum { + FP_CMD_SEND = 0, + FP_CMD_GET, + FP_CMD_NUM_STATES, +} FpCmdState; + +static void +fp_cmd_run_state (FpiSsm *ssm, + FpDevice *device) +{ + FpiUsbTransfer *transfer; + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + switch (fpi_ssm_get_cur_state (ssm)) + { + case FP_CMD_SEND: + if (self->cmd_transfer) + { + self->cmd_transfer->ssm = ssm; + fpi_usb_transfer_submit (g_steal_pointer (&self->cmd_transfer), + FOCALTECH_MOC_CMD_TIMEOUT, + NULL, + fpi_ssm_usb_transfer_cb, + NULL); + } + else + { + fpi_ssm_next_state (ssm); + } + + break; + + case FP_CMD_GET: + if (self->cmd_len_in == 0) + { + CommandData *data = fpi_ssm_get_data (ssm); + + if (data->callback) + data->callback (self, NULL, 0, 0); + + fpi_ssm_mark_completed (ssm); + return; + } + + transfer = fpi_usb_transfer_new (device); + transfer->ssm = ssm; + fpi_usb_transfer_fill_bulk (transfer, self->bulk_in_ep, self->cmd_len_in); + fpi_usb_transfer_submit (transfer, + self->cmd_cancelable ? 0 : FOCALTECH_MOC_CMD_TIMEOUT, + self->cmd_cancelable ? fpi_device_get_cancellable (device) : NULL, + fp_cmd_receive_cb, + fpi_ssm_get_data (ssm)); + break; + + } + +} + +static void +fp_cmd_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + g_autoptr(GError) local_error = error; + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + CommandData *data = fpi_ssm_get_data (ssm); + + self->cmd_ssm = NULL; + + if (local_error && data->callback) + data->callback (self, NULL, 0, g_steal_pointer (&local_error)); +} + +static void +fp_cmd_ssm_done_data_free (CommandData *data) +{ + g_free (data); +} + +static void +focaltech_moc_get_cmd (FpDevice *device, guint8 *buffer_out, + gsize length_out, gsize length_in, + gboolean can_be_cancelled, + SynCmdMsgCallback callback) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + g_autoptr(FpiUsbTransfer) transfer = NULL; + CommandData *data = g_new0 (CommandData, 1); + + transfer = fpi_usb_transfer_new (device); + transfer->short_is_error = TRUE; + fpi_usb_transfer_fill_bulk_full (transfer, self->bulk_out_ep, buffer_out, + length_out, g_free); + data->callback = callback; + + self->cmd_transfer = g_steal_pointer (&transfer); + self->cmd_len_in = length_in + 1; + self->cmd_cancelable = can_be_cancelled; + + self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self), + fp_cmd_run_state, + FP_CMD_NUM_STATES); + + fpi_ssm_set_data (self->cmd_ssm, data, (GDestroyNotify) fp_cmd_ssm_done_data_free); + + fpi_ssm_start (self->cmd_ssm, fp_cmd_ssm_done); +} + +struct UserId +{ + uint8_t uid[32]; +}; + +static void +fprint_set_uid (FpPrint *print, uint8_t *uid, size_t size) +{ + GVariant *var_uid; + GVariant *var_data; + + var_uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, uid, size, 1); + var_data = g_variant_new ("(@ay)", var_uid); + fpi_print_set_type (print, FPI_PRINT_RAW); + fpi_print_set_device_stored (print, TRUE); + g_object_set (print, "fpi-data", var_data, NULL); +} + +enum enroll_states { + ENROLL_RSP_RETRY, + ENROLL_RSP_ENROLL_REPORT, + ENROLL_RSP_ENROLL_OK, + ENROLL_RSP_ENROLL_CANCEL_REPORT, +}; + +static void +enroll_status_report (FpiDeviceFocaltechMoc *self, int enroll_status_id, + int data, GError *error) +{ + FpDevice *device = FP_DEVICE (self); + + switch (enroll_status_id) + { + case ENROLL_RSP_RETRY: + { + fpi_device_enroll_progress (device, self->num_frames, NULL, + fpi_device_retry_new (FP_DEVICE_RETRY_CENTER_FINGER)); + break; + } + + case ENROLL_RSP_ENROLL_REPORT: + { + fpi_device_enroll_progress (device, self->num_frames, NULL, NULL); + break; + } + + case ENROLL_RSP_ENROLL_OK: + { + FpPrint *print = NULL; + fp_info ("Enrollment was successful!"); + fpi_device_get_enroll_data (device, &print); + fpi_device_enroll_complete (device, g_object_ref (print), NULL); + break; + } + + case ENROLL_RSP_ENROLL_CANCEL_REPORT: + { + fpi_device_enroll_complete (device, NULL, + fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Enrollment failed (%d) (ENROLL_RSP_ENROLL_CANCEL_REPORT)", + data)); + } + } +} + +static void +task_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + self->num_frames = 0; + self->task_ssm = NULL; + + if (error) + fpi_device_action_error (device, g_steal_pointer (&error)); +} + +static const char * +get_g_usb_device_direction_des (GUsbDeviceDirection dir) +{ + switch (dir) + { + case G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST: + return "G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST"; + + case G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE: + return "G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE"; + + default: + return "unknown"; + } +} + +static int +usb_claim_interface_probe (FpDevice *device, int claim, GError **error) +{ + g_autoptr(GPtrArray) interfaces = NULL; + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + int ret = -1; + int i; + + interfaces = g_usb_device_get_interfaces (fpi_device_get_usb_device (device), error); + + for (i = 0; i < interfaces->len; i++) + { + GUsbInterface *cur_iface = g_ptr_array_index (interfaces, i); + g_autoptr(GPtrArray) endpoints = g_usb_interface_get_endpoints (cur_iface); + + fp_dbg ("class:%x, subclass:%x, protocol:%x", + g_usb_interface_get_class (cur_iface), + g_usb_interface_get_subclass (cur_iface), + g_usb_interface_get_protocol (cur_iface)); + + if (claim == 1) + { + int j; + + for (j = 0; j < endpoints->len; j++) + { + GUsbEndpoint *endpoint = g_ptr_array_index (endpoints, j); + GBytes *bytes = g_usb_endpoint_get_extra (endpoint); + + fp_dbg ("bytes size:%ld", g_bytes_get_size (bytes)); + + fp_dbg ("kind:%x, max packet size:%d, poll interval:%d, refresh:%x, " + "sync address:%x, address:%x, number:%d, direction:%s", + g_usb_endpoint_get_kind (endpoint), + g_usb_endpoint_get_maximum_packet_size (endpoint), + g_usb_endpoint_get_polling_interval (endpoint), + g_usb_endpoint_get_refresh (endpoint), + g_usb_endpoint_get_synch_address (endpoint), + g_usb_endpoint_get_address (endpoint), + g_usb_endpoint_get_number (endpoint), + get_g_usb_device_direction_des (g_usb_endpoint_get_direction (endpoint))); + + if (g_usb_endpoint_get_direction (endpoint) == G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST) + self->bulk_in_ep = g_usb_endpoint_get_address (endpoint); + else + self->bulk_out_ep = g_usb_endpoint_get_address (endpoint); + } + + if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), + g_usb_interface_get_number (cur_iface), + 0, error)) + return ret; + } + else if (!g_usb_device_release_interface (fpi_device_get_usb_device (device), + g_usb_interface_get_number (cur_iface), + 0, error)) + { + return ret; + } + + + } + + ret = 0; + + return ret; +} + +static void +task_ssm_init_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + if (error) + usb_claim_interface_probe (device, 0, &error); + + fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error)); +} + +struct EnrollTimes +{ + uint8_t enroll_times; +}; + +static void +focaltech_moc_get_enroll_times (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct EnrollTimes *enroll_times = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + enroll_times = (struct EnrollTimes *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + fp_dbg ("focaltechmoc enroll_times: %d", enroll_times->enroll_times + 1); + fpi_device_set_nr_enroll_stages (FP_DEVICE (self), enroll_times->enroll_times + 1); + fpi_ssm_next_state (self->task_ssm); + } +} + +static void +focaltech_moc_release_finger (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + fpi_ssm_next_state (self->task_ssm); + } +} + +enum dev_init_states { + DEV_INIT_GET_ENROLL_TIMES, + DEV_INIT_RELEASE_FINGER, + DEV_INIT_STATES, +}; + +static void +dev_init_handler (FpiSsm *ssm, FpDevice *device) +{ + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case DEV_INIT_GET_ENROLL_TIMES: + cmd_len = 0; + resp_len = sizeof (struct EnrollTimes); + cmd_buf = focaltech_moc_compose_cmd (0xa5, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enroll_times); + break; + + case DEV_INIT_RELEASE_FINGER: + { + uint8_t d1 = 0x78; + cmd_len = sizeof (d1); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0x82, &d1, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_release_finger); + break; + } + } +} + +static void +focaltech_moc_open (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + GError *error = NULL; + + if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) + { + fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error)); + return; + } + + if (usb_claim_interface_probe (device, 1, &error) != 0) + { + fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error)); + return; + } + + self->task_ssm = fpi_ssm_new (FP_DEVICE (self), dev_init_handler, DEV_INIT_STATES); + fpi_ssm_start (self->task_ssm, task_ssm_init_done); +} + +static void +task_ssm_exit_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + if (!error) + { + GError *local_error = NULL; + + if (usb_claim_interface_probe (device, 0, &local_error) < 0) + g_propagate_error (&error, g_steal_pointer (&local_error)); + } + + fpi_device_close_complete (FP_DEVICE (self), error); + self->task_ssm = NULL; +} + +enum dev_exit_states { + DEV_EXIT_START, + DEV_EXIT_STATES, +}; + +static void +dev_exit_handler (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + switch (fpi_ssm_get_cur_state (ssm)) + { + case DEV_EXIT_START: + fpi_ssm_next_state (self->task_ssm); + break; + + default: + g_assert_not_reached (); + } +} + +static void +focaltech_moc_close (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + fp_info ("Focaltechmoc dev_exit"); + self->task_ssm = fpi_ssm_new (FP_DEVICE (self), dev_exit_handler, DEV_EXIT_STATES); + fpi_ssm_start (self->task_ssm, task_ssm_exit_done); +} + +enum identify_states { + MOC_IDENTIFY_RELEASE_FINGER, + MOC_IDENTIFY_WAIT_FINGER, + MOC_IDENTIFY_WAIT_FINGER_DELAY, + MOC_IDENTIFY_CAPTURE, + MOC_IDENTIFY_MATCH, + MOC_IDENTIFY_NUM_STATES, +}; + +static void +focaltech_moc_identify_wait_finger_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + uint8_t *finger_status = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + finger_status = (uint8_t *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + + if (*finger_status == 0x01) + fpi_ssm_jump_to_state (self->task_ssm, MOC_IDENTIFY_CAPTURE); + else + fpi_ssm_jump_to_state (self->task_ssm, MOC_IDENTIFY_WAIT_FINGER_DELAY); + } +} + +static void +focaltech_moc_identify_wait_finger_delay (FpDevice *device, + void *user_data) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + fpi_ssm_jump_to_state (self->task_ssm, MOC_IDENTIFY_WAIT_FINGER); +} + +enum FprintError { + ERROR_NONE, + ERROR_QUALITY, + ERROR_SHORT, + ERROR_LEFT, + ERROR_RIGHT, + ERROR_NONFINGER, + ERROR_NOMOVE, + ERROR_OTHER, +}; + +struct CaptureResult +{ + uint8_t error; + uint8_t remain; +}; + +static void +focaltech_moc_identify_capture_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct CaptureResult *capture_result = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + capture_result = (struct CaptureResult *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + if (capture_result->error == ERROR_NONE) + { + fpi_ssm_next_state (self->task_ssm); + } + else + { + if (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_VERIFY) + { + fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_ERROR, NULL, error); + fpi_device_verify_complete (FP_DEVICE (self), NULL); + } + else + { + fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, error); + fpi_device_identify_complete (FP_DEVICE (self), NULL); + } + + fpi_ssm_mark_failed (self->task_ssm, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); + } + } +} + +static void +identify_status_report (FpiDeviceFocaltechMoc *self, FpPrint *print, GError *error) +{ + FpDevice *device = FP_DEVICE (self); + + if (print == NULL) + { + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY) + { + fpi_device_identify_report (device, NULL, NULL, NULL); + fpi_device_identify_complete (device, NULL); + } + else + { + fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); + fpi_device_verify_complete (device, NULL); + } + } + else + { + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY) + { + GPtrArray *prints; + gboolean found = FALSE; + guint index; + + fpi_device_get_identify_data (device, &prints); + found = g_ptr_array_find_with_equal_func (prints, + print, + (GEqualFunc) fp_print_equal, + &index); + + if (found) + fpi_device_identify_report (device, g_ptr_array_index (prints, index), print, NULL); + else + fpi_device_identify_report (device, NULL, print, NULL); + + fpi_device_identify_complete (device, NULL); + } + else + { + FpPrint *verify_print = NULL; + fpi_device_get_verify_data (device, &verify_print); + + if (fp_print_equal (verify_print, print)) + fpi_device_verify_report (device, FPI_MATCH_SUCCESS, print, NULL); + else + fpi_device_verify_report (device, FPI_MATCH_FAIL, print, NULL); + + fpi_device_verify_complete (device, NULL); + } + } +} + +static void +focaltech_moc_identify_match_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct UserId *user_id = NULL; + FpPrint *print = NULL; + + fp_cmd = (FpCmd *) buffer_in; + user_id = (struct UserId *) (fp_cmd + 1); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + if (fp_cmd->code == 0x04) + { + print = fp_print_new (FP_DEVICE (self)); + fprint_set_uid (print, user_id->uid, sizeof (user_id->uid)); + } + + identify_status_report (self, print, error); + + fpi_ssm_next_state (self->task_ssm); +} + +static void +focaltech_identify_run_state (FpiSsm *ssm, FpDevice *device) +{ + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case MOC_IDENTIFY_RELEASE_FINGER: + { + uint8_t d1 = 0x78; + cmd_len = sizeof (d1); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0x82, &d1, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_release_finger); + break; + } + + case MOC_IDENTIFY_WAIT_FINGER: + { + uint8_t data = 0x02; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (uint8_t); + cmd_buf = focaltech_moc_compose_cmd (0x80, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_identify_wait_finger_cb); + break; + } + + case MOC_IDENTIFY_WAIT_FINGER_DELAY: + fpi_device_add_timeout (device, 50, + focaltech_moc_identify_wait_finger_delay, + NULL, NULL); + break; + + case MOC_IDENTIFY_CAPTURE: + cmd_len = 0; + resp_len = sizeof (struct CaptureResult); + cmd_buf = focaltech_moc_compose_cmd (0xa6, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_identify_capture_cb); + break; + + case MOC_IDENTIFY_MATCH: + cmd_len = 0; + resp_len = sizeof (struct UserId); + cmd_buf = focaltech_moc_compose_cmd (0xaa, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_identify_match_cb); + break; + } +} + +static void +focaltech_moc_identify (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + self->task_ssm = fpi_ssm_new (device, + focaltech_identify_run_state, + MOC_IDENTIFY_NUM_STATES); + fpi_ssm_start (self->task_ssm, task_ssm_done); +} + +enum moc_enroll_states { + MOC_ENROLL_GET_ENROLLED_INFO, + MOC_ENROLL_GET_ENROLLED_LIST, + MOC_ENROLL_RELEASE_FINGER, + MOC_ENROLL_START_ENROLL, + MOC_ENROLL_WAIT_FINGER, + MOC_ENROLL_WAIT_FINGER_DELAY, + MOC_ENROLL_ENROLL_CAPTURE, + MOC_ENROLL_SET_ENROLLED_INFO, + MOC_ENROLL_COMMIT_RESULT, + MOC_ENROLL_NUM_STATES, +}; + +struct EnrolledInfoItem +{ + uint8_t uid[FOCALTECH_MOC_UID_PREFIX_LENGTH]; + uint8_t user_id[FOCALTECH_MOC_USER_ID_LENGTH]; +}; + +struct UserDes +{ + uint8_t finger; + char username[FOCALTECH_MOC_USER_ID_LENGTH]; +}; + +struct EnrolledInfo +{ + uint8_t actived[FOCALTECH_MOC_MAX_FINGERS]; + struct EnrolledInfoItem items[FOCALTECH_MOC_MAX_FINGERS]; + struct UserId user_id[FOCALTECH_MOC_MAX_FINGERS]; + struct UserDes user_des[FOCALTECH_MOC_MAX_FINGERS]; +}; + +typedef struct +{ + GPtrArray *list_result; + struct EnrolledInfo *enrolled_info; +} FpActionData; + +struct EnrolledInfoSetData +{ + uint8_t data; + struct EnrolledInfoItem items[FOCALTECH_MOC_MAX_FINGERS]; +}; + +static void +fp_action_ssm_done_data_free (FpActionData *data) +{ + g_clear_pointer (&data->list_result, g_ptr_array_unref); + g_clear_pointer (&data->enrolled_info, g_free); + g_free (data); +} + +static void +focaltech_moc_get_enrolled_info_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct EnrolledInfoItem *items = NULL; + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + items = (struct EnrolledInfoItem *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + memcpy (&data->enrolled_info->items[0], items, + FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem)); + fpi_ssm_next_state (self->task_ssm); + } +} + +struct UidList +{ + uint8_t actived[FOCALTECH_MOC_MAX_FINGERS]; + struct UserId uid[FOCALTECH_MOC_MAX_FINGERS]; +}; + +static int +focaltech_moc_get_enrolled_info_item (FpiDeviceFocaltechMoc *self, + uint8_t *uid, + struct EnrolledInfoItem **pitem, int *index) +{ + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + int ret = -1; + int i; + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + struct EnrolledInfoItem *item = &data->enrolled_info->items[i]; + + if (memcmp (item->uid, uid, FOCALTECH_MOC_UID_PREFIX_LENGTH) == 0) + { + data->enrolled_info->actived[i] = 1; + *pitem = item; + *index = i; + ret = 0; + break; + } + } + + return ret; +} + +static void +focaltech_moc_get_enrolled_list_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct UidList *uid_list = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + uid_list = (struct UidList *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + int i; + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + if (uid_list->actived[i] != 0) + { + struct UserId *user_id = &uid_list->uid[i]; + FpPrint *print = fp_print_new (FP_DEVICE (self)); + struct EnrolledInfoItem *item = NULL; + int index; + + fp_info ("focaltechmoc add slot: %d", i); + + fprint_set_uid (print, user_id->uid, sizeof (user_id->uid)); + + if (focaltech_moc_get_enrolled_info_item (self, user_id->uid, &item, &index) == 0) + { + g_autofree gchar *userid_safe = NULL; + const gchar *username; + userid_safe = g_strndup ((const char *) &item->user_id, FOCALTECH_MOC_USER_ID_LENGTH); + fp_dbg ("%s", userid_safe); + fpi_print_fill_from_user_id (print, userid_safe); + memcpy (data->enrolled_info->user_id[index].uid, user_id->uid, 32); + data->enrolled_info->user_des[index].finger = fp_print_get_finger (print); + username = fp_print_get_username (print); + + if (username != NULL) + strncpy (data->enrolled_info->user_des[index].username, username, 64); + } + + g_ptr_array_add (data->list_result, g_object_ref_sink (print)); + } + } + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + struct EnrolledInfoItem *item = &data->enrolled_info->items[i]; + + if (data->enrolled_info->actived[i] == 0) + memset (item, 0, sizeof (struct EnrolledInfoItem)); + } + + fpi_ssm_next_state (self->task_ssm); + } +} + +static void +focaltech_moc_enroll_wait_finger_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + uint8_t *finger_status = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + finger_status = (uint8_t *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER_DELAY); + } + else + { + + if (*finger_status == 0x01) + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_ENROLL_CAPTURE); + else + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER_DELAY); + } +} + +static void +focaltech_moc_enroll_wait_finger_delay (FpDevice *device, + void *user_data + ) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER); +} + +static void +focaltech_moc_start_enroll_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct UserId *user_id = NULL; + FpPrint *print = NULL; + struct EnrolledInfoItem *item = NULL; + int index; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + user_id = (struct UserId *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + if (fp_cmd->code == 0x05) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_FULL, + "device data full!!")); + } + else + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + return; + } + + if (focaltech_moc_get_enrolled_info_item (self, user_id->uid, &item, &index) == 0) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "uid error!!")); + } + else + { + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + g_autofree gchar *userid_safe = NULL; + gsize userid_len; + uint8_t found = 0; + int i; + struct EnrolledInfoItem *free_item = NULL; + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + item = &data->enrolled_info->items[i]; + + if (data->enrolled_info->actived[i] == 0) + { + found = 1; + free_item = item; + break; + } + } + + if (found == 0) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "no uid slot!!")); + } + else + { + fpi_device_get_enroll_data (FP_DEVICE (self), &print); + fprint_set_uid (print, user_id->uid, sizeof (user_id->uid)); + userid_safe = fpi_print_generate_user_id (print); + userid_len = strlen (userid_safe); + userid_len = MIN (FOCALTECH_MOC_USER_ID_LENGTH, userid_len); + fp_info ("focaltechmoc user id: %s", userid_safe); + memcpy (free_item->uid, user_id->uid, FOCALTECH_MOC_UID_PREFIX_LENGTH); + memcpy (free_item->user_id, userid_safe, userid_len); + fpi_ssm_next_state (self->task_ssm); + } + } +} + +static void +focaltech_moc_enroll_capture_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct CaptureResult *capture_result = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + capture_result = (struct CaptureResult *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + if (capture_result->error == ERROR_NONE) + { + self->num_frames += 1; + enroll_status_report (self, ENROLL_RSP_ENROLL_REPORT, self->num_frames, NULL); + fp_info ("focaltechmoc remain: %d", capture_result->remain); + } + else + { + enroll_status_report (self, ENROLL_RSP_RETRY, self->num_frames, NULL); + } + + if (self->num_frames == fp_device_get_nr_enroll_stages (FP_DEVICE (self))) + fpi_ssm_next_state (self->task_ssm); + else + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER); + } +} + +static void +focaltech_moc_set_enrolled_info_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + return; + } + + fpi_ssm_next_state (self->task_ssm); +} + +static void +focaltech_moc_commit_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + fp_info ("focaltech_moc_commit_cb success"); + enroll_status_report (self, ENROLL_RSP_ENROLL_OK, self->num_frames, NULL); + fpi_ssm_next_state (self->task_ssm); + } +} + +static void +focaltech_enroll_run_state (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case MOC_ENROLL_GET_ENROLLED_INFO: + { + uint8_t data = 0x00; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (struct EnrolledInfoItem) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xaf, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_info_cb); + break; + } + + case MOC_ENROLL_GET_ENROLLED_LIST: + { + cmd_len = 0; + resp_len = sizeof (struct UidList); + cmd_buf = focaltech_moc_compose_cmd (0xab, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_list_cb); + break; + } + + case MOC_ENROLL_RELEASE_FINGER: + { + uint8_t d1 = 0x78; + cmd_len = sizeof (d1); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0x82, &d1, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_release_finger); + break; + } + + case MOC_ENROLL_START_ENROLL: + cmd_len = 0; + resp_len = sizeof (struct UserId); + cmd_buf = focaltech_moc_compose_cmd (0xa9, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_start_enroll_cb); + break; + + + case MOC_ENROLL_WAIT_FINGER: + { + uint8_t data = 0x02; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (uint8_t); + cmd_buf = focaltech_moc_compose_cmd (0x80, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_enroll_wait_finger_cb); + break; + } + + case MOC_ENROLL_WAIT_FINGER_DELAY: + fpi_device_add_timeout (device, 50, + focaltech_moc_enroll_wait_finger_delay, + NULL, NULL); + break; + + case MOC_ENROLL_ENROLL_CAPTURE: + cmd_len = 0; + resp_len = sizeof (struct CaptureResult); + cmd_buf = focaltech_moc_compose_cmd (0xa6, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_enroll_capture_cb); + break; + + case MOC_ENROLL_SET_ENROLLED_INFO: + { + g_autofree struct EnrolledInfoSetData *set_data = NULL; + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + + cmd_len = sizeof (struct EnrolledInfoSetData); + resp_len = 0; + set_data = (struct EnrolledInfoSetData *) g_malloc0 (cmd_len); + set_data->data = 0x01; + memcpy (&set_data->items[0], &data->enrolled_info->items[0], + FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem)); + cmd_buf = focaltech_moc_compose_cmd (0xaf, (const uint8_t *) set_data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_set_enrolled_info_cb); + break; + } + + case MOC_ENROLL_COMMIT_RESULT: + { + FpPrint *print = NULL; + g_autoptr(GVariant) data = NULL; + g_autoptr(GVariant) user_id_var = NULL; + const guint8 *user_id; + gsize user_id_len = 0; + + fpi_device_get_enroll_data (FP_DEVICE (self), &print); + g_object_get (print, "fpi-data", &data, NULL); + + if (!g_variant_check_format_string (data, "(@ay)", FALSE)) + { + fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + return; + } + + g_variant_get (data, + "(@ay)", + &user_id_var); + user_id = g_variant_get_fixed_array (user_id_var, &user_id_len, 1); + + cmd_len = user_id_len; + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0xa3, user_id, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_commit_cb); + break; + } + } +} + +static void +focaltech_moc_enroll (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpActionData *data = g_new0 (FpActionData, 1); + + data->enrolled_info = g_new0 (struct EnrolledInfo, 1); + data->list_result = g_ptr_array_new_with_free_func (g_object_unref); + + self->task_ssm = fpi_ssm_new (FP_DEVICE (self), + focaltech_enroll_run_state, + MOC_ENROLL_NUM_STATES); + fpi_ssm_set_data (self->task_ssm, data, (GDestroyNotify) fp_action_ssm_done_data_free); + fpi_ssm_start (self->task_ssm, task_ssm_done); +} + +static void +focaltech_moc_delete_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + int ssm_state; + + if (self->delete_slot != -1) + { + fp_dbg ("delete slot %d", self->delete_slot); + data->enrolled_info->actived[self->delete_slot] = 0; + memset (&data->enrolled_info->items[self->delete_slot], 0, sizeof (struct EnrolledInfoItem)); + memset (&data->enrolled_info->user_id[self->delete_slot], 0, sizeof (struct UserId)); + memset (&data->enrolled_info->user_des[self->delete_slot], 0, sizeof (struct UserDes)); + } + + ssm_state = fpi_ssm_get_cur_state (self->task_ssm); + fpi_ssm_jump_to_state (self->task_ssm, ssm_state); + } +} + +enum delete_states { + MOC_DELETE_GET_ENROLLED_INFO, + MOC_DELETE_GET_ENROLLED_LIST, + MOC_DELETE_SET_ENROLLED_INFO, + MOC_DELETE_BY_UID, + MOC_DELETE_BY_USER_INFO, + MOC_DELETE_NUM_STATES, +}; + +static void +focaltech_delete_run_state (FpiSsm *ssm, FpDevice *device) +{ + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case MOC_DELETE_GET_ENROLLED_INFO: + { + uint8_t data = 0x00; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (struct EnrolledInfoItem) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xaf, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_info_cb); + break; + } + + case MOC_DELETE_GET_ENROLLED_LIST: + { + cmd_len = 0; + resp_len = sizeof (struct UidList) + sizeof (struct UserId) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xab, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_list_cb); + break; + } + + case MOC_DELETE_SET_ENROLLED_INFO: + { + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + g_autofree struct EnrolledInfoSetData *set_data = NULL; + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + + cmd_len = sizeof (struct EnrolledInfoSetData); + resp_len = 0; + set_data = (struct EnrolledInfoSetData *) g_malloc0 (cmd_len); + set_data->data = 0x01; + memcpy (&set_data->items[0], &data->enrolled_info->items[0], FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem)); + cmd_buf = focaltech_moc_compose_cmd (0xaf, (const uint8_t *) set_data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_set_enrolled_info_cb); + break; + } + + case MOC_DELETE_BY_UID: + { + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpPrint *print = NULL; + g_autoptr(GVariant) data = NULL; + g_autoptr(GVariant) user_id_var = NULL; + const guint8 *user_id; + gsize user_id_len = 0; + struct EnrolledInfoItem *item = NULL; + int index; + + self->delete_slot = -1; + fpi_device_get_delete_data (device, &print); + g_object_get (print, "fpi-data", &data, NULL); + + if (!g_variant_check_format_string (data, "(@ay)", FALSE)) + { + fpi_device_delete_complete (device, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + return; + } + + g_variant_get (data, "(@ay)", &user_id_var); + user_id = g_variant_get_fixed_array (user_id_var, &user_id_len, 1); + + if (focaltech_moc_get_enrolled_info_item (self, (uint8_t *) user_id, &item, &index) == 0) + self->delete_slot = index; + + if (self->delete_slot != -1) + { + cmd_len = sizeof (struct UserId); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0xa8, user_id, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_delete_cb); + } + else + { + fpi_ssm_next_state (self->task_ssm); + } + + break; + } + + case MOC_DELETE_BY_USER_INFO: + { + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + FpPrint *print = NULL; + const guint8 *user_id; + const gchar *username; + uint8_t finger; + int i; + + self->delete_slot = -1; + fpi_device_get_delete_data (device, &print); + username = fp_print_get_username (print); + finger = fp_print_get_finger (print); + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + struct UserDes *user_des = &data->enrolled_info->user_des[i]; + + if (username == NULL) + continue; + + if (strncmp (user_des->username, username, FOCALTECH_MOC_USER_ID_LENGTH) != 0) + continue; + + if (finger != user_des->finger) + continue; + + self->delete_slot = i; + } + + if (self->delete_slot != -1) + { + user_id = (const guint8 *) &data->enrolled_info->user_id[self->delete_slot].uid[0]; + cmd_len = sizeof (struct UserId); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0xa8, user_id, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_delete_cb); + } + else + { + fpi_device_delete_complete (FP_DEVICE (self), NULL); + fpi_ssm_next_state (self->task_ssm); + } + + break; + } + } +} + +static void +focaltech_moc_delete_print (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpActionData *data = g_new0 (FpActionData, 1); + + data->enrolled_info = g_new0 (struct EnrolledInfo, 1); + data->list_result = g_ptr_array_new_with_free_func (g_object_unref); + + self->task_ssm = fpi_ssm_new (device, + focaltech_delete_run_state, + MOC_DELETE_NUM_STATES); + fpi_ssm_set_data (self->task_ssm, data, (GDestroyNotify) fp_action_ssm_done_data_free); + fpi_ssm_start (self->task_ssm, task_ssm_done); +} + +enum moc_list_states { + MOC_LIST_GET_ENROLLED_INFO, + MOC_LIST_GET_ENROLLED_LIST, + MOC_LIST_REPORT, + MOC_LIST_NUM_STATES, +}; + +static void +focaltech_list_run_state (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case MOC_LIST_GET_ENROLLED_INFO: + { + uint8_t data = 0x00; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (struct EnrolledInfoItem) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xaf, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_info_cb); + break; + } + + case MOC_LIST_GET_ENROLLED_LIST: + { + cmd_len = 0; + resp_len = sizeof (struct UidList) + sizeof (struct UserId) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xab, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_list_cb); + break; + } + + case MOC_LIST_REPORT: + { + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + fpi_device_list_complete (FP_DEVICE (self), g_steal_pointer (&data->list_result), NULL); + fpi_ssm_next_state (self->task_ssm); + break; + } + } +} + +static void +focaltech_moc_list (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpActionData *data = g_new0 (FpActionData, 1); + + data->enrolled_info = g_new0 (struct EnrolledInfo, 1); + data->list_result = g_ptr_array_new_with_free_func (g_object_unref); + self->task_ssm = fpi_ssm_new (device, + focaltech_list_run_state, + MOC_LIST_NUM_STATES); + fpi_ssm_set_data (self->task_ssm, data, (GDestroyNotify) fp_action_ssm_done_data_free); + fpi_ssm_start (self->task_ssm, task_ssm_done); +} + +static void +fpi_device_focaltech_moc_init (FpiDeviceFocaltechMoc *self) +{ + G_DEBUG_HERE (); +} + +static void +fpi_device_focaltech_moc_class_init (FpiDeviceFocaltechMocClass *klass) +{ + FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); + + dev_class->id = FP_COMPONENT; + dev_class->full_name = FOCALTECH_MOC_DRIVER_FULLNAME; + + dev_class->type = FP_DEVICE_TYPE_USB; + dev_class->scan_type = FP_SCAN_TYPE_PRESS; + dev_class->id_table = id_table; + dev_class->nr_enroll_stages = FOCALTECH_MOC_MAX_FINGERS; + dev_class->temp_hot_seconds = -1; + + dev_class->open = focaltech_moc_open; + dev_class->close = focaltech_moc_close; + dev_class->verify = focaltech_moc_identify; + dev_class->enroll = focaltech_moc_enroll; + dev_class->identify = focaltech_moc_identify; + dev_class->delete = focaltech_moc_delete_print; + dev_class->list = focaltech_moc_list; + + fpi_device_class_auto_initialize_features (dev_class); +} diff --git a/libfprint/drivers/focaltech_moc/focaltech_moc.h b/libfprint/drivers/focaltech_moc/focaltech_moc.h new file mode 100644 index 00000000..9fcbec5d --- /dev/null +++ b/libfprint/drivers/focaltech_moc/focaltech_moc.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 Focaltech Microelectronics + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "fpi-device.h" +#include "fpi-ssm.h" +#include + +#include +#include + +G_DECLARE_FINAL_TYPE (FpiDeviceFocaltechMoc, fpi_device_focaltech_moc, FPI, DEVICE_FOCALTECH_MOC, FpDevice) + +#define FOCALTECH_MOC_DRIVER_FULLNAME "Focaltech MOC Sensors" + +#define FOCALTECH_MOC_CMD_TIMEOUT 1000 +#define FOCALTECH_MOC_MAX_FINGERS 10 +#define FOCALTECH_MOC_UID_PREFIX_LENGTH 8 +#define FOCALTECH_MOC_USER_ID_LENGTH 64 + +typedef void (*FocaltechCmdMsgCallback) (FpiDeviceFocaltechMoc *self, + GError *error); + +struct _FpiDeviceFocaltechMoc +{ + FpDevice parent; + FpiSsm *task_ssm; + FpiSsm *cmd_ssm; + FpiUsbTransfer *cmd_transfer; + gboolean cmd_cancelable; + gsize cmd_len_in; + int num_frames; + int delete_slot; + guint8 bulk_in_ep; + guint8 bulk_out_ep; +}; diff --git a/libfprint/meson.build b/libfprint/meson.build index c2ebd8c1..da8285e5 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -143,6 +143,8 @@ driver_sources = { [ 'drivers/fpcmoc/fpc.c' ], 'realtek' : [ 'drivers/realtek/realtek.c' ], + 'focaltech_moc' : + [ 'drivers/focaltech_moc/focaltech_moc.c' ], } helper_sources = { diff --git a/meson.build b/meson.build index 9fc10315..aeef6911 100644 --- a/meson.build +++ b/meson.build @@ -132,6 +132,7 @@ default_drivers = [ 'nb1010', 'fpcmoc', 'realtek', + 'focaltech_moc', # SPI 'elanspi', diff --git a/tests/focaltech_moc/custom.pcapng b/tests/focaltech_moc/custom.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..9bcd05c9177d2d98783707ae5447d980f8069bdf GIT binary patch literal 38864 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 J3+d1B^}nh?<5&Oy literal 0 HcmV?d00001 diff --git a/tests/focaltech_moc/custom.py b/tests/focaltech_moc/custom.py new file mode 100755 index 00000000..6a876c60 --- /dev/null +++ b/tests/focaltech_moc/custom.py @@ -0,0 +1,89 @@ +#!/usr/bin/python3 + +import traceback +import sys +import gi + +gi.require_version('FPrint', '2.0') +from gi.repository import FPrint, GLib + +# Exit with error on any exception, included those happening in async callbacks +sys.excepthook = lambda *args: (traceback.print_exception(*args), sys.exit(1)) + +ctx = GLib.main_context_default() + +c = FPrint.Context() +c.enumerate() +devices = c.get_devices() + +d = devices[0] +del devices + +assert d.get_driver() == "focaltech_moc" +assert not d.has_feature(FPrint.DeviceFeature.CAPTURE) +assert d.has_feature(FPrint.DeviceFeature.IDENTIFY) +assert d.has_feature(FPrint.DeviceFeature.VERIFY) +assert not d.has_feature(FPrint.DeviceFeature.DUPLICATES_CHECK) +assert d.has_feature(FPrint.DeviceFeature.STORAGE) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_LIST) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_DELETE) +assert not d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR) + +d.open_sync() + +template = FPrint.Print.new(d) + +def enroll_progress(*args): + #assert d.get_finger_status() == FPrint.FingerStatusFlags.NEEDED + print("finger status: ", d.get_finger_status()) + print('enroll progress: ' + str(args)) + +def identify_done(dev, res): + global identified + identified = True + identify_match, identify_print = dev.identify_finish(res) + print('indentification_done: ', identify_match, identify_print) + assert identify_match.equal(identify_print) + +# List, enroll, list, verify, identify, delete +print("enrolling") +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +p = d.enroll_sync(template, None, enroll_progress, None) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("enroll done") + +print("listing") +stored = d.list_prints_sync() +print("listing done") +assert len(stored) == 1 +assert stored[0].equal(p) +print("verifying") +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +verify_res, verify_print = d.verify_sync(p) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("verify done") +del p +assert verify_res == True + +identified = False +deserialized_prints = [] +for p in stored: + deserialized_prints.append(FPrint.Print.deserialize(p.serialize())) + assert deserialized_prints[-1].equal(p) +del stored + +print('async identifying') +d.identify(deserialized_prints, callback=identify_done) +del deserialized_prints + +while not identified: + ctx.iteration(True) + +print("deleting") +d.delete_print_sync(p) +print("delete done") + +d.close_sync() + +del d +del c diff --git a/tests/focaltech_moc/device b/tests/focaltech_moc/device new file mode 100644 index 00000000..093807d1 --- /dev/null +++ b/tests/focaltech_moc/device @@ -0,0 +1,385 @@ +P: /devices/pci0000:00/0000:00:1c.4/0000:0b:00.0/usb3/3-1/3-1.4 +N: bus/usb/003/006=1201100100000040082879D900020102030109022000010100A0320904000002DCA0B0000705020240000007058102400000 +E: DEVNAME=/dev/bus/usb/003/006 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=2808/d979/200 +E: TYPE=0/0/0 +E: BUSNUM=003 +E: DEVNUM=006 +E: MAJOR=189 +E: MINOR=261 +E: SUBSYSTEM=usb +E: ID_VENDOR=CCore +E: ID_VENDOR_ENC=CCore +E: ID_VENDOR_ID=2808 +E: ID_MODEL=FocalTech_FT9349_ESS +E: ID_MODEL_ENC=FocalTech\x20FT9349\x20ESS +E: ID_MODEL_ID=d979 +E: ID_REVISION=0200 +E: ID_SERIAL=CCore_FocalTech_FT9349_ESS_1234567890ABCDEF +E: ID_SERIAL_SHORT=1234567890ABCDEF +E: ID_BUS=usb +E: ID_USB_INTERFACES=:dca0b0: +E: ID_PATH=pci-0000:0b:00.0-usb-0:1.4 +E: ID_PATH_TAG=pci-0000_0b_00_0-usb-0_1_4 +A: authorized=1 +A: avoid_reset_quirk=0 +A: bConfigurationValue=1 +A: bDeviceClass=00 +A: bDeviceProtocol=00 +A: bDeviceSubClass=00 +A: bMaxPacketSize0=64 +A: bMaxPower=100mA +A: bNumConfigurations=1 +A: bNumInterfaces= 1 +A: bcdDevice=0200 +A: bmAttributes=a0 +A: busnum=3 +A: configuration= +H: descriptors=1201100100000040082879D900020102030109022000010100A0320904000002DCA0B0000705020240000007058102400000 +A: dev=189:261 +A: devnum=6 +A: devpath=1.4 +L: driver=../../../../../../../bus/usb/drivers/usb +A: idProduct=d979 +A: idVendor=2808 +A: ltm_capable=no +A: manufacturer=CCore +A: maxchild=0 +L: port=../3-1:1.0/3-1-port4 +A: power/active_duration=130884 +A: power/async=enabled +A: power/autosuspend=2 +A: power/autosuspend_delay_ms=2000 +A: power/connected_duration=2778952 +A: power/control=auto +A: power/level=auto +A: power/persist=0 +A: power/runtime_active_kids=0 +A: power/runtime_active_time=131747 +A: power/runtime_enabled=enabled +A: power/runtime_status=active +A: power/runtime_suspended_time=2647026 +A: power/runtime_usage=0 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: product=FocalTech FT9349 ESS +A: quirks=0x0 +A: removable=unknown +A: rx_lanes=1 +A: serial=1234567890ABCDEF +A: speed=12 +A: tx_lanes=1 +A: urbnum=1922 +A: version= 1.10 + +P: /devices/pci0000:00/0000:00:1c.4/0000:0b:00.0/usb3/3-1 +N: bus/usb/003/002=1201000209000140E305080636850001000109021900010100E0320904000001090000000705810301000C +E: DEVNAME=/dev/bus/usb/003/002 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=5e3/608/8536 +E: TYPE=9/0/1 +E: BUSNUM=003 +E: DEVNUM=002 +E: MAJOR=189 +E: MINOR=257 +E: SUBSYSTEM=usb +E: ID_VENDOR=05e3 +E: ID_VENDOR_ENC=05e3 +E: ID_VENDOR_ID=05e3 +E: ID_MODEL=USB2.0_Hub +E: ID_MODEL_ENC=USB2.0\x20Hub +E: ID_MODEL_ID=0608 +E: ID_REVISION=8536 +E: ID_SERIAL=05e3_USB2.0_Hub +E: ID_BUS=usb +E: ID_USB_INTERFACES=:090000: +E: ID_VENDOR_FROM_DATABASE=Genesys Logic, Inc. +E: ID_MODEL_FROM_DATABASE=Hub +E: ID_PATH=pci-0000:0b:00.0-usb-0:1 +E: ID_PATH_TAG=pci-0000_0b_00_0-usb-0_1 +E: ID_FOR_SEAT=usb-pci-0000_0b_00_0-usb-0_1 +E: TAGS=:seat: +A: authorized=1 +A: avoid_reset_quirk=0 +A: bConfigurationValue=1 +A: bDeviceClass=09 +A: bDeviceProtocol=01 +A: bDeviceSubClass=00 +A: bMaxPacketSize0=64 +A: bMaxPower=100mA +A: bNumConfigurations=1 +A: bNumInterfaces= 1 +A: bcdDevice=8536 +A: bmAttributes=e0 +A: busnum=3 +A: configuration= +H: descriptors=1201000209000140E305080636850001000109021900010100E0320904000001090000000705810301000C +A: dev=189:257 +A: devnum=2 +A: devpath=1 +L: driver=../../../../../../bus/usb/drivers/usb +A: idProduct=0608 +A: idVendor=05e3 +A: ltm_capable=no +A: maxchild=4 +L: port=../3-0:1.0/usb3-port1 +A: power/active_duration=6193132 +A: power/async=enabled +A: power/autosuspend=0 +A: power/autosuspend_delay_ms=0 +A: power/connected_duration=6193132 +A: power/control=auto +A: power/level=auto +A: power/runtime_active_kids=3 +A: power/runtime_active_time=6192633 +A: power/runtime_enabled=enabled +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/runtime_usage=0 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: product=USB2.0 Hub +A: quirks=0x0 +A: removable=unknown +A: rx_lanes=1 +A: speed=480 +A: tx_lanes=1 +A: urbnum=619 +A: version= 2.00 + +P: /devices/pci0000:00/0000:00:1c.4/0000:0b:00.0/usb3 +N: bus/usb/003/001=12010002090001406B1D020015050302010109021900010100E0000904000001090000000705810304000C +E: DEVNAME=/dev/bus/usb/003/001 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=1d6b/2/515 +E: TYPE=9/0/1 +E: BUSNUM=003 +E: DEVNUM=001 +E: MAJOR=189 +E: MINOR=256 +E: SUBSYSTEM=usb +E: ID_VENDOR=Linux_5.15.0-52-generic_xhci-hcd +E: ID_VENDOR_ENC=Linux\x205.15.0-52-generic\x20xhci-hcd +E: ID_VENDOR_ID=1d6b +E: ID_MODEL=xHCI_Host_Controller +E: ID_MODEL_ENC=xHCI\x20Host\x20Controller +E: ID_MODEL_ID=0002 +E: ID_REVISION=0515 +E: ID_SERIAL=Linux_5.15.0-52-generic_xhci-hcd_xHCI_Host_Controller_0000:0b:00.0 +E: ID_SERIAL_SHORT=0000:0b:00.0 +E: ID_BUS=usb +E: ID_USB_INTERFACES=:090000: +E: ID_VENDOR_FROM_DATABASE=Linux Foundation +E: ID_MODEL_FROM_DATABASE=2.0 root hub +E: ID_PATH=pci-0000:0b:00.0 +E: ID_PATH_TAG=pci-0000_0b_00_0 +E: ID_FOR_SEAT=usb-pci-0000_0b_00_0 +E: TAGS=:seat: +A: authorized=1 +A: authorized_default=1 +A: avoid_reset_quirk=0 +A: bConfigurationValue=1 +A: bDeviceClass=09 +A: bDeviceProtocol=01 +A: bDeviceSubClass=00 +A: bMaxPacketSize0=64 +A: bMaxPower=0mA +A: bNumConfigurations=1 +A: bNumInterfaces= 1 +A: bcdDevice=0515 +A: bmAttributes=e0 +A: busnum=3 +A: configuration= +H: descriptors=12010002090001406B1D020015050302010109021900010100E0000904000001090000000705810304000C +A: dev=189:256 +A: devnum=1 +A: devpath=0 +L: driver=../../../../../bus/usb/drivers/usb +A: idProduct=0002 +A: idVendor=1d6b +A: interface_authorized_default=1 +A: ltm_capable=no +A: manufacturer=Linux 5.15.0-52-generic xhci-hcd +A: maxchild=2 +A: power/active_duration=6193348 +A: power/async=enabled +A: power/autosuspend=0 +A: power/autosuspend_delay_ms=0 +A: power/connected_duration=6193348 +A: power/control=auto +A: power/level=auto +A: power/runtime_active_kids=1 +A: power/runtime_active_time=6193145 +A: power/runtime_enabled=enabled +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/runtime_usage=0 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: product=xHCI Host Controller +A: quirks=0x0 +A: removable=unknown +A: rx_lanes=1 +A: serial=0000:0b:00.0 +A: speed=480 +A: tx_lanes=1 +A: urbnum=36 +A: version= 2.00 + +P: /devices/pci0000:00/0000:00:1c.4/0000:0b:00.0 +E: DRIVER=xhci_hcd +E: PCI_CLASS=C0330 +E: PCI_ID=104C:8241 +E: PCI_SUBSYS_ID=1028:050F +E: PCI_SLOT_NAME=0000:0b:00.0 +E: MODALIAS=pci:v0000104Cd00008241sv00001028sd0000050Fbc0Csc03i30 +E: SUBSYSTEM=pci +E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller +E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller +E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI +E: ID_VENDOR_FROM_DATABASE=Texas Instruments +E: ID_MODEL_FROM_DATABASE=TUSB73x0 SuperSpeed USB 3.0 xHCI Host Controller +A: aer_dev_correctable=RxErr 0\nBadTLP 0\nBadDLLP 0\nRollover 0\nTimeout 0\nNonFatalErr 0\nCorrIntErr 0\nHeaderOF 0\nTOTAL_ERR_COR 0 +A: aer_dev_fatal=Undefined 0\nDLP 0\nSDES 0\nTLP 0\nFCP 0\nCmpltTO 0\nCmpltAbrt 0\nUnxCmplt 0\nRxOF 0\nMalfTLP 0\nECRC 0\nUnsupReq 0\nACSViol 0\nUncorrIntErr 0\nBlockedTLP 0\nAtomicOpBlocked 0\nTLPBlockedErr 0\nPoisonTLPBlocked 0\nTOTAL_ERR_FATAL 0 +A: aer_dev_nonfatal=Undefined 0\nDLP 0\nSDES 0\nTLP 0\nFCP 0\nCmpltTO 0\nCmpltAbrt 0\nUnxCmplt 0\nRxOF 0\nMalfTLP 0\nECRC 0\nUnsupReq 0\nACSViol 0\nUncorrIntErr 0\nBlockedTLP 0\nAtomicOpBlocked 0\nTLPBlockedErr 0\nPoisonTLPBlocked 0\nTOTAL_ERR_NONFATAL 0 +A: ari_enabled=0 +A: broken_parity_status=0 +A: class=0x0c0330 +H: config=4C104182060410000230030C100000000400D0F7000000000400D1F70000000000000000000000000000000028100F050000000040000000000000000B010000014883FE080000000570860000000000000000000000000000000000000000003020000000000000000000000000000010C00200C38F900500201900123C07004200121000000000000000000000000000000000100000000000000000000000020000000000000000000000000000000000000000000F0000000000000000001100078002000000021000000000000028100F05AB0D00001B0000003F000000000040CB00000000000000000000000000000000000000000000000000000000010002150000000000000000302046000020000000200000A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030001000000200000280008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003B00B100FFFFFFFF04000007000F0F1B2001010000000000AA430000800200000000000000000000117E7C031000000830C0000001800000FFFF0F00000000000F00000000000000855023000B50230000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F010000000000000000000000000000000000001B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +A: consistent_dma_mask_bits=64 +A: current_link_speed=5.0 GT/s PCIe +A: current_link_width=1 +A: d3cold_allowed=1 +A: device=0x8241 +A: dma_mask_bits=64 +L: driver=../../../../bus/pci/drivers/xhci_hcd +A: driver_override=(null) +A: enable=1 +A: irq=16 +A: link/clkpm=0 +A: link/l0s_aspm=0 +A: link/l1_aspm=1 +A: local_cpulist=0-3 +A: local_cpus=f +A: max_link_speed=5.0 GT/s PCIe +A: max_link_width=1 +A: modalias=pci:v0000104Cd00008241sv00001028sd0000050Fbc0Csc03i30 +A: msi_bus=1 +A: msi_irqs/25=msix +A: msi_irqs/26=msix +A: msi_irqs/27=msix +A: msi_irqs/28=msix +A: msi_irqs/29=msix +A: numa_node=-1 +A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 11 12 2112 12\nxHCI ring segments 34 34 4096 34\nbuffer-2048 16 32 2048 16\nbuffer-512 0 0 512 0\nbuffer-128 26 32 128 1\nbuffer-32 0 0 32 0 +A: power/async=enabled +A: power/control=on +A: power/runtime_active_kids=1 +A: power/runtime_active_time=6193932 +A: power/runtime_enabled=forbidden +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/runtime_usage=1 +A: power/wakeup=enabled +A: power/wakeup_abort_count=0 +A: power/wakeup_active=0 +A: power/wakeup_active_count=0 +A: power/wakeup_count=0 +A: power/wakeup_expire_count=0 +A: power/wakeup_last_time_ms=0 +A: power/wakeup_max_time_ms=0 +A: power/wakeup_total_time_ms=0 +A: power_state=D0 +A: reset_method=bus +A: resource=0x00000000f7d00000 0x00000000f7d0ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x00000000f7d10000 0x00000000f7d11fff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 +A: revision=0x02 +A: subsystem_device=0x050f +A: subsystem_vendor=0x1028 +A: vendor=0x104c + +P: /devices/pci0000:00/0000:00:1c.4 +E: DRIVER=pcieport +E: PCI_CLASS=60400 +E: PCI_ID=8086:1C18 +E: PCI_SUBSYS_ID=1028:050F +E: PCI_SLOT_NAME=0000:00:1c.4 +E: MODALIAS=pci:v00008086d00001C18sv00001028sd0000050Fbc06sc04i00 +E: SUBSYSTEM=pci +E: ID_PCI_CLASS_FROM_DATABASE=Bridge +E: ID_PCI_SUBCLASS_FROM_DATABASE=PCI bridge +E: ID_PCI_INTERFACE_FROM_DATABASE=Normal decode +E: ID_VENDOR_FROM_DATABASE=Intel Corporation +E: ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 5 +A: ari_enabled=0 +A: broken_parity_status=0 +A: class=0x060400 +H: config=8680181C07001000B5000406100081000000000000000000000B0C00F0000020D0F7D0F7F1FF010000000000000000000000000040000000000000000B011200108042010080000000001000123C1205420012F000B2240000004001000000000000000016000000000000000000000002000000000000000000000000000000059000000000000000000000000000000DA0000028100F050000000000000000010002C8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001020B00000080118100000000003F00000000000001000000000000000000000000000000870F050800000000000000000000000000000000110006000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B363A7400001414311742005B6009002020000A521498095104690616000028BCB5BC4A00000000744C8500DC08DC0061091100D30F07005000E2005B00170001009400370494000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +A: consistent_dma_mask_bits=32 +A: current_link_speed=5.0 GT/s PCIe +A: current_link_width=1 +A: d3cold_allowed=1 +A: device=0x1c18 +A: dma_mask_bits=32 +L: driver=../../../bus/pci/drivers/pcieport +A: driver_override=(null) +A: enable=2 +A: irq=16 +A: local_cpulist=0-3 +A: local_cpus=f +A: max_link_speed=5.0 GT/s PCIe +A: max_link_width=1 +A: modalias=pci:v00008086d00001C18sv00001028sd0000050Fbc06sc04i00 +A: msi_bus=1 +A: numa_node=-1 +A: power/async=enabled +A: power/control=on +A: power/runtime_active_kids=1 +A: power/runtime_active_time=6193944 +A: power/runtime_enabled=forbidden +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/runtime_usage=2 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: power_state=D0 +A: reset_method=pm +A: resource=0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x00000000f7d00000 0x00000000f7dfffff 0x0000000000000200\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 +A: revision=0xb5 +A: secondary_bus_number=11 +A: subordinate_bus_number=12 +A: subsystem_device=0x050f +A: subsystem_vendor=0x1028 +A: vendor=0x8086 + diff --git a/tests/meson.build b/tests/meson.build index f4f15979..48c83037 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -53,6 +53,7 @@ drivers_tests = [ 'egis0570', 'fpcmoc', 'realtek', + 'focaltech_moc', ] if get_option('introspection') From b8933d8f81661b9c1b47bce31ffb7c41b2ae6659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 14:34:54 +0100 Subject: [PATCH 21/64] fp-print: Do not check compile-time macros at runtime --- libfprint/fp-print.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index 7bcb9d8d..92323160 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -721,13 +721,12 @@ fp_print_serialize (FpPrint *print, result = g_variant_builder_end (&builder); - if (G_BYTE_ORDER == G_BIG_ENDIAN) - { - GVariant *tmp; - tmp = g_variant_byteswap (result); - g_variant_unref (result); - result = tmp; - } +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + GVariant *tmp; + tmp = g_variant_byteswap (result); + g_variant_unref (result); + result = tmp; +#endif len = g_variant_get_size (result); /* Add 3 bytes of header */ @@ -800,10 +799,11 @@ fp_print_deserialize (const guchar *data, if (!raw_value) goto invalid_format; - if (G_BYTE_ORDER == G_BIG_ENDIAN) - value = g_variant_byteswap (raw_value); - else - value = g_variant_get_normal_form (raw_value); +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + value = g_variant_byteswap (raw_value); +#else + value = g_variant_get_normal_form (raw_value); +#endif g_variant_get (value, "(i&s&sbymsmsi@a{sv}v)", From 85ec9ec5b222559f13ca34b850226294bb209a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 14:42:18 +0100 Subject: [PATCH 22/64] ci: Allow flatpak failure It's not a critical thing, so if it fails (as it does currently) we should not block on that. --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8bea6726..a5771e9e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -193,8 +193,10 @@ flatpak: - if: '$CI_PIPELINE_SOURCE == "schedule"' when: never - if: '$CI_COMMIT_BRANCH == "master"' + allow_failure: true when: always - if: '$CI_COMMIT_TAG' + allow_failure: true when: always # For any other (commit), allow manual run. # This excludes MRs which would create a duplicate pipeline From c64fa9c81b4d0b0ceda79c3ff27c59c47ce669ba Mon Sep 17 00:00:00 2001 From: Puspendu Banerjee Date: Mon, 12 Feb 2024 19:50:50 -0600 Subject: [PATCH 23/64] synaptics: Add new PID 0x0124 --- 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 44dbbcd3..366f286c 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -224,6 +224,7 @@ usb:v06CBp0100* usb:v06CBp00F0* usb:v06CBp0103* usb:v06CBp0123* +usb:v06CBp0124* usb:v06CBp0126* usb:v06CBp0129* usb:v06CBp0168* diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 55e4d44c..138e734d 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -40,6 +40,7 @@ static const FpIdEntry id_table[] = { { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00F0, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0103, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0123, }, + { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0124, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0126, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0129, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0168, }, From 92c5fc464373d7b70600a22301b30701e76764a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 15:17:04 +0100 Subject: [PATCH 24/64] cleanup: Use allow/deny lists instead of color based ones There was nothing racist on those names here (what? Do human races even exist?!), but let's avoid any confusion. --- NEWS | 4 ++-- libfprint/fp-context.c | 20 ++++++++++---------- libfprint/fprint-list-udev-hwdb.c | 20 ++++++++++---------- tests/create-driver-test.py.in | 2 +- tests/egis0570/device | 2 +- tests/meson.build | 4 ++-- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/NEWS b/NEWS index 1b87665e..103740f6 100644 --- a/NEWS +++ b/NEWS @@ -357,7 +357,7 @@ tests of the drivers using umockdev. - Mark fp_dscv_print functions as deprecated * Udev rules: - - Add some unsupported devices to the whitelist + - Add some unsupported devices to the allowlist 2017-05-14: v0.7.0 release * Drivers: @@ -407,7 +407,7 @@ tests of the drivers using umockdev. - Fix possible race condition, and cancellation in uru4000 driver * Udev rules: - - Add Microsoft keyboard to the suspend blacklist + - Add Microsoft keyboard to the suspend denylist * Plenty of build fixes diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index 34fcdda8..f6c386df 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -67,29 +67,29 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; static const char * -get_drivers_whitelist_env (void) +get_drivers_allowlist_env (void) { - return g_getenv ("FP_DRIVERS_WHITELIST"); + return g_getenv ("FP_DRIVERS_ALLOWLIST"); } static gboolean is_driver_allowed (const gchar *driver) { - g_auto(GStrv) whitelisted_drivers = NULL; - const char *fp_drivers_whitelist_env; + g_auto(GStrv) allowlisted_drivers = NULL; + const char *fp_drivers_allowlist_env; int i; g_return_val_if_fail (driver, TRUE); - fp_drivers_whitelist_env = get_drivers_whitelist_env (); + fp_drivers_allowlist_env = get_drivers_allowlist_env (); - if (!fp_drivers_whitelist_env) + if (!fp_drivers_allowlist_env) return TRUE; - whitelisted_drivers = g_strsplit (fp_drivers_whitelist_env, ":", -1); + allowlisted_drivers = g_strsplit (fp_drivers_allowlist_env, ":", -1); - for (i = 0; whitelisted_drivers[i]; ++i) - if (g_strcmp0 (driver, whitelisted_drivers[i]) == 0) + for (i = 0; allowlisted_drivers[i]; ++i) + if (g_strcmp0 (driver, allowlisted_drivers[i]) == 0) return TRUE; return FALSE; @@ -364,7 +364,7 @@ fp_context_init (FpContext *self) priv->drivers = fpi_get_driver_types (); - if (get_drivers_whitelist_env ()) + if (get_drivers_allowlist_env ()) { for (i = 0; i < priv->drivers->len;) { diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c index 7c41050b..3eca8040 100644 --- a/libfprint/fprint-list-udev-hwdb.c +++ b/libfprint/fprint-list-udev-hwdb.c @@ -24,7 +24,7 @@ #include "fpi-context.h" #include "fpi-device.h" -static const FpIdEntry whitelist_id_table[] = { +static const FpIdEntry allowlist_id_table[] = { /* Currently known and unsupported devices. * You can generate this list from the wiki page using e.g.: * gio cat https://gitlab.freedesktop.org/libfprint/wiki/-/wikis/Unsupported-Devices.md | sed -n 's!|.*\([0-9a-fA-F]\{4\}\):\([0-9a-fA-F]\{4\}\).*|.*! { .vid = 0x\1, .pid = 0x\2 },!p' @@ -138,18 +138,18 @@ static const FpIdEntry whitelist_id_table[] = { { .vid = 0 }, }; -static const FpIdEntry blacklist_id_table[] = { +static const FpIdEntry denylist_id_table[] = { { .vid = 0x0483, .pid = 0x2016 }, /* https://bugs.freedesktop.org/show_bug.cgi?id=66659 */ { .vid = 0x045e, .pid = 0x00bb }, { .vid = 0 }, }; -static const FpDeviceClass whitelist = { +static const FpDeviceClass allowlist = { .type = FP_DEVICE_TYPE_USB, - .id_table = whitelist_id_table, - .id = "whitelist", - .full_name = "Hardcoded whitelist" + .id_table = allowlist_id_table, + .id = "allowlist", + .full_name = "Hardcoded allowlist" }; GHashTable *printed = NULL; @@ -168,7 +168,7 @@ print_driver (const FpDeviceClass *cls) const FpIdEntry *bl_entry; char *key; - for (bl_entry = blacklist_id_table; bl_entry->vid != 0; bl_entry++) + for (bl_entry = denylist_id_table; bl_entry->vid != 0; bl_entry++) if (entry->vid == bl_entry->vid && entry->pid == bl_entry->pid) break; @@ -179,7 +179,7 @@ print_driver (const FpDeviceClass *cls) if (g_hash_table_lookup (printed, key) != NULL) { - if (cls == &whitelist) + if (cls == &allowlist) g_warning ("%s implemented by driver %s", key, (const char *) g_hash_table_lookup (printed, key)); g_free (key); @@ -190,7 +190,7 @@ print_driver (const FpDeviceClass *cls) if (num_printed == 0) { - if (cls != &whitelist) + if (cls != &allowlist) g_print ("\n# Supported by libfprint driver %s\n", cls->id); else g_print ("\n# Known unsupported devices\n"); @@ -244,7 +244,7 @@ main (int argc, char **argv) print_driver (cls); } - print_driver (&whitelist); + print_driver (&allowlist); g_hash_table_destroy (printed); diff --git a/tests/create-driver-test.py.in b/tests/create-driver-test.py.in index 8173271f..64d96db4 100755 --- a/tests/create-driver-test.py.in +++ b/tests/create-driver-test.py.in @@ -44,7 +44,7 @@ if len(sys.argv) > 3: sys.exit(1) driver_name = sys.argv[1] -os.environ['FP_DRIVERS_WHITELIST'] = driver_name +os.environ['FP_DRIVERS_ALLOWLIST'] = driver_name test_variant = None if len(sys.argv) == 3: diff --git a/tests/egis0570/device b/tests/egis0570/device index 5247b785..3001bd98 100644 --- a/tests/egis0570/device +++ b/tests/egis0570/device @@ -24,7 +24,7 @@ E: ID_USB_INTERFACES=:ff0000: E: ID_VENDOR_FROM_DATABASE=LighTuning Technology Inc. E: ID_PATH=pci-0000:00:14.0-usb-0:9 E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_9 -E: LIBFPRINT_DRIVER=Hardcoded whitelist +E: LIBFPRINT_DRIVER=Hardcoded allowlist A: authorized=1\n A: avoid_reset_quirk=0\n A: bConfigurationValue=1\n diff --git a/tests/meson.build b/tests/meson.build index 48c83037..199500d2 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -15,7 +15,7 @@ envs.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libfprint') envs.set('FP_DEVICE_EMULATION', '1') # Set a colon-separated list of native drivers we enable in tests -envs.set('FP_DRIVERS_WHITELIST', ':'.join([ +envs.set('FP_DRIVERS_ALLOWLIST', ':'.join([ 'virtual_image', 'virtual_device', 'virtual_device_storage', @@ -159,7 +159,7 @@ if get_option('introspection') foreach driver_test: drivers_tests driver_name = driver_test.split('-')[0] driver_envs = envs - driver_envs.set('FP_DRIVERS_WHITELIST', driver_name) + driver_envs.set('FP_DRIVERS_ALLOWLIST', driver_name) if (driver_name in supported_drivers and gusb_dep.version().version_compare('>= 0.3.0')) From f3ab1f996f0647ae516ad24fbb6caf8df6d7cd5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 15:23:43 +0100 Subject: [PATCH 25/64] fp-context: use g_strv_contains instead of manual labor --- libfprint/fp-context.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index f6c386df..70d4062a 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -77,7 +77,6 @@ is_driver_allowed (const gchar *driver) { g_auto(GStrv) allowlisted_drivers = NULL; const char *fp_drivers_allowlist_env; - int i; g_return_val_if_fail (driver, TRUE); @@ -87,12 +86,7 @@ is_driver_allowed (const gchar *driver) return TRUE; allowlisted_drivers = g_strsplit (fp_drivers_allowlist_env, ":", -1); - - for (i = 0; allowlisted_drivers[i]; ++i) - if (g_strcmp0 (driver, allowlisted_drivers[i]) == 0) - return TRUE; - - return FALSE; + return g_strv_contains ((const gchar * const *) allowlisted_drivers, driver); } typedef struct From 2fa0975dec75960d5dae6ae3391475649b85a93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 15:17:57 +0100 Subject: [PATCH 26/64] cleanup: Address some newer uncrustify syntax cleanups --- libfprint/drivers/realtek/realtek.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libfprint/drivers/realtek/realtek.c b/libfprint/drivers/realtek/realtek.c index 87b8c805..4d734bd9 100644 --- a/libfprint/drivers/realtek/realtek.c +++ b/libfprint/drivers/realtek/realtek.c @@ -104,6 +104,7 @@ fp_finish_capture_cb (FpiDeviceRealtek *self, } gint capture_status = buffer_in[0]; + if (capture_status == 0) { fpi_device_report_finger_status_changes (FP_DEVICE (self), @@ -237,6 +238,7 @@ fp_identify_feature_cb (FpiDeviceRealtek *self, } gint in_status = buffer_in[0]; + if (in_status == FP_RTK_CMD_ERR) { fpi_ssm_mark_failed (self->task_ssm, @@ -424,6 +426,7 @@ fp_check_duplicate_cb (FpiDeviceRealtek *self, } gint in_status = buffer_in[0]; + if (in_status == FP_RTK_CMD_ERR) { fpi_ssm_mark_failed (self->task_ssm, From ee509c7ee67c512044f3f4c2d68fc7cf1316c200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 14:53:54 +0100 Subject: [PATCH 27/64] libfprint/fprint-list-udev-hwdb: Update unsupported list from wiki --- data/autosuspend.hwdb | 2 ++ libfprint/fprint-list-udev-hwdb.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 366f286c..215a96b3 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -317,6 +317,7 @@ usb:v04F3p3032* usb:v04F3p3057* usb:v04F3p3104* usb:v04F3p310D* +usb:v04F3p3128* usb:v06CBp0081* usb:v06CBp0088* usb:v06CBp008A* @@ -350,6 +351,7 @@ usb:v0BDAp5812* usb:v10A5p0007* usb:v10A5p9200* usb:v10A5p9800* +usb:v10A5pE340* usb:v1188p9545* usb:v138Ap0007* usb:v138Ap003A* diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c index 3eca8040..28530a06 100644 --- a/libfprint/fprint-list-udev-hwdb.c +++ b/libfprint/fprint-list-udev-hwdb.c @@ -46,6 +46,7 @@ static const FpIdEntry allowlist_id_table[] = { { .vid = 0x04f3, .pid = 0x3057 }, { .vid = 0x04f3, .pid = 0x3104 }, { .vid = 0x04f3, .pid = 0x310d }, + { .vid = 0x04f3, .pid = 0x3128 }, { .vid = 0x06cb, .pid = 0x0081 }, { .vid = 0x06cb, .pid = 0x0088 }, { .vid = 0x06cb, .pid = 0x008a }, @@ -79,6 +80,7 @@ static const FpIdEntry allowlist_id_table[] = { { .vid = 0x10a5, .pid = 0x0007 }, { .vid = 0x10a5, .pid = 0x9200 }, { .vid = 0x10a5, .pid = 0x9800 }, + { .vid = 0x10a5, .pid = 0xe340 }, { .vid = 0x1188, .pid = 0x9545 }, { .vid = 0x138a, .pid = 0x0007 }, { .vid = 0x138a, .pid = 0x003a }, From d878148b5e641bc479491da6d15fdde2c711c855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 15:36:09 +0100 Subject: [PATCH 28/64] ci: Expose scan build artifacts on failures That's the only case we care about --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a5771e9e..71f55825 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -152,6 +152,7 @@ test_scan_build: - SCANBUILD=$CI_PROJECT_DIR/.gitlab-ci/scan-build ninja -C _build scan-build artifacts: + when: on_failure paths: - _build/meson-logs expire_in: 1 week From 61f9346aafd3f9a2904d6891e5f554b98694fb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 15:48:55 +0100 Subject: [PATCH 29/64] realtek: Do not leak an error Found by scan-build. --- libfprint/drivers/realtek/realtek.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libfprint/drivers/realtek/realtek.c b/libfprint/drivers/realtek/realtek.c index 4d734bd9..84bec5c5 100644 --- a/libfprint/drivers/realtek/realtek.c +++ b/libfprint/drivers/realtek/realtek.c @@ -249,9 +249,8 @@ fp_identify_feature_cb (FpiDeviceRealtek *self, if (in_status >= FP_RTK_TOO_HIGH && in_status <= FP_RTK_MERGE_FAILURE) { - GError *retry_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); - retry_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); - fpi_ssm_mark_failed (self->task_ssm, retry_error); + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); return; } From e05fbaa8ab124664e6df895e4dfbc084f3c0b223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 15:49:36 +0100 Subject: [PATCH 30/64] realtek: Do not initialize variables in switch cases Handles scan-build warning --- libfprint/drivers/realtek/realtek.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/libfprint/drivers/realtek/realtek.c b/libfprint/drivers/realtek/realtek.c index 84bec5c5..45322908 100644 --- a/libfprint/drivers/realtek/realtek.c +++ b/libfprint/drivers/realtek/realtek.c @@ -864,13 +864,12 @@ fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device) static void fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) { + g_autofree gchar *user_id = NULL; + g_autofree guint8 *payload = NULL; FpiDeviceRealtek *self = FPI_DEVICE_REALTEK (device); FpPrint *print = NULL; guint8 *cmd_buf = NULL; - - g_autofree gchar *user_id = NULL; - g_autofree guint8 *payload = NULL; - + guint8 *trans_id = NULL; GVariant *uid = NULL; GVariant *data = NULL; gsize user_id_len; @@ -920,8 +919,6 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device) break; case FP_RTK_ENROLL_COMMIT: - guint8 *trans_id = NULL; - fpi_device_get_enroll_data (device, &print); user_id = fpi_print_generate_user_id (print); user_id_len = strlen (user_id); From 54ff730f0ca7283f1f6886703232f1ff77a8f635 Mon Sep 17 00:00:00 2001 From: Adam Jones Date: Thu, 4 Jan 2024 22:54:14 +0000 Subject: [PATCH 31/64] Fix typo of libfprint in supported devices list This also updates the title of the website page at https://fprint.freedesktop.org/supported-devices.html --- libfprint/fprint-list-supported-devices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/fprint-list-supported-devices.c b/libfprint/fprint-list-supported-devices.c index ace5de09..1b02c0b2 100644 --- a/libfprint/fprint-list-supported-devices.c +++ b/libfprint/fprint-list-supported-devices.c @@ -110,7 +110,7 @@ main (int argc, char **argv) printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - g_print ("%% lifprint — Supported Devices\n"); + g_print ("%% libfprint — Supported Devices\n"); g_print ("%% Bastien Nocera, Daniel Drake\n"); g_print ("%% 2018\n"); g_print ("\n"); From 0b9a64331f01e16a13d6e47effeceb842014eec7 Mon Sep 17 00:00:00 2001 From: Joshua Grisham <18266314+joshuagrisham@users.noreply.github.com> Date: Sun, 8 Oct 2023 14:52:55 +0200 Subject: [PATCH 32/64] Initial commit of egismoc driver --- libfprint/drivers/egismoc/egismoc.c | 1443 +++++++++++++++++++++++++++ libfprint/drivers/egismoc/egismoc.h | 231 +++++ libfprint/meson.build | 2 + meson.build | 1 + tests/egismoc/custom.pcapng | Bin 0 -> 73752 bytes tests/egismoc/custom.py | 156 +++ tests/egismoc/device | 262 +++++ tests/meson.build | 1 + 8 files changed, 2096 insertions(+) create mode 100644 libfprint/drivers/egismoc/egismoc.c create mode 100644 libfprint/drivers/egismoc/egismoc.h create mode 100644 tests/egismoc/custom.pcapng create mode 100755 tests/egismoc/custom.py create mode 100644 tests/egismoc/device diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c new file mode 100644 index 00000000..0b249c44 --- /dev/null +++ b/libfprint/drivers/egismoc/egismoc.c @@ -0,0 +1,1443 @@ +/* + * Driver for Egis Technology (LighTuning) Match-On-Chip sensors + * Originally authored 2023 by Joshua Grisham + * + * Portions of code and logic inspired from the elanmoc libfprint driver + * which is copyright (C) 2021 Elan Microelectronics Inc (see elanmoc.c) + * + * Based on original reverse-engineering work by Joshua Grisham. The protocol has + * been reverse-engineered from captures of the official Windows driver, and by + * testing commands on the sensor with a multiplatform Python prototype driver: + * https://github.com/joshuagrisham/galaxy-book2-pro-linux/tree/main/fingerprint/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FP_COMPONENT "egismoc" + +#include +#include +#include + +#include "drivers_api.h" + +#include "egismoc.h" + +G_DEFINE_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FP_TYPE_DEVICE); + +static const FpIdEntry egismoc_id_table[] = { + { .vid = 0x1c7a, .pid = 0x0582 }, + { .vid = 0, .pid = 0 } +}; + +typedef void (*SynCmdMsgCallback) (FpDevice *device, + guchar *buffer_in, + gsize length_in, + GError *error); + +typedef struct egismoc_command_data +{ + SynCmdMsgCallback callback; +} CommandData; + +typedef struct egismoc_enroll_print +{ + FpPrint *print; + int stage; +} EnrollPrint; + +static void +egismoc_finger_on_sensor_cb (FpiUsbTransfer *transfer, + FpDevice *device, + gpointer userdata, + GError *error) +{ + fp_dbg ("Finger on sensor callback"); + fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT); + + g_return_if_fail (transfer->ssm); + if (error) + fpi_ssm_mark_failed (transfer->ssm, error); + else + fpi_ssm_next_state (transfer->ssm); +} + +static void +egismoc_wait_finger_on_sensor (FpiSsm *ssm, + FpDevice *device) +{ + fp_dbg ("Wait for finger on sensor"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + g_autoptr(FpiUsbTransfer) transfer = fpi_usb_transfer_new (device); + + fpi_usb_transfer_fill_interrupt (transfer, EGISMOC_EP_CMD_INTERRUPT_IN, EGISMOC_USB_INTERRUPT_IN_RECV_LENGTH); + transfer->ssm = ssm; + transfer->short_is_error = FALSE; /* Interrupt on this device always returns 1 byte short; this is expected */ + + fpi_device_report_finger_status (device, FP_FINGER_STATUS_NEEDED); + + fpi_usb_transfer_submit (g_steal_pointer (&transfer), + EGISMOC_USB_INTERRUPT_TIMEOUT, + self->interrupt_cancellable, + egismoc_finger_on_sensor_cb, + NULL); +} + +static gboolean +egismoc_validate_response_prefix (const guchar *buffer_in, + const gsize buffer_in_len, + const guchar *valid_prefix, + const gsize valid_prefix_len) +{ + const gboolean result = memcmp (buffer_in + (egismoc_read_prefix_len + EGISMOC_CHECK_BYTES_LENGTH), + valid_prefix, + valid_prefix_len) == 0; + + fp_dbg ("Response prefix valid: %s", result ? "yes" : "NO"); + return result; +} + +static gboolean +egismoc_validate_response_suffix (const guchar *buffer_in, + const gsize buffer_in_len, + const guchar *valid_suffix, + const gsize valid_suffix_len) +{ + const gboolean result = memcmp (buffer_in + (buffer_in_len - valid_suffix_len), + valid_suffix, + valid_suffix_len) == 0; + + fp_dbg ("Response suffix valid: %s", result ? "yes" : "NO"); + return result; +} + +static void +egismoc_task_ssm_done (FpiSsm *ssm, + FpDevice *device, + GError *error) +{ + fp_dbg ("Task SSM done"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + /* task_ssm already freed by completion of SSM */ + self->task_ssm = NULL; + + if (self->enrolled_ids) + g_ptr_array_free (self->enrolled_ids, TRUE); + self->enrolled_ids = NULL; + self->enrolled_num = -1; + + if (error) + fpi_device_action_error (device, error); +} + +static void +egismoc_task_ssm_next_state_cb (FpDevice *device, + guchar *buffer_in, + gsize length_in, + GError *error) +{ + fp_dbg ("Task SSM next state callback"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + if (error) + fpi_ssm_mark_failed (self->task_ssm, error); + else + fpi_ssm_next_state (self->task_ssm); +} + +static void +egismoc_cmd_receive_cb (FpiUsbTransfer *transfer, + FpDevice *device, + gpointer userdata, + GError *error) +{ + fp_dbg ("Command receive callback"); + CommandData *data = userdata; + + if (error) + { + fpi_ssm_mark_failed (transfer->ssm, error); + return; + } + if (data == NULL || transfer->actual_length < egismoc_read_prefix_len) + { + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + return; + } + + if (data->callback) + data->callback (device, transfer->buffer, transfer->actual_length, NULL); + + fpi_ssm_mark_completed (transfer->ssm); +} + +static void +egismoc_cmd_run_state (FpiSsm *ssm, + FpDevice *device) +{ + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + g_autoptr(FpiUsbTransfer) transfer = NULL; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case CMD_SEND: + if (self->cmd_transfer) + { + self->cmd_transfer->ssm = ssm; + fpi_usb_transfer_submit (g_steal_pointer (&self->cmd_transfer), + EGISMOC_USB_SEND_TIMEOUT, + NULL, + fpi_ssm_usb_transfer_cb, + NULL); + } + else + { + fpi_ssm_next_state (ssm); + } + break; + + case CMD_GET: + transfer = fpi_usb_transfer_new (device); + transfer->ssm = ssm; + fpi_usb_transfer_fill_bulk (transfer, EGISMOC_EP_CMD_IN, EGISMOC_USB_IN_RECV_LENGTH); + fpi_usb_transfer_submit (g_steal_pointer (&transfer), + EGISMOC_USB_RECV_TIMEOUT, + NULL, + egismoc_cmd_receive_cb, + fpi_ssm_get_data (ssm)); + break; + } +} + +static void +egismoc_cmd_ssm_done (FpiSsm *ssm, + FpDevice *device, + GError *error) +{ + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + CommandData *data = fpi_ssm_get_data (ssm); + + self->cmd_ssm = NULL; + self->cmd_transfer = NULL; + + if (error) + { + if (data->callback) + data->callback (device, NULL, 0, error); + else + g_error_free (error); + } +} + +typedef union egismoc_check_bytes +{ + unsigned short check_short; + guchar check_bytes[EGISMOC_CHECK_BYTES_LENGTH]; +} EgisMocCheckBytes; + +/* + Derive the 2 "check bytes" for write payloads + 32-bit big-endian sum of all 16-bit words (including check bytes) MOD 0xFFFF should be 0, otherwise + the device will reject the payload + */ +static EgisMocCheckBytes +egismoc_get_check_bytes (const guchar *value, + const gsize value_length) +{ + fp_dbg ("Get check bytes"); + EgisMocCheckBytes check_bytes; + unsigned short value_bigendian_shorts[(int) ((value_length + 1) / 2)]; + int s = 0; + + for (int i = 0; i < value_length; i = i + 2, s++) + { + if (i + 1 < value_length) + value_bigendian_shorts[s] = (((short) value[i + 1]) << 8) | (0x00ff & value[i]); + else + value_bigendian_shorts[s] = (((short) 0x00) << 8) | (0x00ff & value[i]); + } + unsigned long sum_shorts = 0; + + for (int i = 0; i < s; i++) + sum_shorts += value_bigendian_shorts[i]; + + /* + derive the "first possible occurence" of check bytes as: + `0xFFFF - (sum_of_32bit_words % 0xFFFF) + */ + check_bytes.check_short = 0xffff - (sum_shorts % 0xffff); + return check_bytes; +} + +static void +egismoc_exec_cmd (FpDevice *device, + guchar *cmd, + const gsize cmd_length, + GDestroyNotify cmd_destroy, + SynCmdMsgCallback callback) +{ + fp_dbg ("Execute command and get response"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + EgisMocCheckBytes check_bytes; + g_autofree guchar *buffer_out = NULL; + gsize buffer_out_length = 0; + + g_autoptr(FpiUsbTransfer) transfer = NULL; + CommandData *data = g_new0 (CommandData, 1); + + self->cmd_ssm = fpi_ssm_new (device, + egismoc_cmd_run_state, + CMD_STATES); + + transfer = fpi_usb_transfer_new (device); + transfer->short_is_error = TRUE; + + /* + buffer_out should be a fully composed command (with prefix, check bytes, etc) which looks like this + E G I S 00 00 00 01 {cb1} {cb2} {payload} + where cb1 and cb2 are some check bytes generated by the egismoc_get_check_bytes method + and payload is what is passed via the cmd parameter + */ + buffer_out_length = egismoc_write_prefix_len + + EGISMOC_CHECK_BYTES_LENGTH + + cmd_length; + buffer_out = g_malloc0 (buffer_out_length); + + /* Prefix */ + memcpy (buffer_out, egismoc_write_prefix, egismoc_write_prefix_len); + + /* Check Bytes - leave them as 00 for now then later generate and copy over the real ones */ + + /* Command Payload */ + memcpy (buffer_out + egismoc_write_prefix_len + EGISMOC_CHECK_BYTES_LENGTH, cmd, cmd_length); + + /* destroy cmd if requested */ + if (cmd_destroy) + cmd_destroy (cmd); + + /* Now fetch and set the "real" check bytes based on the currently assembled payload */ + check_bytes = egismoc_get_check_bytes (buffer_out, buffer_out_length); + memcpy (buffer_out + egismoc_write_prefix_len, check_bytes.check_bytes, EGISMOC_CHECK_BYTES_LENGTH); + + fpi_usb_transfer_fill_bulk_full (transfer, + EGISMOC_EP_CMD_OUT, + g_steal_pointer (&buffer_out), + buffer_out_length, + g_free); + transfer->ssm = self->cmd_ssm; + self->cmd_transfer = g_steal_pointer (&transfer); + data->callback = callback; + + fpi_ssm_set_data (self->cmd_ssm, data, g_free); + fpi_ssm_start (self->cmd_ssm, egismoc_cmd_ssm_done); +} + +static void +egismoc_set_print_data (FpPrint *print, + const guchar *device_print_id) +{ + g_autofree gchar *user_id = g_malloc (EGISMOC_FINGERPRINT_DATA_SIZE + 1); + GVariant *print_id_var = NULL; + GVariant *fpi_data = NULL; + + memcpy (user_id, device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); + memset (user_id + EGISMOC_FINGERPRINT_DATA_SIZE, '\0', sizeof (gchar)); + + fpi_print_fill_from_user_id (print, user_id); + fpi_print_set_type (print, FPI_PRINT_RAW); + fpi_print_set_device_stored (print, TRUE); + + g_object_set (print, "description", user_id, NULL); + + print_id_var = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + device_print_id, + EGISMOC_FINGERPRINT_DATA_SIZE, + sizeof (guchar)); + fpi_data = g_variant_new ("(@ay)", print_id_var); + g_object_set (print, "fpi-data", fpi_data, NULL); +} + +static GPtrArray * +egismoc_get_enrolled_prints (FpDevice *device) +{ + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + g_autoptr(GPtrArray) result = g_ptr_array_new_with_free_func (g_object_unref); + FpPrint *print = NULL; + + for (int i = 0; i < self->enrolled_num; i++) + { + print = fp_print_new (device); + egismoc_set_print_data (print, g_ptr_array_index (self->enrolled_ids, i)); + g_ptr_array_add (result, g_object_ref_sink (print)); + } + + return g_steal_pointer (&result); +} + +static void +egismoc_list_fill_enrolled_ids_cb (FpDevice *device, + guchar *buffer_in, + gsize length_in, + GError *error) +{ + fp_dbg ("List callback"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + if (self->enrolled_ids) + g_ptr_array_free (self->enrolled_ids, TRUE); + self->enrolled_ids = g_ptr_array_new_with_free_func (g_free); + self->enrolled_num = 0; + + /* + Each fingerprint ID will be returned in this response as a 32 byte array + The other stuff in the payload is 16 bytes long, so if there is at least 1 print + then the length should be at least 16+32=48 bytes long + */ + for (int pos = EGISMOC_LIST_RESPONSE_PREFIX_SIZE; + pos < length_in - EGISMOC_LIST_RESPONSE_SUFFIX_SIZE; + pos += EGISMOC_FINGERPRINT_DATA_SIZE, self->enrolled_num++) + { + g_autofree guchar *print_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE); + memcpy (print_id, buffer_in + pos, EGISMOC_FINGERPRINT_DATA_SIZE); + fp_dbg ("Device fingerprint %0d: %.*s%c", self->enrolled_num, EGISMOC_FINGERPRINT_DATA_SIZE, print_id, '\0'); + g_ptr_array_add (self->enrolled_ids, g_steal_pointer (&print_id)); + } + + fp_info ("Number of currently enrolled fingerprints on the device is %d", self->enrolled_num); + + if (self->task_ssm) + fpi_ssm_next_state (self->task_ssm); +} + +static void +egismoc_list_run_state (FpiSsm *ssm, + FpDevice *device) +{ + g_autoptr(GPtrArray) enrolled_prints = NULL; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case LIST_GET_ENROLLED_IDS: + egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, egismoc_list_fill_enrolled_ids_cb); + break; + + case LIST_RETURN_ENROLLED_PRINTS: + enrolled_prints = egismoc_get_enrolled_prints (device); + fpi_device_list_complete (device, g_steal_pointer (&enrolled_prints), NULL); + fpi_ssm_next_state (ssm); + break; + } +} + +static void +egismoc_list (FpDevice *device) +{ + fp_dbg ("List"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + self->task_ssm = fpi_ssm_new (device, + egismoc_list_run_state, + LIST_STATES); + fpi_ssm_start (self->task_ssm, egismoc_task_ssm_done); +} + +static guchar * +egismoc_get_delete_cmd (FpDevice *device, + FpPrint *delete_print, + gsize *length_out) +{ + fp_dbg ("Get delete command"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + g_autofree const gchar *print_description = NULL; + + g_autoptr(GVariant) print_data = NULL; + g_autoptr(GVariant) print_data_id_var = NULL; + const guchar *print_data_id = NULL; + gsize print_data_id_len = 0; + g_autofree guchar *enrolled_print_id = NULL; + guchar *result = NULL; + gsize pos = 0; + + /* + The final command body should contain: + 1) hard-coded 00 00 + 2) 2-byte size indiciator, 20*Number deleted identifiers plus 7 in form of: num_to_delete * 0x20 + 0x07 + Since max prints can be higher than 7 then this goes up to 2 bytes (e9 + 9 = 109) + 3) Hard-coded prefix (cmd_delete_prefix) + 4) 2-byte size indiciator, 20*Number of enrolled identifiers without plus 7 (num_to_delete * 0x20) + 5) All of the currently registered prints to delete in their 32-byte device identifiers (enrolled_list) + */ + + const int num_to_delete = (!delete_print) ? self->enrolled_num : 1; + const gsize body_length = sizeof (guchar) * EGISMOC_FINGERPRINT_DATA_SIZE * num_to_delete; + /* total_length is the 6 various bytes plus prefix and body payload */ + const gsize total_length = (sizeof (guchar) * 6) + cmd_delete_prefix_len + body_length; + + /* pre-fill entire payload with 00s */ + result = g_malloc0 (total_length); + + /* start with 00 00 (just move starting offset up by 2) */ + pos = 2; + + /* Size Counter bytes */ + /* "easiest" way to handle 2-bytes size for counter is to hard-code logic for when we go to the 2nd byte */ + /* note this will not work in case any model ever supports more than 14 prints (assumed max is 10) */ + if (num_to_delete > 7) + { + memset (result + pos, 0x01, sizeof (guchar)); + pos += sizeof (guchar); + memset (result + pos, ((num_to_delete - 8) * 0x20) + 0x07, sizeof (guchar)); + pos += sizeof (guchar); + } + else + { + /* first byte is 0x00, just skip it */ + pos += sizeof (guchar); + memset (result + pos, (num_to_delete * 0x20) + 0x07, sizeof (guchar)); + pos += sizeof (guchar); + } + + /* command prefix */ + memcpy (result + pos, cmd_delete_prefix, cmd_delete_prefix_len); + pos += cmd_delete_prefix_len; + + /* 2-bytes size logic for counter again */ + if (num_to_delete > 7) + { + memset (result + pos, 0x01, sizeof (guchar)); + pos += sizeof (guchar); + memset (result + pos, ((num_to_delete - 8) * 0x20), sizeof (guchar)); + pos += sizeof (guchar); + } + else + { + /* first byte is 0x00, just skip it */ + pos += sizeof (guchar); + memset (result + pos, (num_to_delete * 0x20), sizeof (guchar)); + pos += sizeof (guchar); + } + + /* append desired 32-byte fingerprint IDs */ + /* if passed a delete_print then fetch its data from the FpPrint */ + if (delete_print) + { + g_object_get (delete_print, "description", &print_description, NULL); + g_object_get (delete_print, "fpi-data", &print_data, NULL); + + if (!g_variant_check_format_string (print_data, "(@ay)", FALSE)) + { + fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + return NULL; + } + + g_variant_get (print_data, "(@ay)", &print_data_id_var); + print_data_id = g_variant_get_fixed_array (print_data_id_var, &print_data_id_len, sizeof (guchar)); + + if (!g_str_has_prefix (print_description, "FP")) + fp_dbg ("Fingerprint '%s' was not created by libfprint; deleting anyway.", print_description); + + fp_info ("Delete fingerprint %s (%s)", print_description, print_data_id); + + memcpy (result + pos, print_data_id, EGISMOC_FINGERPRINT_DATA_SIZE); + } + /* Otherwise assume this is a "clear" - just loop through and append all enrolled IDs */ + else + { + for (int i = 0; i < self->enrolled_ids->len; i++) + memcpy (result + pos + (EGISMOC_FINGERPRINT_DATA_SIZE * i), + g_ptr_array_index (self->enrolled_ids, i), + EGISMOC_FINGERPRINT_DATA_SIZE); + } + pos += body_length; + + if (length_out) + *length_out = total_length; + + return g_steal_pointer (&result); +} + +static void +egismoc_delete_cb (FpDevice *device, + guchar *buffer_in, + gsize length_in, + GError *error) +{ + fp_dbg ("Delete callback"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + /* Check that the read payload indicates "success" with the delete */ + if (egismoc_validate_response_prefix (buffer_in, + length_in, + rsp_delete_success_prefix, + rsp_delete_success_prefix_len)) + { + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_CLEAR_STORAGE) + { + fpi_device_clear_storage_complete (device, NULL); + fpi_ssm_next_state (self->task_ssm); + } + else if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_DELETE) + { + fpi_device_delete_complete (device, NULL); + fpi_ssm_next_state (self->task_ssm); + } + else + { + fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Unsupported delete action.")); + } + } + else + { + fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Delete print was not successfull")); + } +} + +static void +egismoc_delete_run_state (FpiSsm *ssm, + FpDevice *device) +{ + g_autofree guchar *payload = NULL; + gsize payload_length = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case DELETE_GET_ENROLLED_IDS: + /* get enrolled_ids and enrolled_num from device for use building delete payload below */ + egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, egismoc_list_fill_enrolled_ids_cb); + break; + + case DELETE_DELETE: + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_DELETE) + payload = egismoc_get_delete_cmd (device, fpi_ssm_get_data (ssm), &payload_length); + else + payload = egismoc_get_delete_cmd (device, NULL, &payload_length); + + egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, g_free, egismoc_delete_cb); + break; + } +} + +static void +egismoc_clear_storage (FpDevice *device) +{ + fp_dbg ("Clear storage"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + self->task_ssm = fpi_ssm_new (device, + egismoc_delete_run_state, + DELETE_STATES); + fpi_ssm_start (self->task_ssm, egismoc_task_ssm_done); +} + +static void +egismoc_delete (FpDevice *device) +{ + fp_dbg ("Delete"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + g_autoptr(FpPrint) delete_print = NULL; + + fpi_device_get_delete_data (device, &delete_print); + + self->task_ssm = fpi_ssm_new (device, + egismoc_delete_run_state, + DELETE_STATES); + fpi_ssm_set_data (self->task_ssm, g_steal_pointer (&delete_print), NULL); /* todo leak or cleared by libfprint ? */ + fpi_ssm_start (self->task_ssm, egismoc_task_ssm_done); +} + +static void +egismoc_enroll_status_report (FpDevice *device, + EnrollPrint *enroll_print, + EnrollStatus status, + GError *error) +{ + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + switch (status) + { + case ENROLL_STATUS_DEVICE_FULL: + case ENROLL_STATUS_DUPLICATE: + fpi_ssm_mark_failed (self->task_ssm, error); + break; + + case ENROLL_STATUS_RETRY: + fpi_device_enroll_progress (device, enroll_print->stage, NULL, error); + break; + + case ENROLL_STATUS_PARTIAL_OK: + enroll_print->stage++; + fp_info ("Partial capture successful. Please touch the sensor again (%d/%d)", + enroll_print->stage, + EGISMOC_MAX_ENROLL_NUM); + fpi_device_enroll_progress (device, enroll_print->stage, enroll_print->print, NULL); + break; + + case ENROLL_STATUS_COMPLETE: + fp_info ("Enrollment was successful!"); + fpi_device_enroll_complete (device, g_object_ref (enroll_print->print), NULL); + break; + + default: + if (error) + fpi_ssm_mark_failed (self->task_ssm, error); + else + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "Unknown error")); + } +} + +static void +egismoc_read_capture_cb (FpDevice *device, + guchar *buffer_in, + gsize length_in, + GError *error) +{ + fp_dbg ("Read capture callback"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + EnrollPrint *enroll_print = fpi_ssm_get_data (self->task_ssm); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + /* Check that the read payload indicates "success" */ + if (egismoc_validate_response_prefix (buffer_in, + length_in, + rsp_read_success_prefix, + rsp_read_success_prefix_len) && + egismoc_validate_response_suffix (buffer_in, + length_in, + rsp_read_success_suffix, + rsp_read_success_suffix_len)) + { + egismoc_enroll_status_report (device, enroll_print, ENROLL_STATUS_PARTIAL_OK, NULL); + } + else + { + /* If not success then the sensor can either report "off center" or "sensor is dirty" */ + + /* "Off center" */ + if (egismoc_validate_response_prefix (buffer_in, + length_in, + rsp_read_offcenter_prefix, + rsp_read_offcenter_prefix_len) && + egismoc_validate_response_suffix (buffer_in, + length_in, + rsp_read_offcenter_suffix, + rsp_read_offcenter_suffix_len)) + error = fpi_device_retry_new (FP_DEVICE_RETRY_CENTER_FINGER); + + /* "Sensor is dirty" */ + else if (egismoc_validate_response_prefix (buffer_in, + length_in, + rsp_read_dirty_prefix, + rsp_read_dirty_prefix_len)) + error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER, + "Your device is having trouble recognizing you. Make sure your sensor is clean."); + + else + error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER, + "Unknown failure trying to read your finger. Please try again."); + + egismoc_enroll_status_report (device, enroll_print, ENROLL_STATUS_RETRY, error); + } + + if (enroll_print->stage == EGISMOC_ENROLL_TIMES) + fpi_ssm_next_state (self->task_ssm); + else + fpi_ssm_jump_to_state (self->task_ssm, ENROLL_CAPTURE_SENSOR_RESET); +} + +static void +egismoc_enroll_check_cb (FpDevice *device, + guchar *buffer_in, + gsize length_in, + GError *error) +{ + fp_dbg ("Enroll check callback"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + /* Check that the read payload reports "not yet enrolled" */ + if (egismoc_validate_response_prefix (buffer_in, + length_in, + rsp_check_not_yet_enrolled_prefix, + rsp_check_not_yet_enrolled_prefix_len)) + fpi_ssm_next_state (self->task_ssm); + else + egismoc_enroll_status_report (device, NULL, ENROLL_STATUS_DUPLICATE, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_DUPLICATE)); +} + +/* + Builds the full "check" payload which includes identifiers for all fingerprints which currently + should exist on the storage. This payload is used during both enrollment and verify actions. + */ +static guchar * +egismoc_get_check_cmd (FpDevice *device, + gsize *length_out) +{ + fp_dbg ("Get check command"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + guchar *device_print_id = NULL; + guchar *result = NULL; + gsize pos = 0; + + /* + The final command body should contain: + 1) hard-coded 00 00 + 2) 2-byte size indiciator, 20*Number enrolled identifiers plus 9 in form of: (enrolled_num + 1) * 0x20 + 0x09 + Since max prints can be higher than 7 then this goes up to 2 bytes (e9 + 9 = 109) + 3) Hard-coded prefix (cmd_check_prefix) + 4) 2-byte size indiciator, 20*Number of enrolled identifiers without plus 9 ((enrolled_num + 1) * 0x20) + 5) Hard-coded 32 * 0x00 bytes + 6) All of the currently registered prints in their 32-byte device identifiers (enrolled_list) + 7) Hard-coded suffix (cmd_check_suffix) + */ + + const gsize body_length = sizeof (guchar) * self->enrolled_num * EGISMOC_FINGERPRINT_DATA_SIZE; + + /* total_length is the 6 various bytes plus all other prefixes/suffixes and the body payload */ + const gsize total_length = (sizeof (guchar) * 6) + + cmd_check_prefix_len + + EGISMOC_CMD_CHECK_SEPARATOR_LENGTH + + body_length + + cmd_check_suffix_len; + + /* pre-fill entire payload with 00s */ + result = g_malloc0 (total_length); + + /* start with 00 00 (just move starting offset up by 2) */ + pos = 2; + + /* Size Counter bytes */ + /* "easiest" way to handle 2-bytes size for counter is to hard-code logic for when we go to the 2nd byte */ + /* note this will not work in case any model ever supports more than 14 prints (assumed max is 10) */ + if (self->enrolled_num > 6) + { + memset (result + pos, 0x01, sizeof (guchar)); + pos += sizeof (guchar); + memset (result + pos, ((self->enrolled_num - 7) * 0x20) + 0x09, sizeof (guchar)); + pos += sizeof (guchar); + } + else + { + /* first byte is 0x00, just skip it */ + pos += sizeof (guchar); + memset (result + pos, ((self->enrolled_num + 1) * 0x20) + 0x09, sizeof (guchar)); + pos += sizeof (guchar); + } + + /* command prefix */ + memcpy (result + pos, cmd_check_prefix, cmd_check_prefix_len); + pos += cmd_check_prefix_len; + + /* 2-bytes size logic for counter again */ + if (self->enrolled_num > 6) + { + memset (result + pos, 0x01, sizeof (guchar)); + pos += sizeof (guchar); + memset (result + pos, (self->enrolled_num - 7) * 0x20, sizeof (guchar)); + pos += sizeof (guchar); + } + else + { + /* first byte is 0x00, just skip it */ + pos += sizeof (guchar); + memset (result + pos, (self->enrolled_num + 1) * 0x20, sizeof (guchar)); + pos += sizeof (guchar); + } + + /* add 00s "separator" to offset position */ + pos += EGISMOC_CMD_CHECK_SEPARATOR_LENGTH; + + /* append all currently registered 32-byte fingerprint IDs */ + const gsize print_id_length = sizeof (guchar) * EGISMOC_FINGERPRINT_DATA_SIZE; + + for (int i = 0; i < self->enrolled_num; i++) + { + device_print_id = g_ptr_array_index (self->enrolled_ids, i); + memcpy (result + pos + (print_id_length * i), device_print_id, print_id_length); + } + pos += body_length; + + /* command suffix */ + memcpy (result + pos, cmd_check_suffix, cmd_check_suffix_len); + pos += cmd_check_suffix_len; + + if (length_out) + *length_out = total_length; + + return g_steal_pointer (&result); +} + +static void +egismoc_enroll_run_state (FpiSsm *ssm, + FpDevice *device) +{ + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + EnrollPrint *enroll_print = fpi_ssm_get_data (ssm); + g_autofree guchar *payload = NULL; + gsize payload_length = 0; + g_autofree guchar *device_print_id = NULL; + g_autofree gchar *user_id = NULL; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case ENROLL_GET_ENROLLED_IDS: + /* get enrolled_ids and enrolled_num from device for use in check stages below */ + egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, egismoc_list_fill_enrolled_ids_cb); + break; + + case ENROLL_CHECK_ENROLLED_NUM: + if (self->enrolled_num >= EGISMOC_MAX_ENROLL_NUM) + { + egismoc_enroll_status_report (device, enroll_print, ENROLL_STATUS_DEVICE_FULL, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_FULL)); + return; + } + fpi_ssm_next_state (ssm); + break; + + case ENROLL_SENSOR_RESET: + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case ENROLL_SENSOR_ENROLL: + egismoc_exec_cmd (device, cmd_sensor_enroll, cmd_sensor_enroll_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case ENROLL_WAIT_FINGER: + egismoc_wait_finger_on_sensor (ssm, device); + break; + + case ENROLL_SENSOR_CHECK: + egismoc_exec_cmd (device, cmd_sensor_check, cmd_sensor_check_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case ENROLL_CHECK: + payload = egismoc_get_check_cmd (device, &payload_length); + egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, g_free, egismoc_enroll_check_cb); + break; + + case ENROLL_START: + egismoc_exec_cmd (device, cmd_enroll_starting, cmd_enroll_starting_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case ENROLL_CAPTURE_SENSOR_RESET: + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case ENROLL_CAPTURE_SENSOR_START_CAPTURE: + egismoc_exec_cmd (device, cmd_sensor_start_capture, cmd_sensor_start_capture_len, NULL, + egismoc_task_ssm_next_state_cb); + break; + + case ENROLL_CAPTURE_WAIT_FINGER: + egismoc_wait_finger_on_sensor (ssm, device); + break; + + case ENROLL_CAPTURE_READ_RESPONSE: + egismoc_exec_cmd (device, cmd_read_capture, cmd_read_capture_len, NULL, egismoc_read_capture_cb); + break; + + case ENROLL_COMMIT_START: + egismoc_exec_cmd (device, cmd_commit_starting, cmd_commit_starting_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case ENROLL_COMMIT: + user_id = fpi_print_generate_user_id (enroll_print->print); + fp_dbg ("New fingerprint ID: %s", user_id); + + device_print_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE); + memcpy (device_print_id, user_id, MIN (strlen (user_id), EGISMOC_FINGERPRINT_DATA_SIZE)); + egismoc_set_print_data (enroll_print->print, device_print_id); + + /* create new dynamic payload of cmd_new_print_prefix + device_print_id */ + payload_length = cmd_new_print_prefix_len + EGISMOC_FINGERPRINT_DATA_SIZE; + payload = g_malloc0 (payload_length); + memcpy (payload, cmd_new_print_prefix, cmd_new_print_prefix_len); + memcpy (payload + cmd_new_print_prefix_len, device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); + + egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, g_free, egismoc_task_ssm_next_state_cb); + break; + + case ENROLL_COMMIT_SENSOR_RESET: + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case ENROLL_COMPLETE: + egismoc_enroll_status_report (device, enroll_print, ENROLL_STATUS_COMPLETE, NULL); + fpi_ssm_next_state (ssm); + break; + } +} + +static void +egismoc_enroll (FpDevice *device) +{ + fp_dbg ("Enroll"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + EnrollPrint *enroll_print = g_new0 (EnrollPrint, 1); + + fpi_device_get_enroll_data (device, &enroll_print->print); + enroll_print->stage = 0; + + self->task_ssm = fpi_ssm_new (device, egismoc_enroll_run_state, ENROLL_STATES); + fpi_ssm_set_data (self->task_ssm, g_steal_pointer (&enroll_print), g_free); + fpi_ssm_start (self->task_ssm, egismoc_task_ssm_done); +} + +static void +egismoc_identify_check_cb (FpDevice *device, + guchar *buffer_in, + gsize length_in, + GError *error) +{ + fp_dbg ("Identify check callback"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + guchar device_print_id[EGISMOC_FINGERPRINT_DATA_SIZE]; + FpPrint *print = NULL; + FpPrint *verify_print = NULL; + GPtrArray *prints; + gboolean found = FALSE; + guint index; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + /* Check that the read payload indicates "match" */ + if (egismoc_validate_response_prefix (buffer_in, + length_in, + rsp_identify_match_prefix, + rsp_identify_match_prefix_len) && + egismoc_validate_response_suffix (buffer_in, + length_in, + rsp_identify_match_suffix, + rsp_identify_match_suffix_len)) + { + /* + On success, there is a 32 byte array of "something"(?) in chars 14-45 + and then the 32 byte array ID of the matched print comes as chars 46-77 + */ + memcpy (device_print_id, + buffer_in + EGISMOC_IDENTIFY_RESPONSE_PRINT_ID_OFFSET, + EGISMOC_FINGERPRINT_DATA_SIZE); + + /* Create a new print from this ID and then see if it matches the one indicated */ + print = fp_print_new (device); + egismoc_set_print_data (print, device_print_id); + + if (!print) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID, + "Failed to build a print from device response.")); + return; + } + + fp_info ("Identify successful for: %s", fp_print_get_description (print)); + + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY) + { + fpi_device_get_identify_data (device, &prints); + found = g_ptr_array_find_with_equal_func (prints, + print, + (GEqualFunc) fp_print_equal, + &index); + + if (found) + fpi_device_identify_report (device, g_ptr_array_index (prints, index), print, NULL); + else + fpi_device_identify_report (device, NULL, print, NULL); + + fpi_ssm_next_state (self->task_ssm); + } + else + { + fpi_device_get_verify_data (device, &verify_print); + fp_info ("Verifying against: %s", fp_print_get_description (verify_print)); + + if (fp_print_equal (verify_print, print)) + fpi_device_verify_report (device, FPI_MATCH_SUCCESS, print, NULL); + else + fpi_device_verify_report (device, FPI_MATCH_FAIL, print, NULL); + + fpi_ssm_next_state (self->task_ssm); + } + } + /* If device was successfully read but it was a "not matched" */ + else if (egismoc_validate_response_prefix (buffer_in, + length_in, + rsp_identify_notmatch_prefix, + rsp_identify_notmatch_prefix_len)) + { + fp_info ("Print was not identified by the device"); + + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY) + { + fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); + fpi_ssm_next_state (self->task_ssm); + } + else + { + fpi_device_identify_report (device, NULL, NULL, NULL); + fpi_ssm_next_state (self->task_ssm); + } + } + else + { + fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Unrecognized response from device.")); + } +} + +static void +egismoc_identify_run_state (FpiSsm *ssm, + FpDevice *device) +{ + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + g_autofree guchar *payload = NULL; + gsize payload_length = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case IDENTIFY_GET_ENROLLED_IDS: + /* get enrolled_ids and enrolled_num from device for use in check stages below */ + egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, egismoc_list_fill_enrolled_ids_cb); + break; + + case IDENTIFY_CHECK_ENROLLED_NUM: + if (self->enrolled_num == 0) + { + fpi_ssm_mark_failed (g_steal_pointer (&self->task_ssm), + fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND)); + return; + } + fpi_ssm_next_state (ssm); + break; + + case IDENTIFY_SENSOR_RESET: + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case IDENTIFY_SENSOR_IDENTIFY: + egismoc_exec_cmd (device, cmd_sensor_identify, cmd_sensor_identify_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case IDENTIFY_WAIT_FINGER: + egismoc_wait_finger_on_sensor (ssm, device); + break; + + case IDENTIFY_SENSOR_CHECK: + egismoc_exec_cmd (device, cmd_sensor_check, cmd_sensor_check_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + case IDENTIFY_CHECK: + payload = egismoc_get_check_cmd (device, &payload_length); + egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, g_free, egismoc_identify_check_cb); + break; + + case IDENTIFY_COMPLETE_SENSOR_RESET: + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + break; + + /* + In Windows, the driver seems at this point to then immediately take another read from the sensor; + this is suspected to be an on-chip "verify". However, because the user's finger is still on the + sensor from the identify, then it seems to always return positive. We will consider this extra + step unnecessary and just skip it in this driver. This driver will instead handle matching of the + FpPrint from the gallery in the "verify" case of the callback egismoc_identify_check_cb. + */ + + case IDENTIFY_COMPLETE: + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY) + fpi_device_identify_complete (device, NULL); + else + fpi_device_verify_complete (device, NULL); + + fpi_ssm_mark_completed (ssm); + break; + } +} + +static void +egismoc_identify_verify (FpDevice *device) +{ + fp_dbg ("Identify or Verify"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + self->task_ssm = fpi_ssm_new (device, egismoc_identify_run_state, IDENTIFY_STATES); + fpi_ssm_start (self->task_ssm, egismoc_task_ssm_done); +} + +static void +egismoc_fw_version_cb (FpDevice *device, + guchar *buffer_in, + gsize length_in, + GError *error) +{ + fp_dbg ("Firmware version callback"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + g_autofree guchar *fw_version = NULL; + gsize prefix_length; + gsize fw_version_length; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + /* Check that the read payload indicates "success" */ + if (!egismoc_validate_response_suffix (buffer_in, + length_in, + rsp_fw_version_suffix, + rsp_fw_version_suffix_len)) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Device firmware response was not valid.")); + return; + } + + /* + FW Version is 12 bytes: a carriage return (0x0d) plus the version string itself. + Always skip [the read prefix] + [2 * check bytes] + [3 * 0x00] that come with every payload + Then we will also skip the carriage return and take all but the last 2 bytes as the FW Version + */ + prefix_length = egismoc_read_prefix_len + 2 + 3 + 1; + fw_version_length = length_in - prefix_length - rsp_fw_version_suffix_len; + fw_version = g_malloc0 (fw_version_length + 1); + + memcpy (fw_version, + buffer_in + prefix_length, + length_in - prefix_length - rsp_fw_version_suffix_len); + *(fw_version + fw_version_length) = '\0'; + + fp_info ("Device firmware version is %s", fw_version); + + fpi_ssm_next_state (self->task_ssm); +} + +static void +egismoc_dev_init_done (FpiSsm *ssm, + FpDevice *device, + GError *error) +{ + if (error) + g_usb_device_release_interface (fpi_device_get_usb_device (device), 0, 0, NULL); + + fpi_device_open_complete (device, error); +} + +static void +egismoc_dev_init_handler (FpiSsm *ssm, + FpDevice *device) +{ + g_autoptr(FpiUsbTransfer) transfer = fpi_usb_transfer_new (device); + + switch (fpi_ssm_get_cur_state (ssm)) + { + case DEV_INIT_CONTROL1: + fpi_usb_transfer_fill_control (transfer, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 32, 0x0000, 4, 16); + goto send_control; + break; + + case DEV_INIT_CONTROL2: + fpi_usb_transfer_fill_control (transfer, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 32, 0x0000, 4, 40); + goto send_control; + break; + + case DEV_INIT_CONTROL3: + fpi_usb_transfer_fill_control (transfer, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_STANDARD, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0, 0x0000, 0, 2); + goto send_control; + break; + + case DEV_INIT_CONTROL4: + fpi_usb_transfer_fill_control (transfer, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_STANDARD, + G_USB_DEVICE_RECIPIENT_DEVICE, + 0, 0x0000, 0, 2); + goto send_control; + break; + + case DEV_INIT_CONTROL5: + fpi_usb_transfer_fill_control (transfer, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + 82, 0x0000, 0, 8); + goto send_control; + break; + + case DEV_GET_FW_VERSION: + egismoc_exec_cmd (device, cmd_fw_version, cmd_fw_version_len, NULL, egismoc_fw_version_cb); + break; + } + + return; + +send_control: + transfer->ssm = ssm; + transfer->short_is_error = TRUE; + fpi_usb_transfer_submit (g_steal_pointer (&transfer), + EGISMOC_USB_CONTROL_TIMEOUT, + NULL, + fpi_ssm_usb_transfer_cb, + NULL); +} + +static void +egismoc_open (FpDevice *device) +{ + fp_dbg ("Opening device"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + GError *error = NULL; + + self->interrupt_cancellable = g_cancellable_new (); + + if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) + goto error; + + if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error)) + goto error; + + self->task_ssm = fpi_ssm_new (device, egismoc_dev_init_handler, DEV_INIT_STATES); + fpi_ssm_start (self->task_ssm, egismoc_dev_init_done); + return; + +error: + return fpi_device_open_complete (device, error); +} + +static void +egismoc_cancel (FpDevice *device) +{ + fp_dbg ("Cancel"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + + g_cancellable_cancel (self->interrupt_cancellable); + g_clear_object (&self->interrupt_cancellable); + self->interrupt_cancellable = g_cancellable_new (); +} + +static void +egismoc_close (FpDevice *device) +{ + fp_dbg ("Closing device"); + FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + GError *error = NULL; + + egismoc_cancel (device); + + g_usb_device_release_interface (fpi_device_get_usb_device (device), 0, 0, &error); + fpi_device_close_complete (device, error); + + if (self->task_ssm) + fpi_ssm_free (self->task_ssm); + self->task_ssm = NULL; + + if (self->cmd_ssm) + fpi_ssm_free (self->cmd_ssm); + self->cmd_ssm = NULL; + + self->cmd_transfer = NULL; + + g_clear_object (&self->interrupt_cancellable); + + if (self->enrolled_ids) + g_ptr_array_free (self->enrolled_ids, TRUE); + self->enrolled_ids = NULL; + self->enrolled_num = -1; +} + +static void +fpi_device_egismoc_init (FpiDeviceEgisMoc *self) +{ + G_DEBUG_HERE (); +} + +static void +fpi_device_egismoc_class_init (FpiDeviceEgisMocClass *klass) +{ + FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); + + dev_class->id = FP_COMPONENT; + dev_class->full_name = EGISMOC_DRIVER_FULLNAME; + + dev_class->type = FP_DEVICE_TYPE_USB; + dev_class->scan_type = FP_SCAN_TYPE_PRESS; + dev_class->id_table = egismoc_id_table; + dev_class->nr_enroll_stages = EGISMOC_ENROLL_TIMES; + dev_class->temp_hot_seconds = 0; /* device should be "always off" unless being used */ + + dev_class->open = egismoc_open; + dev_class->cancel = egismoc_cancel; + dev_class->suspend = egismoc_cancel; + dev_class->close = egismoc_close; + dev_class->identify = egismoc_identify_verify; + dev_class->verify = egismoc_identify_verify; + dev_class->enroll = egismoc_enroll; + dev_class->delete = egismoc_delete; + dev_class->clear_storage = egismoc_clear_storage; + dev_class->list = egismoc_list; + + fpi_device_class_auto_initialize_features (dev_class); + dev_class->features |= FP_DEVICE_FEATURE_DUPLICATES_CHECK; +} diff --git a/libfprint/drivers/egismoc/egismoc.h b/libfprint/drivers/egismoc/egismoc.h new file mode 100644 index 00000000..c30bac1b --- /dev/null +++ b/libfprint/drivers/egismoc/egismoc.h @@ -0,0 +1,231 @@ +/* + * Driver for Egis Technology (LighTuning) Match-On-Chip sensors + * Originally authored 2023 by Joshua Grisham + * + * Portions of code and logic inspired from the elanmoc libfprint driver + * which is copyright (C) 2021 Elan Microelectronics Inc (see elanmoc.c) + * + * Based on original reverse-engineering work by Joshua Grisham. The protocol has + * been reverse-engineered from captures of the official Windows driver, and by + * testing commands on the sensor with a multiplatform Python prototype driver: + * https://github.com/joshuagrisham/galaxy-book2-pro-linux/tree/main/fingerprint/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "fpi-device.h" +#include "fpi-ssm.h" + +G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC, FpDevice) + +#define EGISMOC_DRIVER_FULLNAME "Egis Technology (LighTuning) Match-on-Chip" +#define EGISMOC_EP_CMD_OUT (0x02 | FPI_USB_ENDPOINT_OUT) +#define EGISMOC_EP_CMD_IN (0x81 | FPI_USB_ENDPOINT_IN) +#define EGISMOC_EP_CMD_INTERRUPT_IN 0x83 + +#define EGISMOC_USB_CONTROL_TIMEOUT 5000 +#define EGISMOC_USB_SEND_TIMEOUT 5000 +#define EGISMOC_USB_RECV_TIMEOUT 5000 +#define EGISMOC_USB_INTERRUPT_TIMEOUT 0 + +#define EGISMOC_USB_IN_RECV_LENGTH 4096 +#define EGISMOC_USB_INTERRUPT_IN_RECV_LENGTH 64 + +#define EGISMOC_ENROLL_TIMES 10 +#define EGISMOC_MAX_ENROLL_NUM 10 +#define EGISMOC_FINGERPRINT_DATA_SIZE 32 +#define EGISMOC_LIST_RESPONSE_PREFIX_SIZE 14 +#define EGISMOC_LIST_RESPONSE_SUFFIX_SIZE 2 + +struct _FpiDeviceEgisMoc +{ + FpDevice parent; + FpiSsm *task_ssm; + FpiSsm *cmd_ssm; + FpiUsbTransfer *cmd_transfer; + GCancellable *interrupt_cancellable; + + int enrolled_num; + GPtrArray *enrolled_ids; +}; + + +/* standard prefixes for all read/writes */ + +static guchar egismoc_write_prefix[] = {'E', 'G', 'I', 'S', 0x00, 0x00, 0x00, 0x01}; +static gsize egismoc_write_prefix_len = sizeof (egismoc_write_prefix) / sizeof (egismoc_write_prefix[0]); + +static guchar egismoc_read_prefix[] = {'S', 'I', 'G', 'E', 0x00, 0x00, 0x00, 0x01}; +static gsize egismoc_read_prefix_len = sizeof (egismoc_read_prefix) / sizeof (egismoc_read_prefix[0]); + + +/* hard-coded command payloads */ + +static guchar cmd_fw_version[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x0c}; +static gsize cmd_fw_version_len = sizeof (cmd_fw_version) / sizeof (cmd_fw_version[0]); +static guchar rsp_fw_version_suffix[] = {0x90, 0x00}; +static gsize rsp_fw_version_suffix_len = sizeof (rsp_fw_version_suffix) / sizeof (rsp_fw_version_suffix[0]); + +static guchar cmd_list[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x19, 0x04, 0x00, 0x00, 0x01, 0x40}; +static gsize cmd_list_len = sizeof (cmd_list) / sizeof (cmd_list[0]); + +static guchar cmd_sensor_reset[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x1a, 0x00, 0x00}; +static gsize cmd_sensor_reset_len = sizeof (cmd_sensor_reset) / sizeof (cmd_sensor_reset[0]); + +static guchar cmd_sensor_check[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x17, 0x02, 0x00}; +static gsize cmd_sensor_check_len = sizeof (cmd_sensor_check) / sizeof (cmd_sensor_check[0]); + +static guchar cmd_sensor_identify[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x17, 0x01, 0x01}; +static gsize cmd_sensor_identify_len = sizeof (cmd_sensor_identify) / sizeof (cmd_sensor_identify[0]); +static guchar rsp_identify_match_prefix[] = {0x00, 0x00, 0x00, 0x42}; +static gsize rsp_identify_match_prefix_len = sizeof (rsp_identify_match_prefix) / sizeof (rsp_identify_match_prefix[0]); +static guchar rsp_identify_match_suffix[] = {0x90, 0x00}; +static gsize rsp_identify_match_suffix_len = sizeof (rsp_identify_match_suffix) / sizeof (rsp_identify_match_suffix[0]); +static guchar rsp_identify_notmatch_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x90, 0x04}; +static gsize rsp_identify_notmatch_prefix_len = sizeof (rsp_identify_notmatch_prefix) / sizeof (rsp_identify_notmatch_prefix[0]); + +static guchar cmd_sensor_enroll[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x17, 0x01, 0x00}; +static gsize cmd_sensor_enroll_len = sizeof (cmd_sensor_enroll) / sizeof (cmd_sensor_enroll[0]); + +static guchar cmd_enroll_starting[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x01, 0x00, 0x00, 0x00, 0x20}; +static gsize cmd_enroll_starting_len = sizeof (cmd_enroll_starting) / sizeof (cmd_enroll_starting[0]); + +static guchar cmd_sensor_start_capture[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x16, 0x02, 0x01}; +static gsize cmd_sensor_start_capture_len = sizeof (cmd_sensor_start_capture) / sizeof (cmd_sensor_start_capture[0]); + +static guchar cmd_read_capture[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x02, 0x02, 0x00, 0x00, 0x02}; +static gsize cmd_read_capture_len = sizeof (cmd_read_capture) / sizeof (cmd_read_capture[0]); +static guchar rsp_read_success_prefix[] = {0x00, 0x00, 0x00, 0x04}; +static gsize rsp_read_success_prefix_len = sizeof (rsp_read_success_prefix) / sizeof (rsp_read_success_prefix[0]); +static guchar rsp_read_success_suffix[] = {0x0a, 0x90, 0x00}; +static gsize rsp_read_success_suffix_len = sizeof (rsp_read_success_suffix) / sizeof (rsp_read_success_suffix[0]); +static guchar rsp_read_offcenter_prefix[] = {0x00, 0x00, 0x00, 0x04}; +static gsize rsp_read_offcenter_prefix_len = sizeof (rsp_read_offcenter_prefix) / sizeof (rsp_read_offcenter_prefix[0]); +static guchar rsp_read_offcenter_suffix[] = {0x0a, 0x64, 0x91}; +static gsize rsp_read_offcenter_suffix_len = sizeof (rsp_read_offcenter_suffix) / sizeof (rsp_read_offcenter_suffix[0]); +static guchar rsp_read_dirty_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x64}; +static gsize rsp_read_dirty_prefix_len = sizeof (rsp_read_dirty_prefix) / sizeof (rsp_read_dirty_prefix[0]); + +static guchar cmd_commit_starting[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x05, 0x00, 0x00, 0x00, 0x20}; +static gsize cmd_commit_starting_len = sizeof (cmd_commit_starting) / sizeof (cmd_commit_starting[0]); + + +/* commands which exist on the device but are currently not used */ +/* + static guchar cmd_sensor_cancel[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x16, 0x04, 0x00}; + static gsize cmd_sensor_cancel_len = sizeof(cmd_sensor_cancel) / sizeof(cmd_sensor_cancel[0]); + + static guchar cmd_sensor_verify[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x04, 0x01, 0x00}; + static gsize cmd_sensor_verify_len = sizeof(cmd_sensor_verify) / sizeof(cmd_sensor_verify[0]); + + static guchar cmd_read_verify[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x04, 0x02, 0x00}; + static gsize cmd_read_verify_len = sizeof(cmd_read_verify) / sizeof(cmd_read_verify[0]); + */ + + +/* prefixes/suffixes and other things for dynamically created command payloads */ + +#define EGISMOC_CHECK_BYTES_LENGTH 2 +#define EGISMOC_IDENTIFY_RESPONSE_PRINT_ID_OFFSET 46 +#define EGISMOC_CMD_CHECK_SEPARATOR_LENGTH 32 + +static guchar cmd_new_print_prefix[] = {0x00, 0x00, 0x00, 0x27, 0x50, 0x16, 0x03, 0x00, 0x00, 0x00, 0x20}; +static gsize cmd_new_print_prefix_len = sizeof (cmd_new_print_prefix) / sizeof (cmd_new_print_prefix[0]); + +static guchar cmd_delete_prefix[] = {0x50, 0x18, 0x04, 0x00, 0x00}; +static gsize cmd_delete_prefix_len = sizeof (cmd_delete_prefix) / sizeof (cmd_delete_prefix[0]); +static guchar rsp_delete_success_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x90, 0x00}; +static gsize rsp_delete_success_prefix_len = sizeof (rsp_delete_success_prefix) / sizeof (rsp_delete_success_prefix[0]); + +static guchar cmd_check_prefix[] = {0x50, 0x17, 0x03, 0x00, 0x00}; +static gsize cmd_check_prefix_len = sizeof (cmd_check_prefix) / sizeof (cmd_check_prefix[0]); +static guchar cmd_check_suffix[] = {0x00, 0x40}; +static gsize cmd_check_suffix_len = sizeof (cmd_check_suffix) / sizeof (cmd_check_suffix[0]); +static guchar rsp_check_not_yet_enrolled_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x90}; +static gsize rsp_check_not_yet_enrolled_prefix_len = sizeof (rsp_check_not_yet_enrolled_prefix) / sizeof (rsp_check_not_yet_enrolled_prefix[0]); + + +/* SSM task states and various status enums */ + +typedef enum { + CMD_SEND, + CMD_GET, + CMD_STATES, +} CommandStates; + +typedef enum { + DEV_INIT_CONTROL1, + DEV_INIT_CONTROL2, + DEV_INIT_CONTROL3, + DEV_INIT_CONTROL4, + DEV_INIT_CONTROL5, + DEV_GET_FW_VERSION, + DEV_INIT_STATES, +} DeviceInitStates; + +typedef enum { + IDENTIFY_GET_ENROLLED_IDS, + IDENTIFY_CHECK_ENROLLED_NUM, + IDENTIFY_SENSOR_RESET, + IDENTIFY_SENSOR_IDENTIFY, + IDENTIFY_WAIT_FINGER, + IDENTIFY_SENSOR_CHECK, + IDENTIFY_CHECK, + IDENTIFY_COMPLETE_SENSOR_RESET, + IDENTIFY_COMPLETE, + IDENTIFY_STATES, +} IdentifyStates; + +typedef enum { + ENROLL_GET_ENROLLED_IDS, + ENROLL_CHECK_ENROLLED_NUM, + ENROLL_SENSOR_RESET, + ENROLL_SENSOR_ENROLL, + ENROLL_WAIT_FINGER, + ENROLL_SENSOR_CHECK, + ENROLL_CHECK, + ENROLL_START, + ENROLL_CAPTURE_SENSOR_RESET, + ENROLL_CAPTURE_SENSOR_START_CAPTURE, + ENROLL_CAPTURE_WAIT_FINGER, + ENROLL_CAPTURE_READ_RESPONSE, + ENROLL_COMMIT_START, + ENROLL_COMMIT, + ENROLL_COMMIT_SENSOR_RESET, + ENROLL_COMPLETE, + ENROLL_STATES, +} EnrollStates; + +typedef enum { + ENROLL_STATUS_DEVICE_FULL, + ENROLL_STATUS_DUPLICATE, + ENROLL_STATUS_PARTIAL_OK, + ENROLL_STATUS_RETRY, + ENROLL_STATUS_COMPLETE, +} EnrollStatus; + +typedef enum { + LIST_GET_ENROLLED_IDS, + LIST_RETURN_ENROLLED_PRINTS, + LIST_STATES, +} ListStates; + +typedef enum { + DELETE_GET_ENROLLED_IDS, + DELETE_DELETE, + DELETE_STATES, +} DeleteStates; diff --git a/libfprint/meson.build b/libfprint/meson.build index da8285e5..8eed4dd5 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -119,6 +119,8 @@ driver_sources = { [ 'drivers/etes603.c' ], 'egis0570' : [ 'drivers/egis0570.c' ], + 'egismoc' : + [ 'drivers/egismoc/egismoc.c' ], 'vfs0050' : [ 'drivers/vfs0050.c' ], 'elan' : diff --git a/meson.build b/meson.build index aeef6911..10d17e75 100644 --- a/meson.build +++ b/meson.build @@ -120,6 +120,7 @@ default_drivers = [ 'vfs0050', 'etes603', 'egis0570', + 'egismoc', 'vcom5s', 'synaptics', 'elan', diff --git a/tests/egismoc/custom.pcapng b/tests/egismoc/custom.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..fcd8119ed5643faaa19e43c62c678d40ad71abb0 GIT binary patch literal 73752 zcmeHw3!F_=8~2`zVFu++M3ZPn8RlY$TsCscZIBS@2)V_KTQ`*vA&MclMDNg5Q7REd z2bH4S@1a~?-J=vr@jcI8>sf1`XU=W!_q^Y{zwh_0`K@!?`~08vtpBtA>)C7XeI}t~ z$%IvgVRUSKbLA*_z9LJsk!GZ&j~SfUX5fg#b|c0Nyg#{Xt;CkuqXs75(Xm$IkOnDf z>Ge~y5|bYs0y}l<)~a5e^jh%!7^8yGVaSNF4<**GlU^q^C8J)-pn)R>jvCV6Fk+2H z#%*JV=k)KFlbGCN$f$v%2lpE_tX5*ZI;nLs5|i5u8I$;cc%9R)|FC|81`bH zNkecb*j8+3Tc}5%n%)>A+9+y_9ewZc>=7A8JZz1F&r1Yp2W+;!kNbJ>4V0}#h}qcZpgN^TrBhu*;l(bmTn zF5-_7Z!P?NfZr9~VhAL6uiTe^C3xfifGKv7bM*U@398*^Gm?TQdw5(jr~F5 zSB^h$+#gZ)|%_^We!x{UkAaS37=cFy?>op0QPW9S$MFo z@Q2>P2h1PTBP{68#03kI2E%Ktro`tu(Q=0H2NAIrC;|L2qI$IJ)V*5^zW-GCfcshI zIh&td{QJ2Z+JSybb4IQOOz+^0eCdIgh(7=-7WDHd;A2Cuni8M;xp=1V6JaD2ys{Pf zAx1l5b04ZLwE57=zYoX2hFc#_0Y0`Lt10oh4=?5YXFgPUUi)Bl_3y)1U;}+92JOJ& z?TU}|4qnQB^1w^%qrp(IAP?UFep`5rmGHR_`Ll!%h={d7k@yK$i7kWS3GF;2cSJj5 z7cEHY4)3vgTT4G;qETm*j%Cgkew2rOum&ov+O+Ei+oH-HgU6y-<8i+dP|+TbffvE9 z37=ynyDf!XrEmSgE|y?6ak~d9$KPVd?@NA7K))K3zD<#S`4=AmW&GIZo?Bm7kklJ? z!RnRxEn_Tv)o+)c745GA`(Q0lB!0JcZCb&$s743jv1G&4%+xw*@OMjEo}PQ`oQ+9P|72A!>d8EW&wL5-2JahcWPmevAXJ9{a?MV7w$DujD^oSqu7?Ekm zx5pwMhuL7)gl@O0w9DhL?$_?)u-p$C-Z|GMOwqd1y<>Tx_=vOSk`V_Z&pmO}5Qf>INi8tSIw){rHTe#ya z2aQ2Ny}$kUH`FnRA@OOPRhMfATVEBcFeu|#3lygJ`-a0F(Ru0^#6BUqgq7?PPh>acb>U|)oA7nNJe|8x5Wkzq zbs7`?=r@F4{yPzXWj}i0CB}iBr*0QDPrCggmm>Ti3_Cx6z0J)DOFuFXMPvCw$BzBRmw&BEm%ziY(H!KMk@ zu;xmeoTJ+B2_MmBu;PJ@T=TAv*7!zw^2wK@C4lcFx4;<3f|{2P_{0(6bB+>MJL8CJ zo}Hubz#bmfT8FmrqBXibF^3b=!T;We(<^8 zHC^MLqeVq*9I5vE>fN<#opH3+nwMs&dC3EPhXrHeSFmToYpfI#-J&L1$HFv@MuI)$ z$l3?@lcf~BVEUaeKS%s>FK|3!@u<<&oKKu`9a&(rmoZ^;(u43Gt9cdqBXor9Wvq<0IBEdo?p0d(_IR z7z0m-7O>mkFO+Rkn-scmUdkC0Ltqy9X-AU;c!~_r{UmzC12}Tlnq2 zlL0gckLT|GSn){soX2xdV-JO&TkRWyHuGXJUeF(VZuN8Dk97`Qiv02A@#km6dC72d zV51EfDVkLQM@yo#$NRDWJs&Hca;06)Q~w#lPd7R61slFmiTv{A=>p(8nYZ)67c4m5 zQd@tLg#C$?@HtPZWASJd)~{r>-q;^x9ME{%`y6bn-qWa5X5sAvOH#-e)X{q*PK}Mh9#Q?G_!h}Pn2eQ%aFgmJXU`o){$n0%DvWsO%s}om27ej z*MUteZZvaJ+k*Hsuam}1dQkY}W~}YG;Zr@u5^V-ZB zpDKsQ6&|o-cPnf8wWpp>i+6=ebw>n&Q?s!p*(vRE6EsDnz@@z3lTr?Wev_^Fg3_ zz4rUXEk47Y5306;_Q8Ar7mrv|AKK$_v>xo5@VQk3%-ie1E*3ZQ!LVsI`~dmau(iT3 z{{tNiGhXdu`_S<()z}V{e;q1S(rSOeu`Th{e9#YekBv&mgC`o>cAM=_YMYd^ay@BZ zres7le>K=Np*>j1CfAeIU=xcQJ!yW*hR=?-Y5X-WD|`x#L-!Z0Tgl^X ziaF2TtE_QkLR;-c#Ru~zU+#pOU+Ut1rTJ=&N*-_5l*ijUPl-9KFf{*9XkT||FIH~} z;#1^~)V{U<7IR)CHUHi}_p5#%sQ z_1I?XtAt;skGRq!T+QEOX?}D-&3`>d+cc~C=W6KP#UiTt*Nr@=cp{ser?0^#7B_mb z(vDx5d=h#>{g~5N_~b@UzUX{N>4_=vNlys>>Bi31?}BOm=Bz_1F6g`g&C6tOViRX; ztf4eNwaX#RkvvDjIXVkA-Ey?WhHt2KV_%M@GP!I_QW%<_ntohqK95mq-^wFmJu;G- z|2)_=!Dp+oI3>GPj*5P9qxl&bHhiWfg#Rw!yVd+-Yfk8~&9sE@``#LbJzx_{u$aK@9;lq7w1pbq*hfAI%_pDUzRmsmTihe36i0;5 zIhtIf2sY1}_K~LoMdsb=n0Ywt5$*rH+6=H~!e*?LZ<6MdKUdWhYgM?9y(rs3^8EL| z`d(>1;d9NuWwMyV3PbZNAN^iwJ{^ZpUPAsaoFeADNNWB9uxmnRVkNsgPAg3n{o+RR zn;+Bobi7D;KKZo~@ZD;Dg#kaRu}%0qPUl^PS5Av@=69^IINfdK`7r_W{8Iz9O*)3> zTn#8yBBFWz==*-uJjt~R&eJ)tNq$-{0z7!&bFL?Y25Ed+d*M8NTUz+!CeLr!>L;xy zbY5Ft<4tj}_KawMBs4#(%}**WXf2F$wDVVI9EH;SLtxW{zQsy5IY+&IbI;M_b{e17 zF*!$ZzyAjuCH<^8B78oU*6wn){uXR)XJhius=w5Fwp`nxm}tI7tiM@(iv21}IGTU@ z+uxPu6FzBvlFajO%o1xDg`xQ`e*e4Dd^+DiF-q+_*(LI8J2(IEyXtqcOL0nehjkVG z;zskA{iyNjyb#4H;eX%F{o2mT`_8MeP52b2gnxE>Q7cly&GUCznjafb^Yeyin{=*@ zbG36<(THmP^+V4qp2#NWso!qxCyfn%&mQ=YYyQSz8lTP)bDpC13ZGnQe%lL5PY9p$ z^zcftHWdlY|DoLl6&GZWb9B=?&eq>TX@2hQ7ZgWilXK(+n^^qj9@ZA|;DIO3(Le1q zKE(j%sL3k#>u=3!UQ`?rKIdrsGtSoEf@%IgH(pfzMPrxpd>Xq=X1c$Au5IhfO7jVy zG(TBte%t#+e;0=4d)i(OsQJ`B&j`cPkx1tGXTh!sZe!(|Pj=6OT`X=if8FgGpXOSM zQ^H@DBm4?iOYH0UBeq{rW1H|PP6>a+?_zB#T+RP`Cpa4$8*puB@h+Q9bzWMWdm)>P zFF3o_G1S^l#_lVMC!K>?Hk*>ojEmY&i?9F>9{8N=Nu|9SpK?^rQ>DM0t-%4UR>8HM zGs}$REO>{N@HtOsc8CM6Qm_lw0)-uq1zX$c{DzU-6#%g6;}&0zI`0zK`~{2)*#KI9 z3#Iu7!KMk{!Adqs^NFK_U=s`43|2g_k#m&1LgVY>OR=^?_{n?RU&put@QEYB=Nzp7 zd@SK|6ioBe-_rijF*<2J`IEj+_+wgD^@EBJ8517Zg#DEd6${2()e3RReTnX}x)uK5|S7gb}M@OhkO?9}JY{EjslH?#)1)5`OU1kCeaU9N4?+BE0t)!oj{ z4~EkG^c6)lPwHH?ujZ%k(SAZ01uP!e!u4d@n;M_a5p$lV?R9^CFnwAvr6+{Xc}ia? z))2$hlVF;^e0nhz7j({;bF}+zPgXj zSM!epzLRy*9BC6Za^{eD&5=5{XW<*9K{IEHYxu(DD46Ci85Xbli_Wof&0jK0T+?UT z`UYQyqxsF=xLRpG;gjaql6n4>8Dj7khUOb9u2z~)?V}i__IdNfoEJ&Wp9prbKe3Ws zic_-Na+c^9H=1AbEsd|wO^Y}s{D04OmYdi$|KWC}_1I={O85;^^m#MCV~uJ44oman z0&0GKdz(#lURvakWYbgE+4;dxntyYL(uyapCuH+Su!+U5kJc9P;DINu`6D~p@YQ)~ zU!Ky^+@Bw;b5j|uC+Zxu&=YF^FC~ju4GdRLf@%KIo6D%U;PW!sTYPn7ug@P)yNu$9 zZ1S;Gqm=N`4M#^(G(P1wG%u4+i%Pq{KL5e^vWg?Zr+Jz18~p5yqhOkUA)&19FLtda z*<16A`*XN!t5;TDW)U-DTMHMR+#$7#Ri;+kR{XMV>T)BL}7 z!j7?V0oQi2w`rSn49vNz`)3M{A3C@W2z- z{Bd7ud|H>_dUEUA!Y7?%*+B87>_-p0MEo46STKh?x9nP_CxlNt-6V6a(e1_lkf9{W55wgsnC7>bURCuMt;|`p{^h~q99Us!{n>u}%0qPA^@h*F5}=H5oUwwi8P8f5_H0 z>0B3SKK0qq(ve-;xhtop;)!gM=9A6CU=xd9AFVCo!2?fR^S>FX@#*{{=gCvn{o2l) zjyEbjA$-nLxig~u;p$1SJikWg8&zD8JwM-{th ze2M|i(fr@sbM$6vlH!Q)IY)JNCxCa6S?Ja*mzc87KY{@on1Si=2QEs{ucRjxLu#Et@@p7KG~fOcCon8{14`6e41-1 zP6_|gIcK?vUGsl0UPp~>!lyVT{CdNLsG}D8)kCKF+pV>oq5;=-23=*d$$gJ1+798@ z&33j1mm{rX&UNaiI!aFnpYv3sgSfvS5}JSWFLsW|ACW_mKf^jYyN4o_<}U=BCiraC zV!|fp=rGvC;x`VhE#kogPn@HiGaBDpf_(DjXk{n&_fX6|t2rWk&e6Wk&NvE|=NJD~ z`(s`u{gKyd5=ZT`gg>U}7WEj8=0CSHQ)xcobIm_gH^IUz49)*_SEkbZ+-rzW-xy6x z6LVf9^ZbW)XR0_QyF5OY@5b)cg;gwArjiKKXKW>6AG46ws#Cqv30Q`^gOyPlmL~d7A!j?WctI z`{aR7xt?^HqVc`u$tPc)W}SBS8yR-Z-#6eErTK)A+rulQ)HPrnz=!=flZ@V(h-}{>T>+^s3tf|ucT!~MbUt8w+Km1$FVTGajL$@|n znr~!F|K)EGQ~QQ~E9SgNYW_N~OL0neDNf1mIo(8|H{j>-R@Zf>Zx#qw5l*acyPCoha^yVw>&kq(I*j($0#OFK}eF9q*{<=Z1 zwVgTlHCJ(wJCywKKXFiV#Sz)$93@T?KKhMA%X{(QfhW#U#$b(a z4kVv^Im&p_*}X3Id3i$17K$Un=NwJ&h;`mbXnu677OKCzX}fKTEByDn@iqzMBT7(67 z@WAI>PZsqlh)-*9)Q^1{IGda8dUD{FPD)Rx&p1!>%SP_E7t$Ja(s2>vo9B~1zm{`$ zZB;1E&j*_(^et9H+TF+-Uwkc6^%4<^LKXUe`Buf8Ea9TD{cRR&%+$r=Re<$6jrdF;h>3>hCvDFN0kZwqYf^ zoU09B7mFLNDy7))Ro)@;Nb1XjMTK8(uHAW}PA{eVhRQE2e1q^?#EbTat10}q%NJPQ zi#)GVYOmz6@E)rvhU3v z@UR7i?cww5a%h(cpIcS7i}QGYanTRK+5`}W2P(%ukf!my{mE}%9_N>E&*RlS`Y0X= zpXP=-GJl?VMhvR3c^nRo*!~ZbJ+Ir)vyU3%=7Z#q$cM>Z(_bTd{_L(^eKbeXCUHbI z4}(oCe)C9ci+J$B6X&SPT^c_(hkOzo5&rYPxj%o_7VxvcE>^ko^JhJ_!-P5w#5UL^R%pw z#`oUmpQl=XIO8b~jX}X<(VvI*NzQ@SSP7r=G(1M+@~N;7)&fPiXV5iB9p=Lx)kBGs z{yzbB9)BLLdCZ-3|4{q?(jp#*$2nVX42Q=CBXmELUC!egu#3eFkIl0+zWD_C#LdP%0~9MbOYElp--$TZE_Bq zmJ@y8Ml&ZqYQvvTKKXJOQ{EYeIPS3ESlk2nS@0UGDe*an-2fj;DOs&I_6If=gRMor z^O)wym`ncna+D642HtNMFn5)a~heC zW@{ah_!OgrKXx}pf_oi#-}<(Qp&uU0PR1!8t9K^(BXor9-LzM%|M!4>um&omBi}v( zd$be(Ys(C@GkqQ^)c_{3yDK)GA?CD@~d=Oy^J?eHSM zYqZf}Tud&$$MXBXAOrj)IyY2@=NT(uZ|o0yPVh_ANHvy?1=JtDaZ;zJv)`r)wN~9d zdZgl*Y;un0f=w)L#`2gLjc?8(pL{u9)yvuMKjc`g5gfl?c8ng&5}$K?afq{f)_G1~ z&%bjjjZw!@-lxbPUyfFe5ZBm5@;K^4uxo2I)$#ZCUb^cszCPA9*7c{-8p z?AoC`t2KhBO@ED1$5DjOd5Z2LuB%Cf?_n)a*jRLW9AzJC&Hf&v#+td1{PpE=d{<}J z7KIvXQ^2MPPFYpjUbAN5oJiyO_wqYfF&f$K*$5KjG z>y7<^amdG-oui(YHAhB(d2_)X&NvE{fBy??n$TXXWRr9BHQ2=BhNC;K6vU@fwx|VEOl-(PLGAQB2T%9%Qd!FX4}A^_qUb7_o|s2@kx% zIWZq97M#PMJvBk)--J*3cdE?4pS>vVyD5zPd+EO?1k@2~-_k!_-HQ}3|2_*gP3Rx1 zG94kCXTc^GH!-^Fv<;ul1%$urFZcH%wb}55icvln5Plnn_abo}vFG1+c%RVuH=DD` z-W_|z^~?WD`S(0%x5Y8=$me`&_dIAf7B~5KiA@@x=VpXoV!yNNx>s7Q5o7mMi%BZ~ zCVZMN2>;ZbLW^)t!CIiO`QpEsf6u#Zk{ZjD3)31owRc`mXY);{oZuALG{I@CWRvC_ zvUv(@VsSH;<6CQdItHTohVbL>azEcZ+JA}~%Y;uHr%8QooM|=RbxvTheT$ta-G1 zyQwPw*4Gh;ynyh_SHWZ2@c-ZOf0BQnxqYe{Yq`Veyp71esXcF9E3P%exDOWlv?da2 ztc`3xRdGl*IfrRgMIQvShkfLM%GWbKu;J5nm^9YNr*VMq_V?MQbeO8f8sYP?)~1?h ze<^4m)&hm;DW5m(`S*IT*A)P;df9apibpx8t+(h2+z`>)dlRuCwNdpD!f*Td#MWDr6KK=B3oqwxq%!JmHz5Arr|1ag=O`e&rc-GghS?z90?OqS<#^NUb zK54_(*JSwe+~47RvC?Xd%)hUCPUqk1+6yt~Q~L+JAO?RHd=G1Z!sd%e=im0+?)oy% zsoajPpW|bB$&$!lqw+G?HNkPLWS5WUnoC7TxXJB4F01kBdMwHdh=p;yPU^A!7i3yHgUTLD%a&zoisk(C&YQIcg)#&$viX$1=qTY*Ilf5Bz(@}^bOdm z|2sMQoYci?Zq@qa%VRmOvwN{Z<>;wti#3NzvwSsoCD_CgY%a&`9;lqdzUdmDG@WyJ zY@@StjyY%y3UU~eu~^NmgwHvgxJk6X6nqD3fx^aOupGTjJ++}tyVEgHYrBQX5CYwPu%E8)599yTu(lU+>P*?{@eX=-TOG0I=!aLWQ8_yG7wHJuJN%KzQT~^5^eOMH3CZ&mujnT?Ir=NBZTR{A zd46T1v)nC53|(vNPOZCE@oY$Z&hykp&hF#l_gvU>vzlpZb#C@H`6K3Z;<;vHaUWN> zxtY-h9D;jea5N=CMSG}G9;jGQqh1EPCcL()w9Cii%U~CatK2NzhQEsZ@*R(>nmD`P zF3)O>Xn);u>r`%LN_@^!-Rqp?*dNva1J!F)FF)?$q6@=gi(K9|Y|e%kdsg0Y=S`dU z%(p4&?H9+xgkQ;u+{j$NaPYk5NMFb3%hACoXSrFZ z`&t`c(0xcYIY*77-REZaT-5k#0 z4^vFsdc=KhHf!ZaDmNp1T0bTHSsx@=VuY;oYC9jYCZu*Z=jY@wBpT}uRqCRiyR}V#6qV5aY+Yg+Ljqs{M!s?zlRm< zczoh{%@O5uoTG_R;(lt33BPe6`;XSXLTUb1uxY|~u#!#A(N?gDC73x6jLs4sOyMNqjz*8l7~;QLy9jqlY%>{-WbidCrV9@8~Jvj|u%@ zFQw>>9J?NPkNuSo6$|FNzo&ezG@p*8Nb@u0@%XCqBG)Yp&7WNNbFKMr5uYM2q4u@D z=fU$ATZ9Rs`tc1_rZ)hq2%oRZyau#3fw=0B6F@eSpdh*QGv_@}dD z0K5Kv_uf{e`Gik#O8Bomj?o~-ncuM{$7+nl^c+ce(@z*VlZ z95Pg%e-3P#&@8J;o1CY2!6p{JK3ZGEg9o0to=kdQ;~VdgPrf|eHo<)kdF{MyN>2!% z^K_)Iv){uCrum1#URUrFtD&yD_8m_P?-9R;h5Z$*&%)LG#QED4M`V+8^bOd=63iZM z_dw+w^|0fs`^tPdstCU+=Jp(P_px7Sj#!>gW2wCSzoV$RN_XM4Dr_tT)BNATo(W&H zD&3nZHJ|LQ0(g^`-=A>TeZtZFnPAt1KC&vi=a}qv2D@0?X#N^IKD%$6@E??Z`41v6 z%y_lyZ_nm?)!63ujn|iR`N1t#^R02_cdSW#()>MEo*x}B&+pzs+oXH#DTgGR`#y7a zO-m@vpAR-oa04sZBt0RU6+efKQ2hERHd&7zc;tGr*^a-+_jru(TLQjYJ$d+seM(Pq zB|d3B;s3b|S12R1w$r7?J{1?<+2oJVe6qLebyw#!DnvswF$Q@b&Ig+&e2$fD7U1X& z;iDUlw%hSvAfJThlTV!h-z`UJvHKNAUWv~+dV87}ERoRs)kXHJ{xYAG{z%Oyf9gLi z*6y)S?Ij$|?{WOF)_jRic|PGE7$oN4!qEKrCk`vkH`M*LzA;*Hh?v78srg@nT@yMJ zE7|37dJove;zsk=oz(cb&-#zkM)Ln@xzYSCFCI~2oA7y@?y41UwI9cs-?1jbSgq~s zwlqH`pyrQ#Mcbr#hHL&GwVmBZ6iV~o2Ad|d->P!%Ugl;(T$_m8(@6L4+k^WNGf z<=mwCWHUbB+3yR5()>z&PAi_sCg-Uq*u>&SPpaLm@m0bu1KZ$M3N@~zSn z!sk3~n;_N@Bcb_AntiL{f^q@Q(ea65O)+3xSiPzLPfV!w{8Hw(nj@LtaE`Jj2_M~X zw63`gU(I8_W9hFaovoAFHGgZh?-WOR&a&_gih+dw;yQtFISQuvpCo>#`b*_bGS4S_ zMFxuN229BXJ)wQ8$g%5zH#kS;L&bt~*r$K~q&1)NB&PXsO%tsB3q$kcFa4x7pB;;m z|LvNIIWLl$e+Sq#!E3Dic$DnU0lQe-Xnvi)HNKk5nU;(&o#iHW%|HCyFKTQPKG%}U z$MpYW@;lZXYJQDH+9uua%k^ZyHNQT|o9oCYU(J8+BlqXo58ZWEaYXo>qig;ZxpTO) z`kYn$<$Xu`!!&P?~RyZP4AG=TrM; zR1tYrBsJft{DNz_p!etF%qye)37^3E7;s+F1@6D$g(V-g(WFw8?oYwMP4C z2}^(n4}8uwKWcSBe7X*he2RMCeGb`n_64OU)MuQhwsV~2kij&6#j_U*#0AaE~ckd8$cqBEy&GyR$`kiY@o1LOx+-Ux^9U4DSoKD;2EH|-h{@9U5t*#J*SP7rU z>DbZY95jwIzhjMQ{!UBtiv-mC&SSJq8rz(!&JT$5?E!6Kd!f);aVX6{0X9w8A1m49 zJe>fWSp52EZ4nP1c;cEr^Z|`en#}cN=tIJ%aL>Eg^ZbLIqiSV=U95ypJT;`XRq<~( zEO^ZI3lzpvFwMV|6{X{X&C6u(12BT;JHzEDl;&r2iBcSCABEEz1;hs;8RvZyN=VZ)7Of& zE$w+=JH~WAR4ll*lQAs5)_8c0RcV7<+adgOH;LcBEey?XGCaOkcW4?`uKCoye`<^C zmLjS7Ua)JTO`BcP60)0{BKpOR=J&}ih|lBn+Pcnu7uc@(>6MGCu}%HPzMm36I#&EIZme$jxMKkjrvo1Cjl{Udu0H}>1IiYMwv&eNm; z+E0tH01qDcoa;&4cN(AKfb%qYptChNyPnM3Qcmj$TQ?w{Dzu8+Iow{Kms4>;vht;e$Rnj^J-C^VlqI&h=2 z^|xS}fARJ5s=t&!QuE2)L&@&X;nv<-MQJ|abIo7grZ})=7=@wvr?*v6nosedHyH;tEsp!m(ux8an6O}qSbNX5B68E zKBM)wP@4Z2*fasls?sLs=n=4qC73A!U&F?c*<9lBrzeJo8 z{-T$iop-Wp{-z2^YHSle#VO%Och&bk`WG{P=*H|86C1(|ntJ^5yDWH)r>F zhSL1{*Cc74q)pD#19xaY?c;;L_P_^RPdZfA__=GzCtseP?(Y5`&rTPTw4O+O&eM!^ zv4$9~o&?kU3l}v<-uKBLUyd5o6Kjf*()_`H>OLf!oTJykCKkVOXl)S>9(dv$4fspr z=dLE7d^vh4)7d(y{a9@8fn?2*#OEB9D=XFy!{sPgp5Nq9vWjW%2I-H?^J(lpR!*!Z znxb3OV>p_hFfLVTKH-z*H`9p8D*?gUR5_&>5ht`Yi+RkOLX+pEG=1QBKr^{dyi(enDE#kogPh3yFX{_+4GoTG#a&NvFD`J=08f9P6Dx+jbL8C_BM zV~WsKkKt(kFB?p)`BGC!^P5P`e{-xD{DqGdAe7whx_#rzDLCaFSzD!o~iMT_sAz-o+irm zZ#U-$XWrLRX+Gg|o;p``wpJW0&rcZKQpE+$F|__haj~_UIKIUG3f5=guI)SpHch~? zs+>1DNA2YAzXr1h5PsKoJ{qF&Y2GKzC!fYAIy+|0L1R$xoM-Y-%@N^qj=lqYEU39k zcdbw40~<@hG{5pNn?IBf$~>R^IV^w6)wJ7y*UGlE=Yh{Krt_g zApB>a5^HURq4{(7wpW_Z*LJ9V-KUGSx=60=)Y#Wv$0=LeA-ku*E*3YMpRr%#dvot- z^3${{S9qMBd)nC=qU< z&2J#(c5+QfD9!%~Y?|N$R<0#a6e);T9iX*bgIhqSLvAEIv zTXJmpPmoW(9KC95uK&kk4ZyT| zO+SRA`3KVPRGLrtl;;zEk5>!tTFtKY?o^uZeV+LAjnUn&MgCgN)nFI<6D!%}aaw^Q(NPlZ{_e}rY8f?OEpkfx42-V+jpqBlyhvJIta;~1eNBHVyes1=Y z#`n%8zkIp+$m8su=^XF{3+7!HUhkGc^tgDx8jlTIgj7n zZ^Ey_Y^8^^27^`i{56+L65v}6q_D%jk^F($zPjRWj&tNvOEgqm9EnG=REd|6X(4$p--^} zDl{IO2h>$R(>j3p)76F0E)%w4rFL}tAx*a-nv;Vik5=!U@cIDd+IkBgX!wIIsKHb(tOT&%-AB%_2Ia{ z8mJJD;f%)z=Jr!OkzLNyYOsqXm`&X7fy#NxoUifu|LY;1#(wVp`hjJDp9OZY5BmoVdv<9mvi9h{{T|Ie6s)m literal 0 HcmV?d00001 diff --git a/tests/egismoc/custom.py b/tests/egismoc/custom.py new file mode 100755 index 00000000..3a662380 --- /dev/null +++ b/tests/egismoc/custom.py @@ -0,0 +1,156 @@ +#!/usr/bin/python3 + +import traceback +import sys +import time +import gi + +gi.require_version('FPrint', '2.0') +from gi.repository import FPrint, GLib + +# Exit with error on any exception, included those happening in async callbacks +sys.excepthook = lambda *args: (traceback.print_exception(*args), sys.exit(1)) + +ctx = GLib.main_context_default() + +c = FPrint.Context() +c.enumerate() +devices = c.get_devices() + +d = devices[0] +del devices + +d.open_sync() + +assert d.get_driver() == "egismoc" +assert not d.has_feature(FPrint.DeviceFeature.CAPTURE) +assert d.has_feature(FPrint.DeviceFeature.IDENTIFY) +assert d.has_feature(FPrint.DeviceFeature.VERIFY) +assert d.has_feature(FPrint.DeviceFeature.DUPLICATES_CHECK) +assert d.has_feature(FPrint.DeviceFeature.STORAGE) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_LIST) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_DELETE) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR) + +def enroll_progress(*args): + print("finger status: ", d.get_finger_status()) + print('enroll progress: ' + str(args)) + +def identify_done(dev, res): + global identified + identified = True + identify_match, identify_print = dev.identify_finish(res) + print('indentification_done: ', identify_match, identify_print) + assert identify_match.equal(identify_print) + +# Beginning with list and clear assumes you begin with >0 prints enrolled before capturing + +print("listing - device should have prints") +stored = d.list_prints_sync() +assert len(stored) > 0 +del stored + +print("clear device storage") +d.clear_storage_sync() +print("clear done") + +print("listing - device should be empty") +stored = d.list_prints_sync() +assert len(stored) == 0 +del stored + +print("enrolling") +template = FPrint.Print.new(d) +template.set_finger(FPrint.Finger.LEFT_INDEX) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +p1 = d.enroll_sync(template, None, enroll_progress, None) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("enroll done") +del template + +print("listing - device should have 1 print") +stored = d.list_prints_sync() +assert len(stored) == 1 +assert stored[0].equal(p1) + +print("verifying") +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +verify_res, verify_print = d.verify_sync(p1) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("verify done") +assert verify_res == True + +identified = False +deserialized_prints = [] +for p in stored: + deserialized_prints.append(FPrint.Print.deserialize(p.serialize())) + assert deserialized_prints[-1].equal(p) +del stored + +print('async identifying') +d.identify(deserialized_prints, callback=identify_done) +del deserialized_prints + +while not identified: + ctx.iteration(True) + +print("try to enroll duplicate") +template = FPrint.Print.new(d) +template.set_finger(FPrint.Finger.RIGHT_INDEX) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +try: + d.enroll_sync(template, None, enroll_progress, None) +except GLib.Error as error: + assert error.matches(FPrint.DeviceError.quark(), + FPrint.DeviceError.DATA_DUPLICATE) +except Exception as exc: + raise +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("duplicate enroll attempt done") + +print("listing - device should still only have 1 print") +stored = d.list_prints_sync() +assert len(stored) == 1 +assert stored[0].equal(p1) +del stored + +print("enroll new finger") +template = FPrint.Print.new(d) +template.set_finger(FPrint.Finger.RIGHT_INDEX) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +p2 = d.enroll_sync(template, None, enroll_progress, None) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("enroll new finger done") +del template + +print("listing - device should have 2 prints") +stored = d.list_prints_sync() +assert len(stored) == 2 +assert (stored[0].equal(p1) and stored[1].equal(p2)) or (stored[0].equal(p2) and stored[1].equal(p1)) +del stored + +print("deleting first print") +d.delete_print_sync(p1) +print("delete done") +del p1 + +print("listing - device should only have second print") +stored = d.list_prints_sync() +assert len(stored) == 1 +assert stored[0].equal(p2) +del stored +del p2 + +print("clear device storage") +d.clear_storage_sync() +print("clear done") + +print("listing - device should be empty") +stored = d.list_prints_sync() +assert len(stored) == 0 +del stored + +d.close_sync() + +del d +del c diff --git a/tests/egismoc/device b/tests/egismoc/device new file mode 100644 index 00000000..6bd912a0 --- /dev/null +++ b/tests/egismoc/device @@ -0,0 +1,262 @@ +P: /devices/pci0000:00/0000:00:14.0/usb3/3-5 +N: bus/usb/003/012=12010002FF0000407A1C820581110102030109022700010100A0320904000003FF000000070581020002000705020200020007058303400005 +E: BUSNUM=003 +E: CURRENT_TAGS=:snap_cups_ippeveprinter:snap_cups_cupsd: +E: DEVNAME=/dev/bus/usb/003/012 +E: DEVNUM=012 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: ID_BUS=usb +E: ID_MODEL=ETU905A80-E +E: ID_MODEL_ENC=ETU905A80-E +E: ID_MODEL_ID=0582 +E: ID_PATH=pci-0000:00:14.0-usb-0:5 +E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_5 +E: ID_REVISION=1181 +E: ID_SERIAL=EGIS_ETU905A80-E_0E7828PBS393 +E: ID_SERIAL_SHORT=0E7828PBS393 +E: ID_USB_INTERFACES=:ff0000: +E: ID_USB_MODEL=ETU905A80-E +E: ID_USB_MODEL_ENC=ETU905A80-E +E: ID_USB_MODEL_ID=0582 +E: ID_USB_REVISION=1181 +E: ID_USB_SERIAL=EGIS_ETU905A80-E_0E7828PBS393 +E: ID_USB_SERIAL_SHORT=0E7828PBS393 +E: ID_USB_VENDOR=EGIS +E: ID_USB_VENDOR_ENC=EGIS +E: ID_USB_VENDOR_ID=1c7a +E: ID_VENDOR=EGIS +E: ID_VENDOR_ENC=EGIS +E: ID_VENDOR_FROM_DATABASE=LighTuning Technology Inc. +E: ID_VENDOR_ID=1c7a +E: MAJOR=189 +E: MINOR=267 +E: PRODUCT=1c7a/582/1181 +E: SUBSYSTEM=usb +E: TAGS=:snap_cups_ippeveprinter:snap_cups_cupsd: +E: TYPE=255/0/0 +A: authorized=1\n +A: avoid_reset_quirk=0\n +A: bConfigurationValue=1\n +A: bDeviceClass=ff\n +A: bDeviceProtocol=00\n +A: bDeviceSubClass=00\n +A: bMaxPacketSize0=64\n +A: bMaxPower=100mA\n +A: bNumConfigurations=1\n +A: bNumInterfaces= 1\n +A: bcdDevice=1181\n +A: bmAttributes=a0\n +A: busnum=3\n +A: configuration= +H: descriptors=12010002FF0000407A1C820581110102030109022700010100A0320904000003FF000000070581020002000705020200020007058303400005 +A: dev=189:267\n +A: devnum=12\n +A: devpath=5\n +L: driver=../../../../../bus/usb/drivers/usb +L: firmware_node=../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:51/device:52/device:57 +A: idProduct=0582\n +A: idVendor=1c7a\n +A: ltm_capable=no\n +A: manufacturer=EGIS\n +A: maxchild=0\n +A: physical_location/dock=no\n +A: physical_location/horizontal_position=center\n +A: physical_location/lid=no\n +A: physical_location/panel=unknown\n +A: physical_location/vertical_position=center\n +L: port=../3-0:1.0/usb3-port5 +A: power/active_duration=1425996\n +A: power/async=enabled\n +A: power/autosuspend=2\n +A: power/autosuspend_delay_ms=2000\n +A: power/connected_duration=1426656\n +A: power/control=on\n +A: power/level=on\n +A: power/persist=0\n +A: power/runtime_active_kids=0\n +A: power/runtime_active_time=1426124\n +A: power/runtime_enabled=forbidden\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=0\n +A: power/runtime_usage=1\n +A: power/wakeup=disabled\n +A: power/wakeup_abort_count=\n +A: power/wakeup_active=\n +A: power/wakeup_active_count=\n +A: power/wakeup_count=\n +A: power/wakeup_expire_count=\n +A: power/wakeup_last_time_ms=\n +A: power/wakeup_max_time_ms=\n +A: power/wakeup_total_time_ms=\n +A: product=ETU905A80-E\n +A: quirks=0x0\n +A: removable=fixed\n +A: rx_lanes=1\n +A: serial=0E7828PBS393\n +A: speed=480\n +A: tx_lanes=1\n +A: urbnum=2803\n +A: version= 2.00\n + +P: /devices/pci0000:00/0000:00:14.0/usb3 +N: bus/usb/003/001=12010002090001406B1D020002060302010109021900010100E0000904000001090000000705810304000C +E: BUSNUM=003 +E: CURRENT_TAGS=:seat:snap_cups_cupsd:snap_cups_ippeveprinter: +E: DEVNAME=/dev/bus/usb/003/001 +E: DEVNUM=001 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: ID_AUTOSUSPEND=1 +E: ID_BUS=usb +E: ID_FOR_SEAT=usb-pci-0000_00_14_0 +E: ID_MODEL=xHCI_Host_Controller +E: ID_MODEL_ENC=xHCI\x20Host\x20Controller +E: ID_MODEL_FROM_DATABASE=2.0 root hub +E: ID_MODEL_ID=0002 +E: ID_PATH=pci-0000:00:14.0 +E: ID_PATH_TAG=pci-0000_00_14_0 +E: ID_REVISION=0602 +E: ID_SERIAL=Linux_6.2.0-34-generic_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 +E: ID_SERIAL_SHORT=0000:00:14.0 +E: ID_USB_INTERFACES=:090000: +E: ID_USB_MODEL=xHCI_Host_Controller +E: ID_USB_MODEL_ENC=xHCI\x20Host\x20Controller +E: ID_USB_MODEL_ID=0002 +E: ID_USB_REVISION=0602 +E: ID_USB_SERIAL=Linux_6.2.0-34-generic_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 +E: ID_USB_SERIAL_SHORT=0000:00:14.0 +E: ID_USB_VENDOR=Linux_6.2.0-34-generic_xhci-hcd +E: ID_USB_VENDOR_ENC=Linux\x206.2.0-34-generic\x20xhci-hcd +E: ID_USB_VENDOR_ID=1d6b +E: ID_VENDOR=Linux_6.2.0-34-generic_xhci-hcd +E: ID_VENDOR_ENC=Linux\x206.2.0-34-generic\x20xhci-hcd +E: ID_VENDOR_FROM_DATABASE=Linux Foundation +E: ID_VENDOR_ID=1d6b +E: MAJOR=189 +E: MINOR=256 +E: PRODUCT=1d6b/2/602 +E: SUBSYSTEM=usb +E: TAGS=:snap_cups_cupsd:seat:snap_cups_ippeveprinter: +E: TYPE=9/0/1 +A: authorized=1\n +A: authorized_default=1\n +A: avoid_reset_quirk=0\n +A: bConfigurationValue=1\n +A: bDeviceClass=09\n +A: bDeviceProtocol=01\n +A: bDeviceSubClass=00\n +A: bMaxPacketSize0=64\n +A: bMaxPower=0mA\n +A: bNumConfigurations=1\n +A: bNumInterfaces= 1\n +A: bcdDevice=0602\n +A: bmAttributes=e0\n +A: busnum=3\n +A: configuration= +H: descriptors=12010002090001406B1D020002060302010109021900010100E0000904000001090000000705810304000C +A: dev=189:256\n +A: devnum=1\n +A: devpath=0\n +L: driver=../../../../bus/usb/drivers/usb +L: firmware_node=../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:51/device:52 +A: idProduct=0002\n +A: idVendor=1d6b\n +A: interface_authorized_default=1\n +A: ltm_capable=no\n +A: manufacturer=Linux 6.2.0-34-generic xhci-hcd\n +A: maxchild=12\n +A: power/active_duration=337953872\n +A: power/async=enabled\n +A: power/autosuspend=0\n +A: power/autosuspend_delay_ms=0\n +A: power/connected_duration=337978524\n +A: power/control=auto\n +A: power/level=auto\n +A: power/runtime_active_kids=1\n +A: power/runtime_active_time=337962424\n +A: power/runtime_enabled=enabled\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=616\n +A: power/runtime_usage=0\n +A: power/wakeup=disabled\n +A: power/wakeup_abort_count=\n +A: power/wakeup_active=\n +A: power/wakeup_active_count=\n +A: power/wakeup_count=\n +A: power/wakeup_expire_count=\n +A: power/wakeup_last_time_ms=\n +A: power/wakeup_max_time_ms=\n +A: power/wakeup_total_time_ms=\n +A: product=xHCI Host Controller\n +A: quirks=0x0\n +A: removable=unknown\n +A: rx_lanes=1\n +A: serial=0000:00:14.0\n +A: speed=480\n +A: tx_lanes=1\n +A: urbnum=4969\n +A: version= 2.00\n + +P: /devices/pci0000:00/0000:00:14.0 +E: DRIVER=xhci_hcd +E: ID_MODEL_FROM_DATABASE=Alder Lake PCH USB 3.2 xHCI Host Controller +E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller +E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI +E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller +E: ID_VENDOR_FROM_DATABASE=Intel Corporation +E: MODALIAS=pci:v00008086d000051EDsv0000144Dsd0000C870bc0Csc03i30 +E: PCI_CLASS=C0330 +E: PCI_ID=8086:51ED +E: PCI_SLOT_NAME=0000:00:14.0 +E: PCI_SUBSYS_ID=144D:C870 +E: SUBSYSTEM=pci +A: ari_enabled=0\n +A: broken_parity_status=0\n +A: class=0x0c0330\n +H: config=8680ED51060490020130030C000080000400161D6000000000000000000000000000000000000000000000004D1470C8000000007000000000000000FF010000FD0134A089C27F8000000000000000003F6DD80F000000000000000000000000316000000000000000000000000000000180C2C1080000000000000000000000059087007805E0FE000000000000000009B014F01000400100000000C10A080000080E00001800008F50020000010000090000018680C00009001014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B50F010112000000 +A: consistent_dma_mask_bits=64\n +A: d3cold_allowed=1\n +A: dbc=disabled\n +A: device=0x51ed\n +A: dma_mask_bits=64\n +L: driver=../../../bus/pci/drivers/xhci_hcd +A: driver_override=(null)\n +A: enable=1\n +L: firmware_node=../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:51 +A: index=7\n +L: iommu=../../virtual/iommu/dmar1 +L: iommu_group=../../../kernel/iommu_groups/8 +A: irq=145\n +A: label=Onboard - Other\n +A: local_cpulist=0-15\n +A: local_cpus=ffff\n +A: modalias=pci:v00008086d000051EDsv0000144Dsd0000C870bc0Csc03i30\n +A: msi_bus=1\n +A: msi_irqs/145=msi\n +A: numa_node=-1\n +A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 6 9 2112 9\nxHCI ring segments 26 34 4096 34\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 32 128 1\nbuffer-32 0 0 32 0\n +A: power/async=enabled\n +A: power/control=auto\n +A: power/runtime_active_kids=1\n +A: power/runtime_active_time=337964621\n +A: power/runtime_enabled=enabled\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=438\n +A: power/runtime_usage=0\n +A: power/wakeup=enabled\n +A: power/wakeup_abort_count=0\n +A: power/wakeup_active=0\n +A: power/wakeup_active_count=7\n +A: power/wakeup_count=0\n +A: power/wakeup_expire_count=7\n +A: power/wakeup_last_time_ms=336554844\n +A: power/wakeup_max_time_ms=105\n +A: power/wakeup_total_time_ms=721\n +A: power_state=D0\n +A: resource=0x000000601d160000 0x000000601d16ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n +A: revision=0x01\n +A: subsystem_device=0xc870\n +A: subsystem_vendor=0x144d\n +A: vendor=0x8086\n + diff --git a/tests/meson.build b/tests/meson.build index 199500d2..c919ec6e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -51,6 +51,7 @@ drivers_tests = [ 'goodixmoc', 'nb1010', 'egis0570', + 'egismoc', 'fpcmoc', 'realtek', 'focaltech_moc', From a9269980eb3d38995e4b1194e6be1135db3230c3 Mon Sep 17 00:00:00 2001 From: Joshua Grisham <18266314+joshuagrisham@users.noreply.github.com> Date: Mon, 16 Oct 2023 02:57:45 +0200 Subject: [PATCH 33/64] egismoc: Fix crash during close --- libfprint/drivers/egismoc/egismoc.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 0b249c44..aeac0b6e 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -1385,26 +1385,10 @@ egismoc_close (FpDevice *device) GError *error = NULL; egismoc_cancel (device); + g_clear_object (&self->interrupt_cancellable); g_usb_device_release_interface (fpi_device_get_usb_device (device), 0, 0, &error); fpi_device_close_complete (device, error); - - if (self->task_ssm) - fpi_ssm_free (self->task_ssm); - self->task_ssm = NULL; - - if (self->cmd_ssm) - fpi_ssm_free (self->cmd_ssm); - self->cmd_ssm = NULL; - - self->cmd_transfer = NULL; - - g_clear_object (&self->interrupt_cancellable); - - if (self->enrolled_ids) - g_ptr_array_free (self->enrolled_ids, TRUE); - self->enrolled_ids = NULL; - self->enrolled_num = -1; } static void From 7aae2181e288ecacd004b08b3eae9d5c3daf6462 Mon Sep 17 00:00:00 2001 From: Joshua Grisham <18266314+joshuagrisham@users.noreply.github.com> Date: Fri, 20 Oct 2023 19:54:23 +0200 Subject: [PATCH 34/64] egismoc: Add support for usernames longer than 8 chars --- libfprint/drivers/egismoc/egismoc.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index aeac0b6e..64727067 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -350,16 +350,26 @@ egismoc_exec_cmd (FpDevice *device, static void egismoc_set_print_data (FpPrint *print, - const guchar *device_print_id) + const guchar *device_print_id, + const gchar *user_id) { - g_autofree gchar *user_id = g_malloc (EGISMOC_FINGERPRINT_DATA_SIZE + 1); GVariant *print_id_var = NULL; GVariant *fpi_data = NULL; + g_autofree gchar *fill_user_id = NULL; - memcpy (user_id, device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); - memset (user_id + EGISMOC_FINGERPRINT_DATA_SIZE, '\0', sizeof (gchar)); + if (user_id) + { + fill_user_id = g_strdup (user_id); + } + else + { + fill_user_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE + 1); + memcpy (fill_user_id, device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); + memset (fill_user_id + EGISMOC_FINGERPRINT_DATA_SIZE, '\0', sizeof (gchar)); + } + + fpi_print_fill_from_user_id (print, fill_user_id); - fpi_print_fill_from_user_id (print, user_id); fpi_print_set_type (print, FPI_PRINT_RAW); fpi_print_set_device_stored (print, TRUE); @@ -384,7 +394,7 @@ egismoc_get_enrolled_prints (FpDevice *device) for (int i = 0; i < self->enrolled_num; i++) { print = fp_print_new (device); - egismoc_set_print_data (print, g_ptr_array_index (self->enrolled_ids, i)); + egismoc_set_print_data (print, g_ptr_array_index (self->enrolled_ids, i), NULL); g_ptr_array_add (result, g_object_ref_sink (print)); } @@ -990,7 +1000,7 @@ egismoc_enroll_run_state (FpiSsm *ssm, device_print_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE); memcpy (device_print_id, user_id, MIN (strlen (user_id), EGISMOC_FINGERPRINT_DATA_SIZE)); - egismoc_set_print_data (enroll_print->print, device_print_id); + egismoc_set_print_data (enroll_print->print, device_print_id, user_id); /* create new dynamic payload of cmd_new_print_prefix + device_print_id */ payload_length = cmd_new_print_prefix_len + EGISMOC_FINGERPRINT_DATA_SIZE; @@ -1066,9 +1076,9 @@ egismoc_identify_check_cb (FpDevice *device, buffer_in + EGISMOC_IDENTIFY_RESPONSE_PRINT_ID_OFFSET, EGISMOC_FINGERPRINT_DATA_SIZE); - /* Create a new print from this ID and then see if it matches the one indicated */ + /* Create a new print from this device_print_id and then see if it matches the one indicated */ print = fp_print_new (device); - egismoc_set_print_data (print, device_print_id); + egismoc_set_print_data (print, device_print_id, NULL); if (!print) { From 47fe3668e4eb079fed0bc6ce3ea8ce26854508dc Mon Sep 17 00:00:00 2001 From: Joshua Grisham <18266314+joshuagrisham@users.noreply.github.com> Date: Fri, 20 Oct 2023 20:18:33 +0200 Subject: [PATCH 35/64] egismoc: Return truncated text instead of null for description when using list --- libfprint/drivers/egismoc/egismoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 64727067..0aa31e3c 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -373,7 +373,7 @@ egismoc_set_print_data (FpPrint *print, fpi_print_set_type (print, FPI_PRINT_RAW); fpi_print_set_device_stored (print, TRUE); - g_object_set (print, "description", user_id, NULL); + g_object_set (print, "description", fill_user_id, NULL); print_id_var = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, device_print_id, From 85da0e104bb2cc674c98776d085b0f2aec46a114 Mon Sep 17 00:00:00 2001 From: Joshua Grisham <18266314+joshuagrisham@users.noreply.github.com> Date: Sat, 28 Oct 2023 15:25:16 +0200 Subject: [PATCH 36/64] egismoc: add support for 1c7a:05a1 --- libfprint/drivers/egismoc/egismoc.c | 41 ++++++++++++++++++----------- libfprint/drivers/egismoc/egismoc.h | 22 +++++++++------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 0aa31e3c..dfae716b 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -38,8 +38,9 @@ G_DEFINE_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FP_TYPE_DEVICE); static const FpIdEntry egismoc_id_table[] = { - { .vid = 0x1c7a, .pid = 0x0582 }, - { .vid = 0, .pid = 0 } + { .vid = 0x1c7a, .pid = 0x0582, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 }, + { .vid = 0x1c7a, .pid = 0x05a1, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE2 }, + { .vid = 0, .pid = 0, .driver_data = 0 } }; typedef void (*SynCmdMsgCallback) (FpDevice *device, @@ -808,10 +809,10 @@ egismoc_enroll_check_cb (FpDevice *device, } /* Check that the read payload reports "not yet enrolled" */ - if (egismoc_validate_response_prefix (buffer_in, + if (egismoc_validate_response_suffix (buffer_in, length_in, - rsp_check_not_yet_enrolled_prefix, - rsp_check_not_yet_enrolled_prefix_len)) + rsp_check_not_yet_enrolled_suffix, + rsp_check_not_yet_enrolled_suffix_len)) fpi_ssm_next_state (self->task_ssm); else egismoc_enroll_status_report (device, NULL, ENROLL_STATUS_DUPLICATE, @@ -846,9 +847,13 @@ egismoc_get_check_cmd (FpDevice *device, const gsize body_length = sizeof (guchar) * self->enrolled_num * EGISMOC_FINGERPRINT_DATA_SIZE; + /* prefix length can depend on the type */ + const gsize check_prefix_length = (fpi_device_get_driver_data (device) & EGISMOC_DRIVER_CHECK_PREFIX_TYPE2) ? + cmd_check_prefix_type2_len : cmd_check_prefix_type1_len; + /* total_length is the 6 various bytes plus all other prefixes/suffixes and the body payload */ const gsize total_length = (sizeof (guchar) * 6) - + cmd_check_prefix_len + + check_prefix_length + EGISMOC_CMD_CHECK_SEPARATOR_LENGTH + body_length + cmd_check_suffix_len; @@ -878,8 +883,16 @@ egismoc_get_check_cmd (FpDevice *device, } /* command prefix */ - memcpy (result + pos, cmd_check_prefix, cmd_check_prefix_len); - pos += cmd_check_prefix_len; + if (fpi_device_get_driver_data (device) & EGISMOC_DRIVER_CHECK_PREFIX_TYPE2) + { + memcpy (result + pos, cmd_check_prefix_type2, cmd_check_prefix_type2_len); + pos += cmd_check_prefix_type2_len; + } + else + { + memcpy (result + pos, cmd_check_prefix_type1, cmd_check_prefix_type1_len); + pos += cmd_check_prefix_type1_len; + } /* 2-bytes size logic for counter again */ if (self->enrolled_num > 6) @@ -1059,11 +1072,7 @@ egismoc_identify_check_cb (FpDevice *device, } /* Check that the read payload indicates "match" */ - if (egismoc_validate_response_prefix (buffer_in, - length_in, - rsp_identify_match_prefix, - rsp_identify_match_prefix_len) && - egismoc_validate_response_suffix (buffer_in, + if (egismoc_validate_response_suffix (buffer_in, length_in, rsp_identify_match_suffix, rsp_identify_match_suffix_len)) @@ -1119,10 +1128,10 @@ egismoc_identify_check_cb (FpDevice *device, } } /* If device was successfully read but it was a "not matched" */ - else if (egismoc_validate_response_prefix (buffer_in, + else if (egismoc_validate_response_suffix (buffer_in, length_in, - rsp_identify_notmatch_prefix, - rsp_identify_notmatch_prefix_len)) + rsp_identify_notmatch_suffix, + rsp_identify_notmatch_suffix_len)) { fp_info ("Print was not identified by the device"); diff --git a/libfprint/drivers/egismoc/egismoc.h b/libfprint/drivers/egismoc/egismoc.h index c30bac1b..65b50322 100644 --- a/libfprint/drivers/egismoc/egismoc.h +++ b/libfprint/drivers/egismoc/egismoc.h @@ -33,6 +33,10 @@ G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC, FpDevice) #define EGISMOC_DRIVER_FULLNAME "Egis Technology (LighTuning) Match-on-Chip" + +#define EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 (1 << 0) +#define EGISMOC_DRIVER_CHECK_PREFIX_TYPE2 (1 << 1) + #define EGISMOC_EP_CMD_OUT (0x02 | FPI_USB_ENDPOINT_OUT) #define EGISMOC_EP_CMD_IN (0x81 | FPI_USB_ENDPOINT_IN) #define EGISMOC_EP_CMD_INTERRUPT_IN 0x83 @@ -40,7 +44,7 @@ G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC, #define EGISMOC_USB_CONTROL_TIMEOUT 5000 #define EGISMOC_USB_SEND_TIMEOUT 5000 #define EGISMOC_USB_RECV_TIMEOUT 5000 -#define EGISMOC_USB_INTERRUPT_TIMEOUT 0 +#define EGISMOC_USB_INTERRUPT_TIMEOUT 60000 #define EGISMOC_USB_IN_RECV_LENGTH 4096 #define EGISMOC_USB_INTERRUPT_IN_RECV_LENGTH 64 @@ -91,12 +95,10 @@ static gsize cmd_sensor_check_len = sizeof (cmd_sensor_check) / sizeof (cmd_sens static guchar cmd_sensor_identify[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x17, 0x01, 0x01}; static gsize cmd_sensor_identify_len = sizeof (cmd_sensor_identify) / sizeof (cmd_sensor_identify[0]); -static guchar rsp_identify_match_prefix[] = {0x00, 0x00, 0x00, 0x42}; -static gsize rsp_identify_match_prefix_len = sizeof (rsp_identify_match_prefix) / sizeof (rsp_identify_match_prefix[0]); static guchar rsp_identify_match_suffix[] = {0x90, 0x00}; static gsize rsp_identify_match_suffix_len = sizeof (rsp_identify_match_suffix) / sizeof (rsp_identify_match_suffix[0]); -static guchar rsp_identify_notmatch_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x90, 0x04}; -static gsize rsp_identify_notmatch_prefix_len = sizeof (rsp_identify_notmatch_prefix) / sizeof (rsp_identify_notmatch_prefix[0]); +static guchar rsp_identify_notmatch_suffix[] = {0x90, 0x04}; +static gsize rsp_identify_notmatch_suffix_len = sizeof (rsp_identify_notmatch_suffix) / sizeof (rsp_identify_notmatch_suffix[0]); static guchar cmd_sensor_enroll[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x17, 0x01, 0x00}; static gsize cmd_sensor_enroll_len = sizeof (cmd_sensor_enroll) / sizeof (cmd_sensor_enroll[0]); @@ -151,12 +153,14 @@ static gsize cmd_delete_prefix_len = sizeof (cmd_delete_prefix) / sizeof (cmd_de static guchar rsp_delete_success_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x90, 0x00}; static gsize rsp_delete_success_prefix_len = sizeof (rsp_delete_success_prefix) / sizeof (rsp_delete_success_prefix[0]); -static guchar cmd_check_prefix[] = {0x50, 0x17, 0x03, 0x00, 0x00}; -static gsize cmd_check_prefix_len = sizeof (cmd_check_prefix) / sizeof (cmd_check_prefix[0]); +static guchar cmd_check_prefix_type1[] = {0x50, 0x17, 0x03, 0x00, 0x00}; +static gsize cmd_check_prefix_type1_len = sizeof (cmd_check_prefix_type1) / sizeof (cmd_check_prefix_type1[0]); +static guchar cmd_check_prefix_type2[] = {0x50, 0x17, 0x03, 0x80, 0x00}; +static gsize cmd_check_prefix_type2_len = sizeof (cmd_check_prefix_type2) / sizeof (cmd_check_prefix_type2[0]); static guchar cmd_check_suffix[] = {0x00, 0x40}; static gsize cmd_check_suffix_len = sizeof (cmd_check_suffix) / sizeof (cmd_check_suffix[0]); -static guchar rsp_check_not_yet_enrolled_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x90}; -static gsize rsp_check_not_yet_enrolled_prefix_len = sizeof (rsp_check_not_yet_enrolled_prefix) / sizeof (rsp_check_not_yet_enrolled_prefix[0]); +static guchar rsp_check_not_yet_enrolled_suffix[] = {0x90, 0x04}; +static gsize rsp_check_not_yet_enrolled_suffix_len = sizeof (rsp_check_not_yet_enrolled_suffix) / sizeof (rsp_check_not_yet_enrolled_suffix[0]); /* SSM task states and various status enums */ From 89ab54794e9857c428b0d718e4efba2d505ce3f0 Mon Sep 17 00:00:00 2001 From: Joshua Grisham <18266314+joshuagrisham@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:39:32 +0100 Subject: [PATCH 37/64] egismoc: added test and capture for 05a1 variant --- tests/egismoc-05a1/custom.pcapng | Bin 0 -> 62756 bytes tests/egismoc-05a1/custom.py | 156 ++++++++++++++++++ tests/egismoc-05a1/device | 262 +++++++++++++++++++++++++++++++ tests/meson.build | 1 + 4 files changed, 419 insertions(+) create mode 100644 tests/egismoc-05a1/custom.pcapng create mode 100755 tests/egismoc-05a1/custom.py create mode 100644 tests/egismoc-05a1/device diff --git a/tests/egismoc-05a1/custom.pcapng b/tests/egismoc-05a1/custom.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..4821c01222b66340a8066289ebc3f7aaf69b2298 GIT binary patch literal 62756 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 F{vW14HAw&f literal 0 HcmV?d00001 diff --git a/tests/egismoc-05a1/custom.py b/tests/egismoc-05a1/custom.py new file mode 100755 index 00000000..3a662380 --- /dev/null +++ b/tests/egismoc-05a1/custom.py @@ -0,0 +1,156 @@ +#!/usr/bin/python3 + +import traceback +import sys +import time +import gi + +gi.require_version('FPrint', '2.0') +from gi.repository import FPrint, GLib + +# Exit with error on any exception, included those happening in async callbacks +sys.excepthook = lambda *args: (traceback.print_exception(*args), sys.exit(1)) + +ctx = GLib.main_context_default() + +c = FPrint.Context() +c.enumerate() +devices = c.get_devices() + +d = devices[0] +del devices + +d.open_sync() + +assert d.get_driver() == "egismoc" +assert not d.has_feature(FPrint.DeviceFeature.CAPTURE) +assert d.has_feature(FPrint.DeviceFeature.IDENTIFY) +assert d.has_feature(FPrint.DeviceFeature.VERIFY) +assert d.has_feature(FPrint.DeviceFeature.DUPLICATES_CHECK) +assert d.has_feature(FPrint.DeviceFeature.STORAGE) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_LIST) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_DELETE) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR) + +def enroll_progress(*args): + print("finger status: ", d.get_finger_status()) + print('enroll progress: ' + str(args)) + +def identify_done(dev, res): + global identified + identified = True + identify_match, identify_print = dev.identify_finish(res) + print('indentification_done: ', identify_match, identify_print) + assert identify_match.equal(identify_print) + +# Beginning with list and clear assumes you begin with >0 prints enrolled before capturing + +print("listing - device should have prints") +stored = d.list_prints_sync() +assert len(stored) > 0 +del stored + +print("clear device storage") +d.clear_storage_sync() +print("clear done") + +print("listing - device should be empty") +stored = d.list_prints_sync() +assert len(stored) == 0 +del stored + +print("enrolling") +template = FPrint.Print.new(d) +template.set_finger(FPrint.Finger.LEFT_INDEX) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +p1 = d.enroll_sync(template, None, enroll_progress, None) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("enroll done") +del template + +print("listing - device should have 1 print") +stored = d.list_prints_sync() +assert len(stored) == 1 +assert stored[0].equal(p1) + +print("verifying") +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +verify_res, verify_print = d.verify_sync(p1) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("verify done") +assert verify_res == True + +identified = False +deserialized_prints = [] +for p in stored: + deserialized_prints.append(FPrint.Print.deserialize(p.serialize())) + assert deserialized_prints[-1].equal(p) +del stored + +print('async identifying') +d.identify(deserialized_prints, callback=identify_done) +del deserialized_prints + +while not identified: + ctx.iteration(True) + +print("try to enroll duplicate") +template = FPrint.Print.new(d) +template.set_finger(FPrint.Finger.RIGHT_INDEX) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +try: + d.enroll_sync(template, None, enroll_progress, None) +except GLib.Error as error: + assert error.matches(FPrint.DeviceError.quark(), + FPrint.DeviceError.DATA_DUPLICATE) +except Exception as exc: + raise +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("duplicate enroll attempt done") + +print("listing - device should still only have 1 print") +stored = d.list_prints_sync() +assert len(stored) == 1 +assert stored[0].equal(p1) +del stored + +print("enroll new finger") +template = FPrint.Print.new(d) +template.set_finger(FPrint.Finger.RIGHT_INDEX) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +p2 = d.enroll_sync(template, None, enroll_progress, None) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("enroll new finger done") +del template + +print("listing - device should have 2 prints") +stored = d.list_prints_sync() +assert len(stored) == 2 +assert (stored[0].equal(p1) and stored[1].equal(p2)) or (stored[0].equal(p2) and stored[1].equal(p1)) +del stored + +print("deleting first print") +d.delete_print_sync(p1) +print("delete done") +del p1 + +print("listing - device should only have second print") +stored = d.list_prints_sync() +assert len(stored) == 1 +assert stored[0].equal(p2) +del stored +del p2 + +print("clear device storage") +d.clear_storage_sync() +print("clear done") + +print("listing - device should be empty") +stored = d.list_prints_sync() +assert len(stored) == 0 +del stored + +d.close_sync() + +del d +del c diff --git a/tests/egismoc-05a1/device b/tests/egismoc-05a1/device new file mode 100644 index 00000000..49f1e9ea --- /dev/null +++ b/tests/egismoc-05a1/device @@ -0,0 +1,262 @@ +P: /devices/pci0000:00/0000:00:14.0/usb1/1-5 +N: bus/usb/001/003=12010002FF0000407A1CA10513120102030109022700010100A0320904000003FF000000070581020002000705020200020007058303400005 +E: BUSNUM=001 +E: DEVNAME=/dev/bus/usb/001/003 +E: DEVNUM=003 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: ID_BUS=usb +E: ID_MODEL=ETU905A80-E +E: ID_MODEL_ENC=ETU905A80-E +E: ID_MODEL_ID=05a1 +E: ID_REVISION=1213 +E: ID_SERIAL=EGIS_ETU905A80-E_0C5A44PCU833 +E: ID_SERIAL_SHORT=0C5A44PCU833 +E: ID_USB_INTERFACES=:ff0000: +E: ID_USB_MODEL=ETU905A80-E +E: ID_USB_MODEL_ENC=ETU905A80-E +E: ID_USB_MODEL_ID=05a1 +E: ID_USB_REVISION=1213 +E: ID_USB_SERIAL=EGIS_ETU905A80-E_0C5A44PCU833 +E: ID_USB_SERIAL_SHORT=0C5A44PCU833 +E: ID_USB_VENDOR=EGIS +E: ID_USB_VENDOR_ENC=EGIS +E: ID_USB_VENDOR_ID=1c7a +E: ID_VENDOR=EGIS +E: ID_VENDOR_ENC=EGIS +E: ID_VENDOR_FROM_DATABASE=LighTuning Technology Inc. +E: ID_VENDOR_ID=1c7a +E: MAJOR=189 +E: MINOR=2 +E: PRODUCT=1c7a/5a1/1213 +E: SUBSYSTEM=usb +E: TYPE=255/0/0 +A: authorized=1\n +A: avoid_reset_quirk=0\n +A: bConfigurationValue=1\n +A: bDeviceClass=ff\n +A: bDeviceProtocol=00\n +A: bDeviceSubClass=00\n +A: bMaxPacketSize0=64\n +A: bMaxPower=100mA\n +A: bNumConfigurations=1\n +A: bNumInterfaces= 1\n +A: bcdDevice=1213\n +A: bmAttributes=a0\n +A: busnum=1\n +A: configuration= +H: descriptors=12010002FF0000407A1CA10513120102030109022700010100A0320904000003FF000000070581020002000705020200020007058303400005 +A: dev=189:2\n +A: devnum=3\n +A: devpath=5\n +L: driver=../../../../../bus/usb/drivers/usb +L: firmware_node=../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:51/device:52/device:57 +A: idProduct=05a1\n +A: idVendor=1c7a\n +A: ltm_capable=no\n +A: manufacturer=EGIS\n +A: maxchild=0\n +A: physical_location/dock=no\n +A: physical_location/horizontal_position=center\n +A: physical_location/lid=no\n +A: physical_location/panel=unknown\n +A: physical_location/vertical_position=center\n +L: port=../1-0:1.0/usb1-port5 +A: power/active_duration=955612\n +A: power/async=enabled\n +A: power/autosuspend=2\n +A: power/autosuspend_delay_ms=2000\n +A: power/connected_duration=955612\n +A: power/control=on\n +A: power/level=on\n +A: power/persist=0\n +A: power/runtime_active_kids=0\n +A: power/runtime_active_time=955338\n +A: power/runtime_enabled=forbidden\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=0\n +A: power/runtime_usage=1\n +A: power/wakeup=disabled\n +A: power/wakeup_abort_count=\n +A: power/wakeup_active=\n +A: power/wakeup_active_count=\n +A: power/wakeup_count=\n +A: power/wakeup_expire_count=\n +A: power/wakeup_last_time_ms=\n +A: power/wakeup_max_time_ms=\n +A: power/wakeup_total_time_ms=\n +A: product=ETU905A80-E\n +A: quirks=0x0\n +A: removable=fixed\n +A: rx_lanes=1\n +A: serial=0C5A44PCU833\n +A: speed=480\n +A: tx_lanes=1\n +A: urbnum=491\n +A: version= 2.00\n + +P: /devices/pci0000:00/0000:00:14.0/usb1 +N: bus/usb/001/001=12010002090001406B1D020005060302010109021900010100E0000904000001090000000705810304000C +E: BUSNUM=001 +E: CURRENT_TAGS=:seat: +E: DEVNAME=/dev/bus/usb/001/001 +E: DEVNUM=001 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: ID_AUTOSUSPEND=1 +E: ID_BUS=usb +E: ID_FOR_SEAT=usb-pci-0000_00_14_0 +E: ID_MODEL=xHCI_Host_Controller +E: ID_MODEL_ENC=xHCI\x20Host\x20Controller +E: ID_MODEL_FROM_DATABASE=2.0 root hub +E: ID_MODEL_ID=0002 +E: ID_PATH=pci-0000:00:14.0 +E: ID_PATH_TAG=pci-0000_00_14_0 +E: ID_REVISION=0605 +E: ID_SERIAL=Linux_6.5.0-9-generic_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 +E: ID_SERIAL_SHORT=0000:00:14.0 +E: ID_USB_INTERFACES=:090000: +E: ID_USB_MODEL=xHCI_Host_Controller +E: ID_USB_MODEL_ENC=xHCI\x20Host\x20Controller +E: ID_USB_MODEL_ID=0002 +E: ID_USB_REVISION=0605 +E: ID_USB_SERIAL=Linux_6.5.0-9-generic_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 +E: ID_USB_SERIAL_SHORT=0000:00:14.0 +E: ID_USB_VENDOR=Linux_6.5.0-9-generic_xhci-hcd +E: ID_USB_VENDOR_ENC=Linux\x206.5.0-9-generic\x20xhci-hcd +E: ID_USB_VENDOR_ID=1d6b +E: ID_VENDOR=Linux_6.5.0-9-generic_xhci-hcd +E: ID_VENDOR_ENC=Linux\x206.5.0-9-generic\x20xhci-hcd +E: ID_VENDOR_FROM_DATABASE=Linux Foundation +E: ID_VENDOR_ID=1d6b +E: MAJOR=189 +E: MINOR=0 +E: PRODUCT=1d6b/2/605 +E: SUBSYSTEM=usb +E: TAGS=:seat: +E: TYPE=9/0/1 +A: authorized=1\n +A: authorized_default=1\n +A: avoid_reset_quirk=0\n +A: bConfigurationValue=1\n +A: bDeviceClass=09\n +A: bDeviceProtocol=01\n +A: bDeviceSubClass=00\n +A: bMaxPacketSize0=64\n +A: bMaxPower=0mA\n +A: bNumConfigurations=1\n +A: bNumInterfaces= 1\n +A: bcdDevice=0605\n +A: bmAttributes=e0\n +A: busnum=1\n +A: configuration= +H: descriptors=12010002090001406B1D020005060302010109021900010100E0000904000001090000000705810304000C +A: dev=189:0\n +A: devnum=1\n +A: devpath=0\n +L: driver=../../../../bus/usb/drivers/usb +L: firmware_node=../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:51/device:52 +A: idProduct=0002\n +A: idVendor=1d6b\n +A: interface_authorized_default=1\n +A: ltm_capable=no\n +A: manufacturer=Linux 6.5.0-9-generic xhci-hcd\n +A: maxchild=12\n +A: power/active_duration=956044\n +A: power/async=enabled\n +A: power/autosuspend=0\n +A: power/autosuspend_delay_ms=0\n +A: power/connected_duration=956044\n +A: power/control=auto\n +A: power/level=auto\n +A: power/runtime_active_kids=2\n +A: power/runtime_active_time=956041\n +A: power/runtime_enabled=enabled\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=0\n +A: power/runtime_usage=0\n +A: power/wakeup=disabled\n +A: power/wakeup_abort_count=\n +A: power/wakeup_active=\n +A: power/wakeup_active_count=\n +A: power/wakeup_count=\n +A: power/wakeup_expire_count=\n +A: power/wakeup_last_time_ms=\n +A: power/wakeup_max_time_ms=\n +A: power/wakeup_total_time_ms=\n +A: product=xHCI Host Controller\n +A: quirks=0x0\n +A: removable=unknown\n +A: rx_lanes=1\n +A: serial=0000:00:14.0\n +A: speed=480\n +A: tx_lanes=1\n +A: urbnum=181\n +A: version= 2.00\n + +P: /devices/pci0000:00/0000:00:14.0 +E: DRIVER=xhci_hcd +E: ID_MODEL_FROM_DATABASE=Alder Lake PCH USB 3.2 xHCI Host Controller +E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller +E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI +E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller +E: ID_VENDOR_FROM_DATABASE=Intel Corporation +E: MODALIAS=pci:v00008086d000051EDsv0000144Dsd0000C1CAbc0Csc03i30 +E: PCI_CLASS=C0330 +E: PCI_ID=8086:51ED +E: PCI_SLOT_NAME=0000:00:14.0 +E: PCI_SUBSYS_ID=144D:C1CA +E: SUBSYSTEM=pci +A: ari_enabled=0\n +A: broken_parity_status=0\n +A: class=0x0c0330\n +H: config=8680ED51060490020130030C0000800004001A3E6000000000000000000000000000000000000000000000004D14CAC1000000007000000000000000FF010000FD0134A089C27F8000000000000000003F6DD80F000000000000000000000000316000000000000000000000000000000180C2C108000000000000000000000005908700D804E0FE000000000000000009B014F01000400100000000C10A080000080E00001800008F50020000010000090000018680C00009001014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B50F110112000000 +A: consistent_dma_mask_bits=64\n +A: d3cold_allowed=1\n +A: dbc=disabled\n +A: dbc_bInterfaceProtocol=01\n +A: dbc_bcdDevice=0010\n +A: dbc_idProduct=0010\n +A: dbc_idVendor=1d6b\n +A: device=0x51ed\n +A: dma_mask_bits=64\n +L: driver=../../../bus/pci/drivers/xhci_hcd +A: driver_override=(null)\n +A: enable=1\n +L: firmware_node=../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:51 +A: index=9\n +L: iommu=../../virtual/iommu/dmar2 +L: iommu_group=../../../kernel/iommu_groups/11 +A: irq=142\n +A: label=Onboard - Other\n +A: local_cpulist=0-15\n +A: local_cpus=ffff\n +A: modalias=pci:v00008086d000051EDsv0000144Dsd0000C1CAbc0Csc03i30\n +A: msi_bus=1\n +A: msi_irqs/142=msi\n +A: numa_node=-1\n +A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 6 7 2112 7\nxHCI ring segments 28 28 4096 28\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 1 32 128 1\nbuffer-32 0 0 32 0\n +A: power/async=enabled\n +A: power/control=auto\n +A: power/runtime_active_kids=1\n +A: power/runtime_active_time=957198\n +A: power/runtime_enabled=enabled\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=0\n +A: power/runtime_usage=0\n +A: power/wakeup=enabled\n +A: power/wakeup_abort_count=0\n +A: power/wakeup_active=0\n +A: power/wakeup_active_count=0\n +A: power/wakeup_count=0\n +A: power/wakeup_expire_count=0\n +A: power/wakeup_last_time_ms=0\n +A: power/wakeup_max_time_ms=0\n +A: power/wakeup_total_time_ms=0\n +A: power_state=D0\n +A: resource=0x000000603e1a0000 0x000000603e1affff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n +A: revision=0x01\n +A: subsystem_device=0xc1ca\n +A: subsystem_vendor=0x144d\n +A: vendor=0x8086\n + diff --git a/tests/meson.build b/tests/meson.build index c919ec6e..efa573fb 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -52,6 +52,7 @@ drivers_tests = [ 'nb1010', 'egis0570', 'egismoc', + 'egismoc-05a1', 'fpcmoc', 'realtek', 'focaltech_moc', From 7476faba689d9f7e70ebd82ccd75dd319798e4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 13 Feb 2024 15:08:50 +0100 Subject: [PATCH 38/64] data, libfprint: Update list of unsupported drivers --- data/autosuspend.hwdb | 8 ++++++-- libfprint/fprint-list-udev-hwdb.c | 2 -- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 215a96b3..74ac65b0 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -77,6 +77,12 @@ usb:v1C7Ap0571* ID_AUTOSUSPEND=1 ID_PERSIST=0 +# Supported by libfprint driver egismoc +usb:v1C7Ap0582* +usb:v1C7Ap05A1* + ID_AUTOSUSPEND=1 + ID_PERSIST=0 + # Supported by libfprint driver elan usb:v04F3p0903* usb:v04F3p0907* @@ -372,8 +378,6 @@ usb:v1C7Ap0300* usb:v1C7Ap0575* usb:v1C7Ap0576* usb:v1C7Ap0577* -usb:v1C7Ap0582* -usb:v1C7Ap05A1* usb:v27C6p5042* usb:v27C6p5110* usb:v27C6p5117* diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c index 28530a06..bace9f93 100644 --- a/libfprint/fprint-list-udev-hwdb.c +++ b/libfprint/fprint-list-udev-hwdb.c @@ -101,8 +101,6 @@ static const FpIdEntry allowlist_id_table[] = { { .vid = 0x1c7a, .pid = 0x0575 }, { .vid = 0x1c7a, .pid = 0x0576 }, { .vid = 0x1c7a, .pid = 0x0577 }, - { .vid = 0x1c7a, .pid = 0x0582 }, - { .vid = 0x1c7a, .pid = 0x05a1 }, { .vid = 0x27c6, .pid = 0x5042 }, { .vid = 0x27c6, .pid = 0x5110 }, { .vid = 0x27c6, .pid = 0x5117 }, From b0f0322726fecf6dece6216ca1b2b2ea60bfee7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:31:11 +0100 Subject: [PATCH 39/64] egismoc: Indentation and syntax fixes --- libfprint/drivers/egismoc/egismoc.c | 316 ++++++++++++++++++---------- libfprint/drivers/egismoc/egismoc.h | 13 -- 2 files changed, 202 insertions(+), 127 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index dfae716b..f4ed08b0 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -35,6 +35,18 @@ #include "egismoc.h" +struct _FpiDeviceEgisMoc +{ + FpDevice parent; + FpiSsm *task_ssm; + FpiSsm *cmd_ssm; + FpiUsbTransfer *cmd_transfer; + GCancellable *interrupt_cancellable; + + int enrolled_num; + GPtrArray *enrolled_ids; +}; + G_DEFINE_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FP_TYPE_DEVICE); static const FpIdEntry egismoc_id_table[] = { @@ -84,9 +96,11 @@ egismoc_wait_finger_on_sensor (FpiSsm *ssm, g_autoptr(FpiUsbTransfer) transfer = fpi_usb_transfer_new (device); - fpi_usb_transfer_fill_interrupt (transfer, EGISMOC_EP_CMD_INTERRUPT_IN, EGISMOC_USB_INTERRUPT_IN_RECV_LENGTH); + fpi_usb_transfer_fill_interrupt (transfer, EGISMOC_EP_CMD_INTERRUPT_IN, + EGISMOC_USB_INTERRUPT_IN_RECV_LENGTH); transfer->ssm = ssm; - transfer->short_is_error = FALSE; /* Interrupt on this device always returns 1 byte short; this is expected */ + /* Interrupt on this device always returns 1 byte short; this is expected */ + transfer->short_is_error = FALSE; fpi_device_report_finger_status (device, FP_FINGER_STATUS_NEEDED); @@ -103,7 +117,9 @@ egismoc_validate_response_prefix (const guchar *buffer_in, const guchar *valid_prefix, const gsize valid_prefix_len) { - const gboolean result = memcmp (buffer_in + (egismoc_read_prefix_len + EGISMOC_CHECK_BYTES_LENGTH), + const gboolean result = memcmp (buffer_in + + (egismoc_read_prefix_len + + EGISMOC_CHECK_BYTES_LENGTH), valid_prefix, valid_prefix_len) == 0; @@ -166,9 +182,10 @@ egismoc_cmd_receive_cb (FpiUsbTransfer *transfer, gpointer userdata, GError *error) { - fp_dbg ("Command receive callback"); CommandData *data = userdata; + fp_dbg ("Command receive callback"); + if (error) { fpi_ssm_mark_failed (transfer->ssm, error); @@ -206,17 +223,17 @@ egismoc_cmd_run_state (FpiSsm *ssm, NULL, fpi_ssm_usb_transfer_cb, NULL); + break; } - else - { - fpi_ssm_next_state (ssm); - } + + fpi_ssm_next_state (ssm); break; case CMD_GET: transfer = fpi_usb_transfer_new (device); transfer->ssm = ssm; - fpi_usb_transfer_fill_bulk (transfer, EGISMOC_EP_CMD_IN, EGISMOC_USB_IN_RECV_LENGTH); + fpi_usb_transfer_fill_bulk (transfer, EGISMOC_EP_CMD_IN, + EGISMOC_USB_IN_RECV_LENGTH); fpi_usb_transfer_submit (g_steal_pointer (&transfer), EGISMOC_USB_RECV_TIMEOUT, NULL, @@ -253,9 +270,9 @@ typedef union egismoc_check_bytes } EgisMocCheckBytes; /* - Derive the 2 "check bytes" for write payloads - 32-bit big-endian sum of all 16-bit words (including check bytes) MOD 0xFFFF should be 0, otherwise - the device will reject the payload + * Derive the 2 "check bytes" for write payloads + * 32-bit big-endian sum of all 16-bit words (including check bytes) MOD 0xFFFF + * should be 0, otherwise the device will reject the payload */ static EgisMocCheckBytes egismoc_get_check_bytes (const guchar *value, @@ -279,7 +296,7 @@ egismoc_get_check_bytes (const guchar *value, sum_shorts += value_bigendian_shorts[i]; /* - derive the "first possible occurence" of check bytes as: + derive the "first possible occurrence" of check bytes as: `0xFFFF - (sum_of_32bit_words % 0xFFFF) */ check_bytes.check_short = 0xffff - (sum_shorts % 0xffff); @@ -310,10 +327,12 @@ egismoc_exec_cmd (FpDevice *device, transfer->short_is_error = TRUE; /* - buffer_out should be a fully composed command (with prefix, check bytes, etc) which looks like this - E G I S 00 00 00 01 {cb1} {cb2} {payload} - where cb1 and cb2 are some check bytes generated by the egismoc_get_check_bytes method - and payload is what is passed via the cmd parameter + * buffer_out should be a fully composed command (with prefix, check bytes, etc) + * which looks like this: + * E G I S 00 00 00 01 {cb1} {cb2} {payload} + * where cb1 and cb2 are some check bytes generated by the + * egismoc_get_check_bytes() method and payload is what is passed via the cmd + * parameter */ buffer_out_length = egismoc_write_prefix_len + EGISMOC_CHECK_BYTES_LENGTH @@ -323,18 +342,22 @@ egismoc_exec_cmd (FpDevice *device, /* Prefix */ memcpy (buffer_out, egismoc_write_prefix, egismoc_write_prefix_len); - /* Check Bytes - leave them as 00 for now then later generate and copy over the real ones */ + /* Check Bytes - leave them as 00 for now then later generate and copy over + * the real ones */ /* Command Payload */ - memcpy (buffer_out + egismoc_write_prefix_len + EGISMOC_CHECK_BYTES_LENGTH, cmd, cmd_length); + memcpy (buffer_out + egismoc_write_prefix_len + EGISMOC_CHECK_BYTES_LENGTH, + cmd, cmd_length); /* destroy cmd if requested */ if (cmd_destroy) cmd_destroy (cmd); - /* Now fetch and set the "real" check bytes based on the currently assembled payload */ + /* Now fetch and set the "real" check bytes based on the currently + * assembled payload */ check_bytes = egismoc_get_check_bytes (buffer_out, buffer_out_length); - memcpy (buffer_out + egismoc_write_prefix_len, check_bytes.check_bytes, EGISMOC_CHECK_BYTES_LENGTH); + memcpy (buffer_out + egismoc_write_prefix_len, check_bytes.check_bytes, + EGISMOC_CHECK_BYTES_LENGTH); fpi_usb_transfer_fill_bulk_full (transfer, EGISMOC_EP_CMD_OUT, @@ -423,9 +446,9 @@ egismoc_list_fill_enrolled_ids_cb (FpDevice *device, self->enrolled_num = 0; /* - Each fingerprint ID will be returned in this response as a 32 byte array - The other stuff in the payload is 16 bytes long, so if there is at least 1 print - then the length should be at least 16+32=48 bytes long + * Each fingerprint ID will be returned in this response as a 32 byte array + * The other stuff in the payload is 16 bytes long, so if there is at least 1 + * print then the length should be at least 16+32=48 bytes long */ for (int pos = EGISMOC_LIST_RESPONSE_PREFIX_SIZE; pos < length_in - EGISMOC_LIST_RESPONSE_SUFFIX_SIZE; @@ -433,11 +456,13 @@ egismoc_list_fill_enrolled_ids_cb (FpDevice *device, { g_autofree guchar *print_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE); memcpy (print_id, buffer_in + pos, EGISMOC_FINGERPRINT_DATA_SIZE); - fp_dbg ("Device fingerprint %0d: %.*s%c", self->enrolled_num, EGISMOC_FINGERPRINT_DATA_SIZE, print_id, '\0'); + fp_dbg ("Device fingerprint %0d: %.*s", self->enrolled_num, + EGISMOC_FINGERPRINT_DATA_SIZE, print_id); g_ptr_array_add (self->enrolled_ids, g_steal_pointer (&print_id)); } - fp_info ("Number of currently enrolled fingerprints on the device is %d", self->enrolled_num); + fp_info ("Number of currently enrolled fingerprints on the device is %d", + self->enrolled_num); if (self->task_ssm) fpi_ssm_next_state (self->task_ssm); @@ -452,7 +477,8 @@ egismoc_list_run_state (FpiSsm *ssm, switch (fpi_ssm_get_cur_state (ssm)) { case LIST_GET_ENROLLED_IDS: - egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, egismoc_list_fill_enrolled_ids_cb); + egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, + egismoc_list_fill_enrolled_ids_cb); break; case LIST_RETURN_ENROLLED_PRINTS: @@ -482,30 +508,35 @@ egismoc_get_delete_cmd (FpDevice *device, { fp_dbg ("Get delete command"); FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); - g_autofree const gchar *print_description = NULL; - g_autoptr(GVariant) print_data = NULL; g_autoptr(GVariant) print_data_id_var = NULL; const guchar *print_data_id = NULL; gsize print_data_id_len = 0; + g_autofree gchar *print_description = NULL; g_autofree guchar *enrolled_print_id = NULL; - guchar *result = NULL; + g_autofree guchar *result = NULL; gsize pos = 0; /* - The final command body should contain: - 1) hard-coded 00 00 - 2) 2-byte size indiciator, 20*Number deleted identifiers plus 7 in form of: num_to_delete * 0x20 + 0x07 - Since max prints can be higher than 7 then this goes up to 2 bytes (e9 + 9 = 109) - 3) Hard-coded prefix (cmd_delete_prefix) - 4) 2-byte size indiciator, 20*Number of enrolled identifiers without plus 7 (num_to_delete * 0x20) - 5) All of the currently registered prints to delete in their 32-byte device identifiers (enrolled_list) + * The final command body should contain: + * 1) hard-coded 00 00 + * 2) 2-byte size indiciator, 20*Number deleted identifiers plus 7 in form of: + * num_to_delete * 0x20 + 0x07 + * Since max prints can be higher than 7 then this goes up to 2 bytes + * (e9 + 9 = 109) + * 3) Hard-coded prefix (cmd_delete_prefix) + * 4) 2-byte size indiciator, 20*Number of enrolled identifiers without plus 7 + * (num_to_delete * 0x20) + * 5) All of the currently registered prints to delete in their 32-byte device + * identifiers (enrolled_list) */ const int num_to_delete = (!delete_print) ? self->enrolled_num : 1; - const gsize body_length = sizeof (guchar) * EGISMOC_FINGERPRINT_DATA_SIZE * num_to_delete; + const gsize body_length = sizeof (guchar) * EGISMOC_FINGERPRINT_DATA_SIZE * + num_to_delete; /* total_length is the 6 various bytes plus prefix and body payload */ - const gsize total_length = (sizeof (guchar) * 6) + cmd_delete_prefix_len + body_length; + const gsize total_length = (sizeof (guchar) * 6) + cmd_delete_prefix_len + + body_length; /* pre-fill entire payload with 00s */ result = g_malloc0 (total_length); @@ -514,8 +545,10 @@ egismoc_get_delete_cmd (FpDevice *device, pos = 2; /* Size Counter bytes */ - /* "easiest" way to handle 2-bytes size for counter is to hard-code logic for when we go to the 2nd byte */ - /* note this will not work in case any model ever supports more than 14 prints (assumed max is 10) */ + /* "easiest" way to handle 2-bytes size for counter is to hard-code logic for + * when we go to the 2nd byte + * note this will not work in case any model ever supports more than 14 prints + * (assumed max is 10) */ if (num_to_delete > 7) { memset (result + pos, 0x01, sizeof (guchar)); @@ -560,15 +593,18 @@ egismoc_get_delete_cmd (FpDevice *device, if (!g_variant_check_format_string (print_data, "(@ay)", FALSE)) { - fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); return NULL; } g_variant_get (print_data, "(@ay)", &print_data_id_var); - print_data_id = g_variant_get_fixed_array (print_data_id_var, &print_data_id_len, sizeof (guchar)); + print_data_id = g_variant_get_fixed_array (print_data_id_var, + &print_data_id_len, sizeof (guchar)); if (!g_str_has_prefix (print_description, "FP")) - fp_dbg ("Fingerprint '%s' was not created by libfprint; deleting anyway.", print_description); + fp_dbg ("Fingerprint '%s' was not created by libfprint; deleting anyway.", + print_description); fp_info ("Delete fingerprint %s (%s)", print_description, print_data_id); @@ -623,14 +659,16 @@ egismoc_delete_cb (FpDevice *device, } else { - fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Unsupported delete action.")); + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Unsupported delete action.")); } } else { - fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Delete print was not successfull")); + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Delete print was not successful")); } } @@ -644,17 +682,22 @@ egismoc_delete_run_state (FpiSsm *ssm, switch (fpi_ssm_get_cur_state (ssm)) { case DELETE_GET_ENROLLED_IDS: - /* get enrolled_ids and enrolled_num from device for use building delete payload below */ - egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, egismoc_list_fill_enrolled_ids_cb); + /* get enrolled_ids and enrolled_num from device for use building + * delete payload below + */ + egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, + egismoc_list_fill_enrolled_ids_cb); break; case DELETE_DELETE: if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_DELETE) - payload = egismoc_get_delete_cmd (device, fpi_ssm_get_data (ssm), &payload_length); + payload = egismoc_get_delete_cmd (device, fpi_ssm_get_data (ssm), + &payload_length); else payload = egismoc_get_delete_cmd (device, NULL, &payload_length); - egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, g_free, egismoc_delete_cb); + egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, + g_free, egismoc_delete_cb); break; } } @@ -725,7 +768,8 @@ egismoc_enroll_status_report (FpDevice *device, fpi_ssm_mark_failed (self->task_ssm, error); else fpi_ssm_mark_failed (self->task_ssm, - fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "Unknown error")); + fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Unknown error")); } } @@ -755,7 +799,8 @@ egismoc_read_capture_cb (FpDevice *device, rsp_read_success_suffix, rsp_read_success_suffix_len)) { - egismoc_enroll_status_report (device, enroll_print, ENROLL_STATUS_PARTIAL_OK, NULL); + egismoc_enroll_status_report (device, enroll_print, + ENROLL_STATUS_PARTIAL_OK, NULL); } else { @@ -778,11 +823,13 @@ egismoc_read_capture_cb (FpDevice *device, rsp_read_dirty_prefix, rsp_read_dirty_prefix_len)) error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER, - "Your device is having trouble recognizing you. Make sure your sensor is clean."); + "Your device is having trouble recognizing you. " + "Make sure your sensor is clean."); else error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER, - "Unknown failure trying to read your finger. Please try again."); + "Unknown failure trying to read your finger. " + "Please try again."); egismoc_enroll_status_report (device, enroll_print, ENROLL_STATUS_RETRY, error); } @@ -820,8 +867,9 @@ egismoc_enroll_check_cb (FpDevice *device, } /* - Builds the full "check" payload which includes identifiers for all fingerprints which currently - should exist on the storage. This payload is used during both enrollment and verify actions. + * Builds the full "check" payload which includes identifiers for all + * fingerprints which currently should exist on the storage. This payload is + * used during both enrollment and verify actions. */ static guchar * egismoc_get_check_cmd (FpDevice *device, @@ -829,29 +877,36 @@ egismoc_get_check_cmd (FpDevice *device, { fp_dbg ("Get check command"); FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); - guchar *device_print_id = NULL; - guchar *result = NULL; + g_autofree guchar *result = NULL; gsize pos = 0; /* - The final command body should contain: - 1) hard-coded 00 00 - 2) 2-byte size indiciator, 20*Number enrolled identifiers plus 9 in form of: (enrolled_num + 1) * 0x20 + 0x09 - Since max prints can be higher than 7 then this goes up to 2 bytes (e9 + 9 = 109) - 3) Hard-coded prefix (cmd_check_prefix) - 4) 2-byte size indiciator, 20*Number of enrolled identifiers without plus 9 ((enrolled_num + 1) * 0x20) - 5) Hard-coded 32 * 0x00 bytes - 6) All of the currently registered prints in their 32-byte device identifiers (enrolled_list) - 7) Hard-coded suffix (cmd_check_suffix) + * The final command body should contain: + * 1) hard-coded 00 00 + * 2) 2-byte size indiciator, 20*Number enrolled identifiers plus 9 in form of: + * (enrolled_num + 1) * 0x20 + 0x09 + * Since max prints can be higher than 7 then this goes up to 2 bytes + * (e9 + 9 = 109) + * 3) Hard-coded prefix (cmd_check_prefix) + * 4) 2-byte size indiciator, 20*Number of enrolled identifiers without plus 9 + * ((enrolled_num + 1) * 0x20) + * 5) Hard-coded 32 * 0x00 bytes + * 6) All of the currently registered prints in their 32-byte device identifiers + * (enrolled_list) + * 7) Hard-coded suffix (cmd_check_suffix) */ - const gsize body_length = sizeof (guchar) * self->enrolled_num * EGISMOC_FINGERPRINT_DATA_SIZE; + const gsize body_length = sizeof (guchar) * self->enrolled_num * + EGISMOC_FINGERPRINT_DATA_SIZE; /* prefix length can depend on the type */ - const gsize check_prefix_length = (fpi_device_get_driver_data (device) & EGISMOC_DRIVER_CHECK_PREFIX_TYPE2) ? - cmd_check_prefix_type2_len : cmd_check_prefix_type1_len; + const gsize check_prefix_length = (fpi_device_get_driver_data (device) & + EGISMOC_DRIVER_CHECK_PREFIX_TYPE2) ? + cmd_check_prefix_type2_len : + cmd_check_prefix_type1_len; - /* total_length is the 6 various bytes plus all other prefixes/suffixes and the body payload */ + /* total_length is the 6 various bytes plus all other prefixes/suffixes and + * the body payload */ const gsize total_length = (sizeof (guchar) * 6) + check_prefix_length + EGISMOC_CMD_CHECK_SEPARATOR_LENGTH @@ -865,20 +920,24 @@ egismoc_get_check_cmd (FpDevice *device, pos = 2; /* Size Counter bytes */ - /* "easiest" way to handle 2-bytes size for counter is to hard-code logic for when we go to the 2nd byte */ - /* note this will not work in case any model ever supports more than 14 prints (assumed max is 10) */ + /* "easiest" way to handle 2-bytes size for counter is to hard-code logic for + * when we go to the 2nd byte + * note this will not work in case any model ever supports more than 14 prints + * (assumed max is 10) */ if (self->enrolled_num > 6) { memset (result + pos, 0x01, sizeof (guchar)); pos += sizeof (guchar); - memset (result + pos, ((self->enrolled_num - 7) * 0x20) + 0x09, sizeof (guchar)); + memset (result + pos, ((self->enrolled_num - 7) * 0x20) + 0x09, + sizeof (guchar)); pos += sizeof (guchar); } else { /* first byte is 0x00, just skip it */ pos += sizeof (guchar); - memset (result + pos, ((self->enrolled_num + 1) * 0x20) + 0x09, sizeof (guchar)); + memset (result + pos, ((self->enrolled_num + 1) * 0x20) + 0x09, + sizeof (guchar)); pos += sizeof (guchar); } @@ -918,7 +977,7 @@ egismoc_get_check_cmd (FpDevice *device, for (int i = 0; i < self->enrolled_num; i++) { - device_print_id = g_ptr_array_index (self->enrolled_ids, i); + gchar *device_print_id = g_ptr_array_index (self->enrolled_ids, i); memcpy (result + pos + (print_id_length * i), device_print_id, print_id_length); } pos += body_length; @@ -948,7 +1007,8 @@ egismoc_enroll_run_state (FpiSsm *ssm, { case ENROLL_GET_ENROLLED_IDS: /* get enrolled_ids and enrolled_num from device for use in check stages below */ - egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, egismoc_list_fill_enrolled_ids_cb); + egismoc_exec_cmd (device, cmd_list, cmd_list_len, + NULL, egismoc_list_fill_enrolled_ids_cb); break; case ENROLL_CHECK_ENROLLED_NUM: @@ -962,11 +1022,13 @@ egismoc_enroll_run_state (FpiSsm *ssm, break; case ENROLL_SENSOR_RESET: - egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, + NULL, egismoc_task_ssm_next_state_cb); break; case ENROLL_SENSOR_ENROLL: - egismoc_exec_cmd (device, cmd_sensor_enroll, cmd_sensor_enroll_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_sensor_enroll, cmd_sensor_enroll_len, + NULL, egismoc_task_ssm_next_state_cb); break; case ENROLL_WAIT_FINGER: @@ -974,24 +1036,29 @@ egismoc_enroll_run_state (FpiSsm *ssm, break; case ENROLL_SENSOR_CHECK: - egismoc_exec_cmd (device, cmd_sensor_check, cmd_sensor_check_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_sensor_check, cmd_sensor_check_len, + NULL, egismoc_task_ssm_next_state_cb); break; case ENROLL_CHECK: payload = egismoc_get_check_cmd (device, &payload_length); - egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, g_free, egismoc_enroll_check_cb); + egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, + g_free, egismoc_enroll_check_cb); break; case ENROLL_START: - egismoc_exec_cmd (device, cmd_enroll_starting, cmd_enroll_starting_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_enroll_starting, cmd_enroll_starting_len, + NULL, egismoc_task_ssm_next_state_cb); break; case ENROLL_CAPTURE_SENSOR_RESET: - egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, + NULL, egismoc_task_ssm_next_state_cb); break; case ENROLL_CAPTURE_SENSOR_START_CAPTURE: - egismoc_exec_cmd (device, cmd_sensor_start_capture, cmd_sensor_start_capture_len, NULL, + egismoc_exec_cmd (device, cmd_sensor_start_capture, cmd_sensor_start_capture_len, + NULL, egismoc_task_ssm_next_state_cb); break; @@ -1000,11 +1067,13 @@ egismoc_enroll_run_state (FpiSsm *ssm, break; case ENROLL_CAPTURE_READ_RESPONSE: - egismoc_exec_cmd (device, cmd_read_capture, cmd_read_capture_len, NULL, egismoc_read_capture_cb); + egismoc_exec_cmd (device, cmd_read_capture, cmd_read_capture_len, + NULL, egismoc_read_capture_cb); break; case ENROLL_COMMIT_START: - egismoc_exec_cmd (device, cmd_commit_starting, cmd_commit_starting_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_commit_starting, cmd_commit_starting_len, + NULL, egismoc_task_ssm_next_state_cb); break; case ENROLL_COMMIT: @@ -1019,13 +1088,16 @@ egismoc_enroll_run_state (FpiSsm *ssm, payload_length = cmd_new_print_prefix_len + EGISMOC_FINGERPRINT_DATA_SIZE; payload = g_malloc0 (payload_length); memcpy (payload, cmd_new_print_prefix, cmd_new_print_prefix_len); - memcpy (payload + cmd_new_print_prefix_len, device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); + memcpy (payload + cmd_new_print_prefix_len, device_print_id, + EGISMOC_FINGERPRINT_DATA_SIZE); - egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, g_free, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, + g_free, egismoc_task_ssm_next_state_cb); break; case ENROLL_COMMIT_SENSOR_RESET: - egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, + NULL, egismoc_task_ssm_next_state_cb); break; case ENROLL_COMPLETE: @@ -1085,7 +1157,9 @@ egismoc_identify_check_cb (FpDevice *device, buffer_in + EGISMOC_IDENTIFY_RESPONSE_PRINT_ID_OFFSET, EGISMOC_FINGERPRINT_DATA_SIZE); - /* Create a new print from this device_print_id and then see if it matches the one indicated */ + /* Create a new print from this device_print_id and then see if it matches + * the one indicated + */ print = fp_print_new (device); egismoc_set_print_data (print, device_print_id, NULL); @@ -1093,7 +1167,8 @@ egismoc_identify_check_cb (FpDevice *device, { fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID, - "Failed to build a print from device response.")); + "Failed to build a print from " + "device response.")); return; } @@ -1148,8 +1223,9 @@ egismoc_identify_check_cb (FpDevice *device, } else { - fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Unrecognized response from device.")); + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Unrecognized response from device.")); } } @@ -1165,7 +1241,8 @@ egismoc_identify_run_state (FpiSsm *ssm, { case IDENTIFY_GET_ENROLLED_IDS: /* get enrolled_ids and enrolled_num from device for use in check stages below */ - egismoc_exec_cmd (device, cmd_list, cmd_list_len, NULL, egismoc_list_fill_enrolled_ids_cb); + egismoc_exec_cmd (device, cmd_list, cmd_list_len, + NULL, egismoc_list_fill_enrolled_ids_cb); break; case IDENTIFY_CHECK_ENROLLED_NUM: @@ -1179,11 +1256,13 @@ egismoc_identify_run_state (FpiSsm *ssm, break; case IDENTIFY_SENSOR_RESET: - egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, + NULL, egismoc_task_ssm_next_state_cb); break; case IDENTIFY_SENSOR_IDENTIFY: - egismoc_exec_cmd (device, cmd_sensor_identify, cmd_sensor_identify_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_sensor_identify, cmd_sensor_identify_len, + NULL, egismoc_task_ssm_next_state_cb); break; case IDENTIFY_WAIT_FINGER: @@ -1191,26 +1270,30 @@ egismoc_identify_run_state (FpiSsm *ssm, break; case IDENTIFY_SENSOR_CHECK: - egismoc_exec_cmd (device, cmd_sensor_check, cmd_sensor_check_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_sensor_check, cmd_sensor_check_len, + NULL, egismoc_task_ssm_next_state_cb); break; case IDENTIFY_CHECK: payload = egismoc_get_check_cmd (device, &payload_length); - egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, g_free, egismoc_identify_check_cb); + egismoc_exec_cmd (device, g_steal_pointer (&payload), payload_length, + g_free, egismoc_identify_check_cb); break; case IDENTIFY_COMPLETE_SENSOR_RESET: - egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, NULL, egismoc_task_ssm_next_state_cb); + egismoc_exec_cmd (device, cmd_sensor_reset, cmd_sensor_reset_len, + NULL, egismoc_task_ssm_next_state_cb); break; /* - In Windows, the driver seems at this point to then immediately take another read from the sensor; - this is suspected to be an on-chip "verify". However, because the user's finger is still on the - sensor from the identify, then it seems to always return positive. We will consider this extra - step unnecessary and just skip it in this driver. This driver will instead handle matching of the - FpPrint from the gallery in the "verify" case of the callback egismoc_identify_check_cb. + * In Windows, the driver seems at this point to then immediately take + * another read from the sensor; this is suspected to be an on-chip + * "verify". However, because the user's finger is still on the sensor from + * the identify, then it seems to always return positive. We will consider + * this extra step unnecessary and just skip it in this driver. This driver + * will instead handle matching of the FpPrint from the gallery in the + * "verify" case of the callback egismoc_identify_check_cb. */ - case IDENTIFY_COMPLETE: if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY) fpi_device_identify_complete (device, NULL); @@ -1258,14 +1341,16 @@ egismoc_fw_version_cb (FpDevice *device, { fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Device firmware response was not valid.")); + "Device firmware response " + "was not valid.")); return; } /* - FW Version is 12 bytes: a carriage return (0x0d) plus the version string itself. - Always skip [the read prefix] + [2 * check bytes] + [3 * 0x00] that come with every payload - Then we will also skip the carriage return and take all but the last 2 bytes as the FW Version + * FW Version is 12 bytes: a carriage return (0x0d) plus the version string + * itself. Always skip [the read prefix] + [2 * check bytes] + [3 * 0x00] that + * come with every payload Then we will also skip the carriage return and take + * all but the last 2 bytes as the FW Version */ prefix_length = egismoc_read_prefix_len + 2 + 3 + 1; fw_version_length = length_in - prefix_length - rsp_fw_version_suffix_len; @@ -1346,7 +1431,8 @@ egismoc_dev_init_handler (FpiSsm *ssm, break; case DEV_GET_FW_VERSION: - egismoc_exec_cmd (device, cmd_fw_version, cmd_fw_version_len, NULL, egismoc_fw_version_cb); + egismoc_exec_cmd (device, cmd_fw_version, cmd_fw_version_len, + NULL, egismoc_fw_version_cb); break; } @@ -1406,7 +1492,8 @@ egismoc_close (FpDevice *device) egismoc_cancel (device); g_clear_object (&self->interrupt_cancellable); - g_usb_device_release_interface (fpi_device_get_usb_device (device), 0, 0, &error); + g_usb_device_release_interface (fpi_device_get_usb_device (device), + 0, 0, &error); fpi_device_close_complete (device, error); } @@ -1428,7 +1515,8 @@ fpi_device_egismoc_class_init (FpiDeviceEgisMocClass *klass) dev_class->scan_type = FP_SCAN_TYPE_PRESS; dev_class->id_table = egismoc_id_table; dev_class->nr_enroll_stages = EGISMOC_ENROLL_TIMES; - dev_class->temp_hot_seconds = 0; /* device should be "always off" unless being used */ + /* device should be "always off" unless being used */ + dev_class->temp_hot_seconds = 0; dev_class->open = egismoc_open; dev_class->cancel = egismoc_cancel; diff --git a/libfprint/drivers/egismoc/egismoc.h b/libfprint/drivers/egismoc/egismoc.h index 65b50322..f027ea23 100644 --- a/libfprint/drivers/egismoc/egismoc.h +++ b/libfprint/drivers/egismoc/egismoc.h @@ -55,19 +55,6 @@ G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC, #define EGISMOC_LIST_RESPONSE_PREFIX_SIZE 14 #define EGISMOC_LIST_RESPONSE_SUFFIX_SIZE 2 -struct _FpiDeviceEgisMoc -{ - FpDevice parent; - FpiSsm *task_ssm; - FpiSsm *cmd_ssm; - FpiUsbTransfer *cmd_transfer; - GCancellable *interrupt_cancellable; - - int enrolled_num; - GPtrArray *enrolled_ids; -}; - - /* standard prefixes for all read/writes */ static guchar egismoc_write_prefix[] = {'E', 'G', 'I', 'S', 0x00, 0x00, 0x00, 0x01}; From a7843add0f7ddceec24cfb6bf8d70ef2be454879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:31:27 +0100 Subject: [PATCH 40/64] egismoc: Do not initialize to zero twice --- libfprint/drivers/egismoc/egismoc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index f4ed08b0..2fa7940f 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -389,7 +389,6 @@ egismoc_set_print_data (FpPrint *print, { fill_user_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE + 1); memcpy (fill_user_id, device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); - memset (fill_user_id + EGISMOC_FINGERPRINT_DATA_SIZE, '\0', sizeof (gchar)); } fpi_print_fill_from_user_id (print, fill_user_id); From b8cfb95b49c8a393d14cafc0160e3c47304117c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:33:11 +0100 Subject: [PATCH 41/64] egismoc: Ensure we've enough null bytes at the end of strings --- libfprint/drivers/egismoc/egismoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 2fa7940f..3a2d41d9 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -453,7 +453,7 @@ egismoc_list_fill_enrolled_ids_cb (FpDevice *device, pos < length_in - EGISMOC_LIST_RESPONSE_SUFFIX_SIZE; pos += EGISMOC_FINGERPRINT_DATA_SIZE, self->enrolled_num++) { - g_autofree guchar *print_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE); + g_autofree guchar *print_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE + 1); memcpy (print_id, buffer_in + pos, EGISMOC_FINGERPRINT_DATA_SIZE); fp_dbg ("Device fingerprint %0d: %.*s", self->enrolled_num, EGISMOC_FINGERPRINT_DATA_SIZE, print_id); From eb0915624496df62123fc820bd6d2059c55d76da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:34:16 +0100 Subject: [PATCH 42/64] egismoc: Clear enrolled IDs using nicer GLib api --- libfprint/drivers/egismoc/egismoc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 3a2d41d9..7168ca8a 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -152,8 +152,7 @@ egismoc_task_ssm_done (FpiSsm *ssm, /* task_ssm already freed by completion of SSM */ self->task_ssm = NULL; - if (self->enrolled_ids) - g_ptr_array_free (self->enrolled_ids, TRUE); + g_clear_pointer (&self->enrolled_ids, g_ptr_array_unref); self->enrolled_ids = NULL; self->enrolled_num = -1; @@ -439,8 +438,7 @@ egismoc_list_fill_enrolled_ids_cb (FpDevice *device, return; } - if (self->enrolled_ids) - g_ptr_array_free (self->enrolled_ids, TRUE); + g_clear_pointer (&self->enrolled_ids, g_ptr_array_unref); self->enrolled_ids = g_ptr_array_new_with_free_func (g_free); self->enrolled_num = 0; From 8073a5dc34cc95459011e9a4d6f74b26c8f87880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:34:46 +0100 Subject: [PATCH 43/64] egismoc: Remove unused increments This is also to please static analyzer --- libfprint/drivers/egismoc/egismoc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 7168ca8a..e14b31bc 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -615,7 +615,6 @@ egismoc_get_delete_cmd (FpDevice *device, g_ptr_array_index (self->enrolled_ids, i), EGISMOC_FINGERPRINT_DATA_SIZE); } - pos += body_length; if (length_out) *length_out = total_length; @@ -981,7 +980,6 @@ egismoc_get_check_cmd (FpDevice *device, /* command suffix */ memcpy (result + pos, cmd_check_suffix, cmd_check_suffix_len); - pos += cmd_check_suffix_len; if (length_out) *length_out = total_length; From 87f68e3ac11c70fa86a719be8104f5697eb90ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:35:58 +0100 Subject: [PATCH 44/64] egismoc: Avoid gotos in init code, just handle the errors immediately --- libfprint/drivers/egismoc/egismoc.c | 31 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index e14b31bc..d957830c 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -1386,7 +1386,6 @@ egismoc_dev_init_handler (FpiSsm *ssm, G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, 32, 0x0000, 4, 16); - goto send_control; break; case DEV_INIT_CONTROL2: @@ -1395,7 +1394,6 @@ egismoc_dev_init_handler (FpiSsm *ssm, G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, 32, 0x0000, 4, 40); - goto send_control; break; case DEV_INIT_CONTROL3: @@ -1404,7 +1402,6 @@ egismoc_dev_init_handler (FpiSsm *ssm, G_USB_DEVICE_REQUEST_TYPE_STANDARD, G_USB_DEVICE_RECIPIENT_DEVICE, 0, 0x0000, 0, 2); - goto send_control; break; case DEV_INIT_CONTROL4: @@ -1413,7 +1410,6 @@ egismoc_dev_init_handler (FpiSsm *ssm, G_USB_DEVICE_REQUEST_TYPE_STANDARD, G_USB_DEVICE_RECIPIENT_DEVICE, 0, 0x0000, 0, 2); - goto send_control; break; case DEV_INIT_CONTROL5: @@ -1422,18 +1418,17 @@ egismoc_dev_init_handler (FpiSsm *ssm, G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, 82, 0x0000, 0, 8); - goto send_control; break; case DEV_GET_FW_VERSION: egismoc_exec_cmd (device, cmd_fw_version, cmd_fw_version_len, NULL, egismoc_fw_version_cb); - break; + return; + + default: + g_assert_not_reached (); } - return; - -send_control: transfer->ssm = ssm; transfer->short_is_error = TRUE; fpi_usb_transfer_submit (g_steal_pointer (&transfer), @@ -1453,17 +1448,21 @@ egismoc_open (FpDevice *device) self->interrupt_cancellable = g_cancellable_new (); if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) - goto error; + { + fpi_device_open_complete (device, error); + return; + } - if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error)) - goto error; + if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), + 0, 0, &error)) + { + fpi_device_open_complete (device, error); + return; + } + g_assert (self->task_ssm == NULL); self->task_ssm = fpi_ssm_new (device, egismoc_dev_init_handler, DEV_INIT_STATES); fpi_ssm_start (self->task_ssm, egismoc_dev_init_done); - return; - -error: - return fpi_device_open_complete (device, error); } static void From 92aeb53ee854412861ddbf0e6d0718d1cc14d6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:37:47 +0100 Subject: [PATCH 45/64] egismoc: Simplify egismoc identification stage handling --- libfprint/drivers/egismoc/egismoc.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index d957830c..763d9216 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -1181,8 +1181,6 @@ egismoc_identify_check_cb (FpDevice *device, fpi_device_identify_report (device, g_ptr_array_index (prints, index), print, NULL); else fpi_device_identify_report (device, NULL, print, NULL); - - fpi_ssm_next_state (self->task_ssm); } else { @@ -1193,8 +1191,6 @@ egismoc_identify_check_cb (FpDevice *device, fpi_device_verify_report (device, FPI_MATCH_SUCCESS, print, NULL); else fpi_device_verify_report (device, FPI_MATCH_FAIL, print, NULL); - - fpi_ssm_next_state (self->task_ssm); } } /* If device was successfully read but it was a "not matched" */ @@ -1206,22 +1202,19 @@ egismoc_identify_check_cb (FpDevice *device, fp_info ("Print was not identified by the device"); if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY) - { - fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); - fpi_ssm_next_state (self->task_ssm); - } + fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); else - { - fpi_device_identify_report (device, NULL, NULL, NULL); - fpi_ssm_next_state (self->task_ssm); - } + fpi_device_identify_report (device, NULL, NULL, NULL); } else { fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, "Unrecognized response from device.")); + return; } + + fpi_ssm_next_state (self->task_ssm); } static void From 9e2c14d64e6ef68055c26afb4797142b2b47e805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:38:19 +0100 Subject: [PATCH 46/64] egismoc: Clarify delete print ownership in delete callback --- libfprint/drivers/egismoc/egismoc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 763d9216..2deca613 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -715,15 +715,15 @@ egismoc_delete (FpDevice *device) { fp_dbg ("Delete"); FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); - - g_autoptr(FpPrint) delete_print = NULL; + FpPrint *delete_print = NULL; fpi_device_get_delete_data (device, &delete_print); self->task_ssm = fpi_ssm_new (device, egismoc_delete_run_state, DELETE_STATES); - fpi_ssm_set_data (self->task_ssm, g_steal_pointer (&delete_print), NULL); /* todo leak or cleared by libfprint ? */ + /* the print is owned by libfprint during deletion task */ + fpi_ssm_set_data (self->task_ssm, delete_print, NULL); fpi_ssm_start (self->task_ssm, egismoc_task_ssm_done); } From 226b6abfab103283afec87ba54f38b61ff5aba2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:39:42 +0100 Subject: [PATCH 47/64] egismoc: Use an autopointer to cleanup error on command done callback --- libfprint/drivers/egismoc/egismoc.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 2deca613..05caf8d6 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -247,19 +247,15 @@ egismoc_cmd_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) { + g_autoptr(GError) local_error = error; FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); CommandData *data = fpi_ssm_get_data (ssm); self->cmd_ssm = NULL; self->cmd_transfer = NULL; - if (error) - { - if (data->callback) - data->callback (device, NULL, 0, error); - else - g_error_free (error); - } + if (error && data && data->callback) + data->callback (device, NULL, 0, g_steal_pointer (&local_error)); } typedef union egismoc_check_bytes From 6767cd1a4f9e55d5b16a469eb4d8afc4441b95c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:41:15 +0100 Subject: [PATCH 48/64] egismoc: Ensure that the command callback is after SSM is completed We need to make sure that we won't trigger a callback when a SSM is already in progress or we may end up overwriting it --- libfprint/drivers/egismoc/egismoc.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 05caf8d6..0731aa5b 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -181,7 +181,10 @@ egismoc_cmd_receive_cb (FpiUsbTransfer *transfer, gpointer userdata, GError *error) { + g_autofree guchar *buffer = NULL; CommandData *data = userdata; + SynCmdMsgCallback callback; + gssize actual_length; fp_dbg ("Command receive callback"); @@ -197,10 +200,18 @@ egismoc_cmd_receive_cb (FpiUsbTransfer *transfer, return; } - if (data->callback) - data->callback (device, transfer->buffer, transfer->actual_length, NULL); + /* Let's complete the previous ssm and then handle the callback, so that + * we are sure that we won't start a transfer or a new command while there is + * another one still ongoing + */ + callback = data->callback; + buffer = g_steal_pointer (&transfer->buffer); + actual_length = transfer->actual_length; fpi_ssm_mark_completed (transfer->ssm); + + if (callback) + callback (device, buffer, actual_length, NULL); } static void From 591f9ad3cf38a425f269e237a4a0621b6af01aed Mon Sep 17 00:00:00 2001 From: Joshua Grisham <18266314+joshuagrisham@users.noreply.github.com> Date: Sat, 17 Feb 2024 14:17:58 +0100 Subject: [PATCH 49/64] egismoc: clear task pointers also after dev_init_done --- libfprint/drivers/egismoc/egismoc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 0731aa5b..18a9ea57 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -1367,9 +1367,15 @@ egismoc_dev_init_done (FpiSsm *ssm, GError *error) { if (error) - g_usb_device_release_interface (fpi_device_get_usb_device (device), 0, 0, NULL); + { + g_usb_device_release_interface ( + fpi_device_get_usb_device (device), 0, 0, NULL); + egismoc_task_ssm_done (ssm, device, error); + return; + } - fpi_device_open_complete (device, error); + egismoc_task_ssm_done (ssm, device, NULL); + fpi_device_open_complete (device, NULL); } static void From b97efa6fedd284b053e534d0e810503844dfdb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:44:46 +0100 Subject: [PATCH 50/64] egismoc: Assert that task ssm is unset when setting it We need to ensure that we are not overwriting the instance SSM, so that we can be sure that we are only doing one operation at time. Also we need to ensure that the task unsetting it, is the owner of it. --- libfprint/drivers/egismoc/egismoc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 18a9ea57..08e514bd 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -149,7 +149,8 @@ egismoc_task_ssm_done (FpiSsm *ssm, fp_dbg ("Task SSM done"); FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); - /* task_ssm already freed by completion of SSM */ + /* task_ssm is going to be freed by completion of SSM */ + g_assert (!self->task_ssm || self->task_ssm == ssm); self->task_ssm = NULL; g_clear_pointer (&self->enrolled_ids, g_ptr_array_unref); @@ -325,6 +326,7 @@ egismoc_exec_cmd (FpDevice *device, g_autoptr(FpiUsbTransfer) transfer = NULL; CommandData *data = g_new0 (CommandData, 1); + g_assert (self->cmd_ssm == NULL); self->cmd_ssm = fpi_ssm_new (device, egismoc_cmd_run_state, CMD_STATES); @@ -499,6 +501,7 @@ egismoc_list (FpDevice *device) fp_dbg ("List"); FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + g_assert (self->task_ssm == NULL); self->task_ssm = fpi_ssm_new (device, egismoc_list_run_state, LIST_STATES); @@ -711,6 +714,7 @@ egismoc_clear_storage (FpDevice *device) fp_dbg ("Clear storage"); FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + g_assert (self->task_ssm == NULL); self->task_ssm = fpi_ssm_new (device, egismoc_delete_run_state, DELETE_STATES); @@ -726,6 +730,7 @@ egismoc_delete (FpDevice *device) fpi_device_get_delete_data (device, &delete_print); + g_assert (self->task_ssm == NULL); self->task_ssm = fpi_ssm_new (device, egismoc_delete_run_state, DELETE_STATES); @@ -1119,6 +1124,7 @@ egismoc_enroll (FpDevice *device) fpi_device_get_enroll_data (device, &enroll_print->print); enroll_print->stage = 0; + g_assert (self->task_ssm == NULL); self->task_ssm = fpi_ssm_new (device, egismoc_enroll_run_state, ENROLL_STATES); fpi_ssm_set_data (self->task_ssm, g_steal_pointer (&enroll_print), g_free); fpi_ssm_start (self->task_ssm, egismoc_task_ssm_done); @@ -1306,6 +1312,7 @@ egismoc_identify_verify (FpDevice *device) fp_dbg ("Identify or Verify"); FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); + g_assert (self->task_ssm == NULL); self->task_ssm = fpi_ssm_new (device, egismoc_identify_run_state, IDENTIFY_STATES); fpi_ssm_start (self->task_ssm, egismoc_task_ssm_done); } From 8badfa84e9ab13c946dea416d69599f6cf285106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:45:29 +0100 Subject: [PATCH 51/64] egismoc: Assert that current transfer is unset when setting it We need to ensure that we are not overwriting the instance transfer, so that we can be sure that we are only doing one transfer at time. Also we need to ensure that the ssm unsetting it, is the owner of it. --- libfprint/drivers/egismoc/egismoc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 08e514bd..0673bfed 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -263,6 +263,9 @@ egismoc_cmd_ssm_done (FpiSsm *ssm, FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); CommandData *data = fpi_ssm_get_data (ssm); + g_assert (self->cmd_ssm == ssm); + g_assert (!self->cmd_transfer || self->cmd_transfer->ssm == ssm); + self->cmd_ssm = NULL; self->cmd_transfer = NULL; @@ -373,6 +376,8 @@ egismoc_exec_cmd (FpDevice *device, buffer_out_length, g_free); transfer->ssm = self->cmd_ssm; + + g_assert (self->cmd_transfer == NULL); self->cmd_transfer = g_steal_pointer (&transfer); data->callback = callback; From 904bddd98808305bfd1ed319a7f06fb45e023463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 15:52:43 +0100 Subject: [PATCH 52/64] egismoc: Use g_new0 instead of g_malloc to make the type clearer --- libfprint/drivers/egismoc/egismoc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 0673bfed..f7a30c3e 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -348,7 +348,7 @@ egismoc_exec_cmd (FpDevice *device, buffer_out_length = egismoc_write_prefix_len + EGISMOC_CHECK_BYTES_LENGTH + cmd_length; - buffer_out = g_malloc0 (buffer_out_length); + buffer_out = g_new0 (guchar, buffer_out_length); /* Prefix */ memcpy (buffer_out, egismoc_write_prefix, egismoc_write_prefix_len); @@ -400,7 +400,7 @@ egismoc_set_print_data (FpPrint *print, } else { - fill_user_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE + 1); + fill_user_id = g_new0 (gchar, EGISMOC_FINGERPRINT_DATA_SIZE + 1); memcpy (fill_user_id, device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); } @@ -465,7 +465,7 @@ egismoc_list_fill_enrolled_ids_cb (FpDevice *device, pos < length_in - EGISMOC_LIST_RESPONSE_SUFFIX_SIZE; pos += EGISMOC_FINGERPRINT_DATA_SIZE, self->enrolled_num++) { - g_autofree guchar *print_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE + 1); + g_autofree guchar *print_id = g_new0 (guchar, EGISMOC_FINGERPRINT_DATA_SIZE + 1); memcpy (print_id, buffer_in + pos, EGISMOC_FINGERPRINT_DATA_SIZE); fp_dbg ("Device fingerprint %0d: %.*s", self->enrolled_num, EGISMOC_FINGERPRINT_DATA_SIZE, print_id); @@ -551,7 +551,7 @@ egismoc_get_delete_cmd (FpDevice *device, body_length; /* pre-fill entire payload with 00s */ - result = g_malloc0 (total_length); + result = g_new0 (guchar, total_length); /* start with 00 00 (just move starting offset up by 2) */ pos = 2; @@ -927,7 +927,7 @@ egismoc_get_check_cmd (FpDevice *device, + cmd_check_suffix_len; /* pre-fill entire payload with 00s */ - result = g_malloc0 (total_length); + result = g_new0 (guchar, total_length); /* start with 00 00 (just move starting offset up by 2) */ pos = 2; @@ -1092,13 +1092,13 @@ egismoc_enroll_run_state (FpiSsm *ssm, user_id = fpi_print_generate_user_id (enroll_print->print); fp_dbg ("New fingerprint ID: %s", user_id); - device_print_id = g_malloc0 (EGISMOC_FINGERPRINT_DATA_SIZE); + device_print_id = g_new0 (guchar, EGISMOC_FINGERPRINT_DATA_SIZE); memcpy (device_print_id, user_id, MIN (strlen (user_id), EGISMOC_FINGERPRINT_DATA_SIZE)); egismoc_set_print_data (enroll_print->print, device_print_id, user_id); /* create new dynamic payload of cmd_new_print_prefix + device_print_id */ payload_length = cmd_new_print_prefix_len + EGISMOC_FINGERPRINT_DATA_SIZE; - payload = g_malloc0 (payload_length); + payload = g_new0 (guchar, payload_length); memcpy (payload, cmd_new_print_prefix, cmd_new_print_prefix_len); memcpy (payload + cmd_new_print_prefix_len, device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); @@ -1361,7 +1361,7 @@ egismoc_fw_version_cb (FpDevice *device, */ prefix_length = egismoc_read_prefix_len + 2 + 3 + 1; fw_version_length = length_in - prefix_length - rsp_fw_version_suffix_len; - fw_version = g_malloc0 (fw_version_length + 1); + fw_version = g_new0 (guchar, fw_version_length + 1); memcpy (fw_version, buffer_in + prefix_length, From 9af211cc89841353ad7d522f816686e6ce0b7c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 16:13:07 +0100 Subject: [PATCH 53/64] egismoc: Use device cancellable on transfers --- libfprint/drivers/egismoc/egismoc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index f7a30c3e..3bc46b6f 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -231,7 +231,7 @@ egismoc_cmd_run_state (FpiSsm *ssm, self->cmd_transfer->ssm = ssm; fpi_usb_transfer_submit (g_steal_pointer (&self->cmd_transfer), EGISMOC_USB_SEND_TIMEOUT, - NULL, + fpi_device_get_cancellable (device), fpi_ssm_usb_transfer_cb, NULL); break; @@ -247,7 +247,7 @@ egismoc_cmd_run_state (FpiSsm *ssm, EGISMOC_USB_IN_RECV_LENGTH); fpi_usb_transfer_submit (g_steal_pointer (&transfer), EGISMOC_USB_RECV_TIMEOUT, - NULL, + fpi_device_get_cancellable (device), egismoc_cmd_receive_cb, fpi_ssm_get_data (ssm)); break; @@ -1451,7 +1451,7 @@ egismoc_dev_init_handler (FpiSsm *ssm, transfer->short_is_error = TRUE; fpi_usb_transfer_submit (g_steal_pointer (&transfer), EGISMOC_USB_CONTROL_TIMEOUT, - NULL, + fpi_device_get_cancellable (device), fpi_ssm_usb_transfer_cb, NULL); } From adc66edd8dfb95a3ff2fbc02704580fa0504283d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 16:34:33 +0100 Subject: [PATCH 54/64] egismoc: Implement suspension properly In case of suspension we can't just cancel the operations but also return when completed, and this may not happen immediately if there are ongoing operations. This is automagically handled by libfprint internals, but in order to make it happen, we need to cancel the ongoing operations and then mark it completed. libfprint will then wait for the task completion before actually marking the device as suspended. --- libfprint/drivers/egismoc/egismoc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 3bc46b6f..811a817c 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -1494,6 +1494,16 @@ egismoc_cancel (FpDevice *device) self->interrupt_cancellable = g_cancellable_new (); } +static void +egismoc_suspend (FpDevice *device) +{ + fp_dbg ("Suspend"); + + egismoc_cancel (device); + g_cancellable_cancel (fpi_device_get_cancellable (device)); + fpi_device_suspend_complete (device, NULL); +} + static void egismoc_close (FpDevice *device) { @@ -1532,7 +1542,7 @@ fpi_device_egismoc_class_init (FpiDeviceEgisMocClass *klass) dev_class->open = egismoc_open; dev_class->cancel = egismoc_cancel; - dev_class->suspend = egismoc_cancel; + dev_class->suspend = egismoc_suspend; dev_class->close = egismoc_close; dev_class->identify = egismoc_identify_verify; dev_class->verify = egismoc_identify_verify; From f8f28a066b9cc4a682f6ae61cc768c13daea3ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 16:56:35 +0100 Subject: [PATCH 55/64] egismoc: Simplify fingerprint id and firmware reading We can do copy and duplicate in oneshot since we are handling strings after all. --- libfprint/drivers/egismoc/egismoc.c | 37 +++++++++++------------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 811a817c..27bcd73f 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -386,23 +386,18 @@ egismoc_exec_cmd (FpDevice *device, } static void -egismoc_set_print_data (FpPrint *print, - const guchar *device_print_id, - const gchar *user_id) +egismoc_set_print_data (FpPrint *print, + const gchar *device_print_id, + const gchar *user_id) { GVariant *print_id_var = NULL; GVariant *fpi_data = NULL; g_autofree gchar *fill_user_id = NULL; if (user_id) - { - fill_user_id = g_strdup (user_id); - } + fill_user_id = g_strdup (user_id); else - { - fill_user_id = g_new0 (gchar, EGISMOC_FINGERPRINT_DATA_SIZE + 1); - memcpy (fill_user_id, device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); - } + fill_user_id = g_strndup (device_print_id, EGISMOC_FINGERPRINT_DATA_SIZE); fpi_print_fill_from_user_id (print, fill_user_id); @@ -465,8 +460,8 @@ egismoc_list_fill_enrolled_ids_cb (FpDevice *device, pos < length_in - EGISMOC_LIST_RESPONSE_SUFFIX_SIZE; pos += EGISMOC_FINGERPRINT_DATA_SIZE, self->enrolled_num++) { - g_autofree guchar *print_id = g_new0 (guchar, EGISMOC_FINGERPRINT_DATA_SIZE + 1); - memcpy (print_id, buffer_in + pos, EGISMOC_FINGERPRINT_DATA_SIZE); + g_autofree gchar *print_id = g_strndup ((gchar *) buffer_in + pos, + EGISMOC_FINGERPRINT_DATA_SIZE); fp_dbg ("Device fingerprint %0d: %.*s", self->enrolled_num, EGISMOC_FINGERPRINT_DATA_SIZE, print_id); g_ptr_array_add (self->enrolled_ids, g_steal_pointer (&print_id)); @@ -1012,7 +1007,7 @@ egismoc_enroll_run_state (FpiSsm *ssm, EnrollPrint *enroll_print = fpi_ssm_get_data (ssm); g_autofree guchar *payload = NULL; gsize payload_length = 0; - g_autofree guchar *device_print_id = NULL; + g_autofree gchar *device_print_id = NULL; g_autofree gchar *user_id = NULL; switch (fpi_ssm_get_cur_state (ssm)) @@ -1092,8 +1087,7 @@ egismoc_enroll_run_state (FpiSsm *ssm, user_id = fpi_print_generate_user_id (enroll_print->print); fp_dbg ("New fingerprint ID: %s", user_id); - device_print_id = g_new0 (guchar, EGISMOC_FINGERPRINT_DATA_SIZE); - memcpy (device_print_id, user_id, MIN (strlen (user_id), EGISMOC_FINGERPRINT_DATA_SIZE)); + device_print_id = g_strndup (user_id, EGISMOC_FINGERPRINT_DATA_SIZE); egismoc_set_print_data (enroll_print->print, device_print_id, user_id); /* create new dynamic payload of cmd_new_print_prefix + device_print_id */ @@ -1143,7 +1137,7 @@ egismoc_identify_check_cb (FpDevice *device, { fp_dbg ("Identify check callback"); FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); - guchar device_print_id[EGISMOC_FINGERPRINT_DATA_SIZE]; + gchar device_print_id[EGISMOC_FINGERPRINT_DATA_SIZE]; FpPrint *print = NULL; FpPrint *verify_print = NULL; GPtrArray *prints; @@ -1330,8 +1324,9 @@ egismoc_fw_version_cb (FpDevice *device, { fp_dbg ("Firmware version callback"); FpiDeviceEgisMoc *self = FPI_DEVICE_EGISMOC (device); - g_autofree guchar *fw_version = NULL; + g_autofree gchar *fw_version = NULL; gsize prefix_length; + guchar *fw_version_start; gsize fw_version_length; if (error) @@ -1360,13 +1355,9 @@ egismoc_fw_version_cb (FpDevice *device, * all but the last 2 bytes as the FW Version */ prefix_length = egismoc_read_prefix_len + 2 + 3 + 1; + fw_version_start = buffer_in + prefix_length; fw_version_length = length_in - prefix_length - rsp_fw_version_suffix_len; - fw_version = g_new0 (guchar, fw_version_length + 1); - - memcpy (fw_version, - buffer_in + prefix_length, - length_in - prefix_length - rsp_fw_version_suffix_len); - *(fw_version + fw_version_length) = '\0'; + fw_version = g_strndup ((gchar *) fw_version_start, fw_version_length); fp_info ("Device firmware version is %s", fw_version); From 90c4afded4e669464c866235698d16d9bd940964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 22:14:14 +0100 Subject: [PATCH 56/64] cleanup: Use non-const pointers for non constant cases We had various cases in which we were using const pointers for non constant data, and in fact we were allocating and free'ing them. So let's handle all these case properly, so that we won't have newer GLib complaining at us! --- libfprint/drivers/elanmoc/elanmoc.c | 4 ++-- libfprint/drivers/focaltech_moc/focaltech_moc.c | 4 ++-- libfprint/fp-device-private.h | 2 +- libfprint/fpi-ssm.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libfprint/drivers/elanmoc/elanmoc.c b/libfprint/drivers/elanmoc/elanmoc.c index 0261bfff..e86b79a2 100644 --- a/libfprint/drivers/elanmoc/elanmoc.c +++ b/libfprint/drivers/elanmoc/elanmoc.c @@ -50,9 +50,9 @@ elanmoc_compose_cmd ( const struct elanmoc_cmd *cmd_info ) { - g_autofree char *cmd_buf = NULL; + g_autofree uint8_t *cmd_buf = NULL; - cmd_buf = g_malloc0 (cmd_info->cmd_len); + cmd_buf = g_new0 (uint8_t, cmd_info->cmd_len); if(cmd_info->cmd_len < ELAN_MAX_HDR_LEN) memcpy (cmd_buf, &cmd_info->cmd_header, cmd_info->cmd_len); else diff --git a/libfprint/drivers/focaltech_moc/focaltech_moc.c b/libfprint/drivers/focaltech_moc/focaltech_moc.c index 190ab436..9872c7c7 100644 --- a/libfprint/drivers/focaltech_moc/focaltech_moc.c +++ b/libfprint/drivers/focaltech_moc/focaltech_moc.c @@ -126,12 +126,12 @@ fp_cmd_bcc (uint8_t *data, uint16_t len) static uint8_t * focaltech_moc_compose_cmd (uint8_t cmd, const uint8_t *data, uint16_t len) { - g_autofree char *cmd_buf = NULL; + g_autofree uint8_t *cmd_buf = NULL; FpCmd *fp_cmd = NULL; uint8_t *bcc = NULL; uint16_t header_len = len + sizeof (*bcc); - cmd_buf = g_malloc0 (sizeof (FpCmd) + header_len); + cmd_buf = g_new0 (uint8_t, sizeof (FpCmd) + header_len); fp_cmd = (FpCmd *) cmd_buf; diff --git a/libfprint/fp-device-private.h b/libfprint/fp-device-private.h index 759a678f..1c3702fa 100644 --- a/libfprint/fp-device-private.h +++ b/libfprint/fp-device-private.h @@ -44,7 +44,7 @@ typedef struct FpDeviceType type; GUsbDevice *usb_device; - const gchar *virtual_env; + gchar *virtual_env; struct { gchar *spidev_path; diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index c34498ab..b816945f 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -73,7 +73,7 @@ struct _FpiSsm { FpDevice *dev; - const char *name; + char *name; FpiSsm *parentsm; gpointer ssm_data; GDestroyNotify ssm_data_destroy; From 057c209bebdd852178030e3cae7a7bd1b1dfe05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 22:23:02 +0100 Subject: [PATCH 57/64] build: Build-depend on glib 2.68 GLib 2.68 is now more than 3 years old, so we can definitely start using it without thinking too much. This allows us to drop lots of compat code that we had around. And like the previous commit tells us, it will also help us to have more correct code around. --- libfprint/fpi-compat.h | 30 ------------------------------ meson.build | 2 +- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/libfprint/fpi-compat.h b/libfprint/fpi-compat.h index ad86874a..efb77723 100644 --- a/libfprint/fpi-compat.h +++ b/libfprint/fpi-compat.h @@ -20,36 +20,6 @@ #include -#if !GLIB_CHECK_VERSION (2, 57, 0) -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GTypeClass, g_type_class_unref); -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GEnumClass, g_type_class_unref); -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GFlagsClass, g_type_class_unref); -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GParamSpec, g_param_spec_unref); -#else -/* Re-define G_SOURCE_FUNC as we are technically not allowed to use it with - * the version we depend on currently. */ -#undef G_SOURCE_FUNC -#endif - -#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void))(f)) - -#if !GLIB_CHECK_VERSION (2, 63, 3) -typedef struct _FpDeviceClass FpDeviceClass; -G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpDeviceClass, g_type_class_unref); -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GDate, g_date_free); -#endif - -#if !GLIB_CHECK_VERSION (2, 68, 0) -#define g_memdup2(data, size) g_memdup ((data), (size)) -#else -#define g_memdup2(data, size) \ - (G_GNUC_EXTENSION ({ \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_memdup2 ((data), (size)); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - })) -#endif - #if __GNUC__ > 10 || (__GNUC__ == 10 && __GNUC_MINOR__ >= 1) #define FP_GNUC_ACCESS(m, p, s) __attribute__((access (m, p, s))) #else diff --git a/meson.build b/meson.build index 10d17e75..b57d3c51 100644 --- a/meson.build +++ b/meson.build @@ -21,7 +21,7 @@ datadir = prefix / get_option('datadir') cc = meson.get_compiler('c') cpp = meson.get_compiler('cpp') host_system = host_machine.system() -glib_min_version = '2.56' +glib_min_version = '2.68' glib_version_def = 'GLIB_VERSION_@0@_@1@'.format( glib_min_version.split('.')[0], glib_min_version.split('.')[1]) From ed1815c3d951630c472f093ed8dfdcb03e597134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 20:28:01 +0100 Subject: [PATCH 58/64] build: Allow testing more drivers in both big and little endian I've tested them in a s390x host and many more tests work fine, so let's enable them. --- meson.build | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/meson.build b/meson.build index b57d3c51..64779e9c 100644 --- a/meson.build +++ b/meson.build @@ -142,8 +142,26 @@ default_drivers = [ # FIXME: All the drivers should be fixed by adjusting the byte order. # See https://gitlab.freedesktop.org/libfprint/libfprint/-/issues/236 endian_independent_drivers = virtual_drivers + [ + 'aes1610', + 'aes1660', + 'aes2550', + 'aes2660', 'aes3500', + 'aes4000', + 'egis0570', + 'elanmoc', + 'etes603', + 'focaltech_moc', + 'nb1010', + 'realtek', 'synaptics', + 'upeksonly', + 'upektc', + 'upektc_img', + 'upekts', + 'vcom5s', + 'vfs101', + 'vfs7552', ] all_drivers = default_drivers + virtual_drivers From 6768bd0ff4a975ba23388c9add570140ba681e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 21:12:14 +0100 Subject: [PATCH 59/64] egismoc: Use strictly sized types to hold check bytes contents So we are sure about the size we're sending at compile time too. --- libfprint/drivers/egismoc/egismoc.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 27bcd73f..c33809e1 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -273,12 +273,15 @@ egismoc_cmd_ssm_done (FpiSsm *ssm, data->callback (device, NULL, 0, g_steal_pointer (&local_error)); } -typedef union egismoc_check_bytes +typedef union { - unsigned short check_short; - guchar check_bytes[EGISMOC_CHECK_BYTES_LENGTH]; + guint16 check_value; + guchar check_bytes[EGISMOC_CHECK_BYTES_LENGTH]; } EgisMocCheckBytes; +G_STATIC_ASSERT (G_SIZEOF_MEMBER (EgisMocCheckBytes, check_value) == + sizeof (guint8) * EGISMOC_CHECK_BYTES_LENGTH); + /* * Derive the 2 "check bytes" for write payloads * 32-bit big-endian sum of all 16-bit words (including check bytes) MOD 0xFFFF @@ -290,26 +293,22 @@ egismoc_get_check_bytes (const guchar *value, { fp_dbg ("Get check bytes"); EgisMocCheckBytes check_bytes; - unsigned short value_bigendian_shorts[(int) ((value_length + 1) / 2)]; + guint16 big_endian_values[(int) ((value_length + 1) / 2)]; + size_t sum_values = 0; int s = 0; for (int i = 0; i < value_length; i = i + 2, s++) { if (i + 1 < value_length) - value_bigendian_shorts[s] = (((short) value[i + 1]) << 8) | (0x00ff & value[i]); + big_endian_values[s] = (((guint16) value[i + 1]) << 8) | (0x00ff & value[i]); else - value_bigendian_shorts[s] = (((short) 0x00) << 8) | (0x00ff & value[i]); + big_endian_values[s] = (((guint16) 0x00) << 8) | (0x00ff & value[i]); } - unsigned long sum_shorts = 0; for (int i = 0; i < s; i++) - sum_shorts += value_bigendian_shorts[i]; + sum_values += big_endian_values[i]; - /* - derive the "first possible occurrence" of check bytes as: - `0xFFFF - (sum_of_32bit_words % 0xFFFF) - */ - check_bytes.check_short = 0xffff - (sum_shorts % 0xffff); + check_bytes.check_value = 0xffff - (sum_values % 0xffff); return check_bytes; } From 59dc585ccd070ef94ba7a5dfe173a803b1dfd41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 21:41:44 +0100 Subject: [PATCH 60/64] egismoc: Simplify check bytes computation We use big endian values for generating the check bytes, but we can do the same logic in a simpler way. --- libfprint/drivers/egismoc/egismoc.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index c33809e1..960df140 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -293,19 +293,21 @@ egismoc_get_check_bytes (const guchar *value, { fp_dbg ("Get check bytes"); EgisMocCheckBytes check_bytes; - guint16 big_endian_values[(int) ((value_length + 1) / 2)]; + const size_t steps = (value_length + 1) / 2; + guint16 big_endian_values[steps]; size_t sum_values = 0; - int s = 0; - for (int i = 0; i < value_length; i = i + 2, s++) + for (int i = 0, j = 0; i < value_length; i += 2, j++) { - if (i + 1 < value_length) - big_endian_values[s] = (((guint16) value[i + 1]) << 8) | (0x00ff & value[i]); - else - big_endian_values[s] = (((guint16) 0x00) << 8) | (0x00ff & value[i]); + big_endian_values[j] = 0; + + if (i < value_length - 1) + big_endian_values[j] = value[i + 1] << 8; + + big_endian_values[j] |= (value[i] & 0x00ff); } - for (int i = 0; i < s; i++) + for (int i = 0; i < steps; i++) sum_values += big_endian_values[i]; check_bytes.check_value = 0xffff - (sum_values % 0xffff); From 5462db9901b6540ff46cb9e3b14628cc57c764de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 21:45:53 +0100 Subject: [PATCH 61/64] egismoc: Convert the check value to little endian In this way we can support the big-endian architectures too. --- libfprint/drivers/egismoc/egismoc.c | 2 +- meson.build | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 960df140..841e7e93 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -310,7 +310,7 @@ egismoc_get_check_bytes (const guchar *value, for (int i = 0; i < steps; i++) sum_values += big_endian_values[i]; - check_bytes.check_value = 0xffff - (sum_values % 0xffff); + check_bytes.check_value = GUINT16_TO_LE (0xffff - (sum_values % 0xffff)); return check_bytes; } diff --git a/meson.build b/meson.build index 64779e9c..07019d3c 100644 --- a/meson.build +++ b/meson.build @@ -149,6 +149,7 @@ endian_independent_drivers = virtual_drivers + [ 'aes3500', 'aes4000', 'egis0570', + 'egismoc', 'elanmoc', 'etes603', 'focaltech_moc', From 3e5ab6fdad95666cd998c52be438f767ee8440c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 21:53:41 +0100 Subject: [PATCH 62/64] egismoc: Convert value check values to big endian only when needed Since the driver seem to require a big-endian value it's just better to use architecture native endianness to compute the check value and eventually just convert to big endian as the chip wants. --- libfprint/drivers/egismoc/egismoc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c index 841e7e93..0b5e8d2e 100644 --- a/libfprint/drivers/egismoc/egismoc.c +++ b/libfprint/drivers/egismoc/egismoc.c @@ -294,23 +294,21 @@ egismoc_get_check_bytes (const guchar *value, fp_dbg ("Get check bytes"); EgisMocCheckBytes check_bytes; const size_t steps = (value_length + 1) / 2; - guint16 big_endian_values[steps]; + guint16 values[steps]; size_t sum_values = 0; for (int i = 0, j = 0; i < value_length; i += 2, j++) { - big_endian_values[j] = 0; + values[j] = (value[i] << 8 & 0xff00); if (i < value_length - 1) - big_endian_values[j] = value[i + 1] << 8; - - big_endian_values[j] |= (value[i] & 0x00ff); + values[j] |= value[i + 1] & 0x00ff; } for (int i = 0; i < steps; i++) - sum_values += big_endian_values[i]; + sum_values += values[i]; - check_bytes.check_value = GUINT16_TO_LE (0xffff - (sum_values % 0xffff)); + check_bytes.check_value = GUINT16_TO_BE (0xffff - (sum_values % 0xffff)); return check_bytes; } From 5501dc7b4753c01b4c03bdc9f82fad1b98b63e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 20 Feb 2024 01:00:29 +0100 Subject: [PATCH 63/64] build: Stop using deprecated dep.get_pkgconfig_variable() method Use generic get_variable() instead --- doc/meson.build | 2 +- meson.build | 4 ++-- tests/meson.build | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/meson.build b/doc/meson.build index 77236a06..e48067fd 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -19,7 +19,7 @@ content_files = [ expand_content_files = content_files -glib_prefix = dependency('glib-2.0').get_pkgconfig_variable('prefix') +glib_prefix = dependency('glib-2.0').get_variable(pkgconfig: 'prefix') glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html') docpath = join_paths(get_option('datadir'), 'gtk-doc', 'html') diff --git a/meson.build b/meson.build index 07019d3c..984f26e6 100644 --- a/meson.build +++ b/meson.build @@ -269,7 +269,7 @@ if install_udev_rules udev_rules_dir = get_option('udev_rules_dir') if udev_rules_dir == 'auto' udev_dep = dependency('udev') - udev_rules_dir = udev_dep.get_pkgconfig_variable('udevdir') + '/rules.d' + udev_rules_dir = udev_dep.get_variable(pkgconfig: 'udevdir') + '/rules.d' endif endif @@ -306,7 +306,7 @@ if not udev_hwdb.disabled() if udev_hwdb_dir == 'auto' udev_dep = dependency('udev') - udev_hwdb_dir = udev_dep.get_pkgconfig_variable('udevdir') + '/hwdb.d' + udev_hwdb_dir = udev_dep.get_variable(pkgconfig: 'udevdir') + '/hwdb.d' endif else udev_hwdb_dir = '' diff --git a/tests/meson.build b/tests/meson.build index efa573fb..f68ed406 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -339,7 +339,7 @@ endif valgrind = find_program('valgrind', required: false) if valgrind.found() - glib_share = glib_dep.get_pkgconfig_variable('prefix') / 'share' / glib_dep.name() + glib_share = glib_dep.get_variable(pkgconfig: 'prefix') / 'share' / glib_dep.name() glib_suppressions = glib_share + '/valgrind/glib.supp' libfprint_suppressions = '@0@/@1@'.format(meson.project_source_root(), files('libfprint.supp')[0]) From bebe8565cd7e2c89c0b0c5e6ee7353b80d6a51e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 19 Feb 2024 23:39:50 +0100 Subject: [PATCH 64/64] Release 1.94.7 --- NEWS | 15 +++++++++++++++ meson.build | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 103740f6..44a048f8 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,21 @@ This file lists notable changes in each release. For the full history of all changes, see ChangeLog. +2024-02-20: v1.94.7 release + +Highlights: + * synaptics: fix enroll identify problem after user reset database. + * synaptics: New PIDs 0x0173, 0x0106, 0x0124. + * goodixmoc: New PID 0x6582. + * build: Do not require bash to build, only posix sh. + * fp-image: Simplify minutiae detection tasks. + * GLib 2.68 is now required to build libfprint. + +New drivers: + * realtek (PID 0x5813). + * focaltech_moc (PIDs 0x9E48, 0xD979, 0xA959). + * egismoc (PIDs 0x0582, 0x05a1). + 2023-08-17: v1.94.6 release Highlights: diff --git a/meson.build b/meson.build index 984f26e6..435827ce 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('libfprint', [ 'c', 'cpp' ], - version: '1.94.6', + version: '1.94.7', license: 'LGPLv2.1+', default_options: [ 'buildtype=debugoptimized',