Compare commits

..
Author SHA1 Message Date
Joshua Grisham 2d7c5277de sdcp: tweak virtual-sdcp driver and test to work with gnome-desktop-testing-runner 2026-04-07 19:36:50 +02:00
Joshua Grisham 4a8211074f ci: remove unnecessary installed-tests param 2026-04-06 18:28:27 +02:00
Joshua Grisham e86f3c403c tests: also install subdir for virtual device tests 2026-04-06 18:25:47 +02:00
Joshua Grisham 77b8655616 ci: Add installed-tests=true to test_installed job 2026-04-06 18:07:23 +02:00
Joshua Grisham 5af8496458 egismoc: Keep using default enrollment stages for 0x0582 and 0x05a1 2026-04-06 17:42:00 +02:00
Joshua Grisham 6cdcb3a8e9 Merge remote-tracking branch 'origin/master' into feature/sdcp-v2 2026-04-06 16:23:29 +02:00
Joshua Grisham d878136546 Merge remote-tracking branch 'libfprint/master' into feature/sdcp-v2 2025-10-20 19:14:46 +02:00
Joshua Grisham 31f9a250be sdcp: uncrustify 2025-09-13 13:13:19 +02:00
Joshua Grisham df004970f1 egismoc: avoid double finalization of cancelled tasks 2025-09-13 13:11:41 +02:00
Joshua Grisham 21960019fc sdcp: Fail enroll and identify if there is no application_secret 2025-09-09 21:31:33 +02:00
Joshua Grisham 6086c9fb27 sdcp: Refactor test-sdcp-device-fake to virtual-sdcp to support testing with fprintd 2025-09-09 21:30:38 +02:00
Joshua Grisham 036ebd607a egismoc: uncrustify 2025-09-01 18:29:51 +02:00
Joshua Grisham 99ed5d139b sdcp: uncrustify 2025-09-01 18:29:03 +02:00
Joshua Grisham 74e15c793b sdcp: use GBytes for sdcp-data instead of GVariant 2025-09-01 01:37:06 +02:00
Joshua Grisham e96f9c3618 sdcp: Make sure to always set error when failing and use UNTRUSTED error for SDCP failures 2025-08-31 22:17:52 +02:00
Joshua Grisham 05d0b13c28 egismoc: Add support for 1c7a:0584 2025-08-28 21:10:53 +02:00
Joshua Grisham 7460e0dec4 sdcp-v2: Move SDCP support from using NSS to OpenSSL 3.0 and implement for egismoc 2025-08-28 21:01:54 +02:00
Benjamin BergandJoshua Grisham c74a0d4592 sdcp-device: Use a key DB for testing 2025-07-20 01:14:03 +02:00
Benjamin BergandJoshua Grisham 3c1c9acd71 sdcp-device: Use predictable keys and random numbers for testing
If FP_DEVICE_EMULATION is set, then switch to using predictable EC
ephemeral key and random numbers. This should allow recording and
replaying real device interactions using umockdev.
2025-07-20 01:12:48 +02:00
Benjamin BergandJoshua Grisham 6974e1bad4 port to low level EC key functions
This will allow creating a fixed key for testing
2025-07-20 01:11:20 +02:00
Benjamin BergandJoshua Grisham 89fae86e8c saner public key param 2025-07-20 01:09:58 +02:00
Benjamin BergandJoshua Grisham a925b83456 fixups: use static OID data 2025-07-20 01:09:30 +02:00
Benjamin BergandJoshua Grisham 4e811f7b63 tests: Add SDCP virtual device test 2025-07-20 01:07:24 +02:00
Benjamin BergandJoshua Grisham e3fab01e38 virtual-sdcp: Add a virtual SDCP device
This device is designed to talk to an external executable. This
executable can be provided/created using the test implementation from
Microsoft.
2025-07-20 01:04:54 +02:00
Benjamin BergandJoshua Grisham f063e3787e sdcp: Add SDCP base class
This adds a base class for SDCP devices. Not all functionality has been
fully tested, in particular the code to verify the model certificate is
most likely broken or incomplete. One problem there is that there is no
code to find the root CA to trust.

See: #257
2025-07-20 01:03:16 +02:00
Benjamin BergandJoshua Grisham ca89ead07d device: Add new FP_DEVICE_ERROR_UNTRUSTED error code
Throw a specific error when we are unable to verify the integrety of the
device.
2025-07-20 00:57:32 +02:00
Benjamin BergandJoshua Grisham 4582b3b825 print: Add an SDCP print type
For now it is identical to a RAW print. However, this is likely to
change in the future.
2025-07-20 00:56:30 +02:00
133 changed files with 6168 additions and 13851 deletions
+2 -2
View File
@@ -68,8 +68,8 @@ def build_install(revision):
subprocess.check_call(['meson', build_dir,
'--prefix=/usr', '--libdir=lib',
'-Dx11-examples=false', '-Ddoc=false', '-Dgtk-examples=false'])
subprocess.check_call(['meson', 'compile', '-v', '-C', build_dir])
subprocess.check_call(['meson', 'install', '-v', '-C', build_dir],
subprocess.check_call(['ninja', '-v', '-C', build_dir])
subprocess.check_call(['ninja', '-v', '-C', build_dir, 'install'],
env={'DESTDIR': dest_dir})
return dest_dir
+6 -104
View File
@@ -22,8 +22,7 @@ variables:
FDO_DISTRIBUTION_TAG: $LIBFPRINT_IMAGE_TAG
FDO_DISTRIBUTION_VERSION: rawhide
FDO_UPSTREAM_REPO: "libfprint/$CI_PROJECT_NAME"
FEDORA_IMAGE: "registry.freedesktop.org/libfprint/$CI_PROJECT_NAME/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG"
FEDORA_IMAGE_S390X: "registry.freedesktop.org/libfprint/$CI_PROJECT_NAME/fedora/$FDO_DISTRIBUTION_VERSION-s390x:$LIBFPRINT_IMAGE_TAG"
FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG"
LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546"
workflow:
@@ -90,8 +89,7 @@ test:
script:
- meson setup _build --werror -Ddrivers=all -Db_coverage=true
- meson test -C _build --print-errorlogs --no-stdsplit --timeout-multiplier 3
# We can't use `meson compile` https://github.com/mesonbuild/meson/issues/7895
- ninja -C _build coverage
- ninja -C _build coverage || true
- cat _build/meson-logs/coverage.txt || true
artifacts:
reports:
@@ -201,11 +199,8 @@ test_unsupported_list:
extends:
- .standard_job
allow_failure: true
variables:
FPRINT_HWDB_LIST: libfprint/fprint-list-udev-hwdb.c
script:
- scripts/sync-unsupported-devices.py $FPRINT_HWDB_LIST
- git diff --color=always --exit-code $FPRINT_HWDB_LIST
- tests/hwdb-check-unsupported.py
flatpak:
stage: flatpak
@@ -276,7 +271,8 @@ container_fedora_build_manual:
- if: $LIBFPRINT_CI_ACTION == "build-image"
when: always
.container_fedora_build_on_deps_changed_rules:
container_fedora_build_on_deps_changed:
extends: .container_fedora_build_base
rules:
- if: $CI_PROJECT_NAMESPACE == "libfprint" && $CI_PIPELINE_SOURCE != "schedule"
changes:
@@ -285,111 +281,17 @@ container_fedora_build_manual:
- '.gitlab-ci/libfprint-image-variables.yaml'
- '.gitlab-ci/libfprint-templates.yaml'
container_fedora_build_on_deps_changed:
extends:
- .container_fedora_build_base
- .container_fedora_build_on_deps_changed_rules
.container_fedora_image_build_base_s390x:
stage: image-build
image: quay.io/freedesktop.org/ci-templates:container-build-base-2026-05-19.0
variables:
GIT_STRATEGY: none
S390X_TAG: $LIBFPRINT_IMAGE_TAG-s390x
STORAGE_DRIVER: vfs
script:
- |
if [ -z "${FDO_FORCE_REBUILD:-}" ]; then
if skopeo inspect --no-tags --retry-times 3 "docker://$FEDORA_IMAGE_S390X" 2>/dev/null; then
echo "Image $FEDORA_IMAGE_S390X already exists, skipping build."
exit 0
fi
fi
- buildah from --name s390x-container --platform linux/s390x
registry.fedoraproject.org/fedora:$FDO_DISTRIBUTION_VERSION
- ROOTFS=$(buildah mount s390x-container)
- dnf install -y --installroot=$ROOTFS --forcearch=s390x
--use-host-config
--setopt=tsflags=noscripts --releasever=$FDO_DISTRIBUTION_VERSION
--setopt=install_weak_deps=False
$LIBFPRINT_DEPENDENCIES
- dnf clean all --installroot=$ROOTFS || true
- buildah commit s390x-container "$FEDORA_IMAGE_S390X"
- podman login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- buildah push "$FEDORA_IMAGE_S390X"
container_fedora_build_schedule_s390x:
extends:
- .container_fedora_image_build_base_s390x
- .container_fedora_build_forced
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" && $CRON_TASK == "BUILD_CI_IMAGES"
when: always
container_fedora_build_manual_s390x:
extends:
- .container_fedora_image_build_base_s390x
- .container_fedora_build_forced
rules:
- if: $LIBFPRINT_CI_ACTION == "build-image"
when: always
container_fedora_build_on_deps_changed_s390x:
extends:
- .container_fedora_image_build_base_s390x
- .container_fedora_build_on_deps_changed_rules
test_s390x:
stage: test
allow_failure: true
needs:
- job: container_fedora_build_manual_s390x
optional: true
variables:
STORAGE_DRIVER: vfs
QEMU_CPU: max
before_script:
# The s390x binaries are executed via the qemu-s390x-static registered in
# the *host* kernel's binfmt_misc. Use $FEDORA_IMAGE qemu-user-static
# (and systemd-binfmt), and run it privileged to re-register its handlers
# and make every run use the same emulator.
- podman run --rm --privileged
--security-opt label=filetype:container_file_t
--security-opt label=level:s0
--security-opt label=type:spc_t
-v $CI_PROJECT_DIR/.gitlab-ci/qemu-user-static/container-entrypoint:/container-entrypoint:z
$FEDORA_IMAGE /container-entrypoint
script:
- podman run --rm --platform linux/s390x -e QEMU_CPU -v $CI_PROJECT_DIR:/build:z -w /build "$FEDORA_IMAGE_S390X" /bin/bash -c \
"meson setup _build --werror -Ddrivers=all && meson test -C _build --print-errorlogs --no-stdsplit --timeout-multiplier 4 --suite=drivers --suite=virtual-driver"
artifacts:
when: always
paths:
- _build/meson-logs/
reports:
junit: "_build/meson-logs/testlog.junit.xml"
rules:
- if: $LIBFPRINT_CI_ACTION == "test-s390x"
when: always
- when: manual
pages:
image: alpine:latest
stage: deploy
needs:
- job: test
artifacts: true
- job: build
artifacts: true
script:
- mkdir public
- mv _build/meson-logs/coveragereport public/coverage || true
- mv _build/doc/html public/doc
- mv _build/meson-logs/coveragereport public/coverage
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push"
- if: $CI_COMMIT_TAG
+1 -1
View File
@@ -1,2 +1,2 @@
variables:
LIBFPRINT_IMAGE_TAG: v8
LIBFPRINT_IMAGE_TAG: v6
-2
View File
@@ -27,8 +27,6 @@
meson
openssl-devel
pixman-devel
podman
qemu-user-static
python3-cairo
python3-gobject
systemd
@@ -1,27 +0,0 @@
#!/bin/sh
#
# Unregister any stale qemu-*-static binfmt handlers and (re-)register the ones
# shipped by this image via systemd-binfmt, so the host always uses the recent
# qemu-user-static built into this image (registered with the "F" fix-binary
# flag from /usr/lib/binfmt.d).
#
# Based on https://www.itix.fr/blog/qemu-user-static-with-podman/
set -Eeuo pipefail
if [ ! -d /proc/sys/fs/binfmt_misc ]; then
echo "No binfmt support in the kernel."
echo " Try: '/sbin/modprobe binfmt_misc' from the host"
exit 1
fi
if [ ! -f /proc/sys/fs/binfmt_misc/register ]; then
echo "Mounting /proc/sys/fs/binfmt_misc..."
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
fi
echo "Cleaning up..."
find /proc/sys/fs/binfmt_misc -type f -name 'qemu-*' -exec sh -c 'echo -1 > {}' \;
echo "Registering..."
exec /usr/lib/systemd/systemd-binfmt
+2 -5
View File
@@ -1,7 +1,4 @@
#!/bin/sh -x
#!/bin/sh
# This wrapper just disables the malloc checker
exec /usr/bin/scan-build --status-bugs -disable-checker unix.Malloc \
--exclude "$PWD/_build/meson-private" \
--exclude "$PWD/meson-private" \
"$@"
exec /usr/bin/scan-build --status-bugs -disable-checker unix.Malloc --exclude "_build/meson-private" "$@"
-24
View File
@@ -1,30 +1,6 @@
This file lists notable changes in each release. For the full history of all
changes, see ChangeLog.
2026-07-25: v1.94.100 release
Highlights:
* bump meson dependency to 0.62
* egis_etu905: New driver supporting PIDs 0x05AE and 0x9201
* synaptics: New PIDs 0x010D, 0x010E and 0x01A4
* goodixmoc: Support 27c6:6382, 27c6:6984 and 27c6:6090
* nb1010: Support the NB-2020-U fingerprint reader
* focaltech_moc: New PID 0xA97A
* elanmoc: New PID 0x0CB2
* fpcmoc: New PID 0x9B24
* aes2550: Handle tapping without swiping and fix deactivation after errors
* fp-device: Implement verify using identification, improving driver
consistency and reducing duplicated driver code
* fpcmoc, egis_etu905, elan, elanspi, goodixmoc and mafpmoc: Improve
big-endian support
* goodixmoc: Fix malformed payload, CRC and invalid finger handling
* synaptics, realtek, vfs101, vfs301, upekts, elan and egismoc: Fix
memory safety, bounds checking and error handling issues
* aes1610, aes2510, aesx660, fpcmoc and uru4000: Fix resource leaks and
cancellation or device deactivation issues
* Expanded USB replay coverage for aes2550 and egis_etu905, and improved
virtual-image testing with transformed fingerprint images
2026-02-10: v1.94.10 release
Highlights:
+11 -71
View File
@@ -77,12 +77,6 @@ usb:v1C7Ap0571*
ID_AUTOSUSPEND=1
ID_PERSIST=0
# Supported by libfprint driver egis_etu905
usb:v1C7Ap05AE*
usb:v1C7Ap9201*
ID_AUTOSUSPEND=1
ID_PERSIST=0
# Supported by libfprint driver egismoc
usb:v1C7Ap0582*
usb:v1C7Ap0583*
@@ -91,6 +85,7 @@ usb:v1C7Ap0586*
usb:v1C7Ap0587*
usb:v1C7Ap0588*
usb:v1C7Ap05A1*
usb:v1C7Ap05A5*
ID_AUTOSUSPEND=1
ID_PERSIST=0
@@ -168,14 +163,12 @@ usb:v04F3p0C8C*
usb:v04F3p0C8D*
usb:v04F3p0C98*
usb:v04F3p0C99*
usb:v04F3p0C9C*
usb:v04F3p0C9D*
usb:v04F3p0C9F*
usb:v04F3p0CA3*
usb:v04F3p0CA7*
usb:v04F3p0CA8*
usb:v04F3p0CB0*
usb:v04F3p0CB2*
ID_AUTOSUSPEND=1
ID_PERSIST=0
@@ -192,12 +185,9 @@ usb:v2808pA959*
usb:v2808pA99A*
usb:v2808pA57A*
usb:v2808pA78A*
usb:v2808pA97A*
usb:v2808p1579*
usb:v2808p077A*
usb:v2808p079A*
usb:v2808p5158*
usb:v2808p6553*
ID_AUTOSUSPEND=1
ID_PERSIST=0
@@ -211,14 +201,12 @@ usb:v10A5pD205*
usb:v10A5p9524*
usb:v10A5p9544*
usb:v10A5pC844*
usb:v10A5p9B24*
ID_AUTOSUSPEND=1
ID_PERSIST=0
# Supported by libfprint driver goodixmoc
usb:v27C6p5840*
usb:v27C6p6014*
usb:v27C6p6090*
usb:v27C6p6092*
usb:v27C6p6094*
usb:v27C6p609A*
@@ -231,7 +219,6 @@ usb:v27C6p6304*
usb:v27C6p631C*
usb:v27C6p633C*
usb:v27C6p634C*
usb:v27C6p6382*
usb:v27C6p6384*
usb:v27C6p639C*
usb:v27C6p63AC*
@@ -249,21 +236,13 @@ usb:v27C6p659A*
usb:v27C6p659C*
usb:v27C6p6A94*
usb:v27C6p6512*
usb:v27C6p6890*
usb:v27C6p689A*
usb:v27C6p66A9*
usb:v27C6p6984*
ID_AUTOSUSPEND=1
ID_PERSIST=0
# Supported by libfprint driver mafpmoc
usb:v3274p8012*
ID_AUTOSUSPEND=1
ID_PERSIST=0
# Supported by libfprint driver nb1010
usb:v298Dp1010*
usb:v298Dp2020*
ID_AUTOSUSPEND=1
ID_PERSIST=0
@@ -271,12 +250,6 @@ usb:v298Dp2020*
usb:v0BDAp5813*
usb:v0BDAp5816*
usb:v2541pFA03*
usb:v3274p9003*
ID_AUTOSUSPEND=1
ID_PERSIST=0
# Supported by libfprint driver secugen
usb:v1162p2200*
ID_AUTOSUSPEND=1
ID_PERSIST=0
@@ -298,8 +271,6 @@ usb:v06CBp0107*
usb:v06CBp0108*
usb:v06CBp0109*
usb:v06CBp010A*
usb:v06CBp010D*
usb:v06CBp010E*
usb:v06CBp0123*
usb:v06CBp0124*
usb:v06CBp0126*
@@ -313,7 +284,6 @@ usb:v06CBp0174*
usb:v06CBp019D*
usb:v06CBp019F*
usb:v06CBp01A0*
usb:v06CBp01A4*
ID_AUTOSUSPEND=1
ID_PERSIST=0
@@ -382,20 +352,17 @@ usb:v138Ap0091*
ID_PERSIST=0
# Known unsupported devices
usb:v006Bp00DA*
usb:v0413p2766*
usb:v045Ep0815*
usb:v0A5Cp5802*
usb:v047Dp00F2*
usb:v047Dp8054*
usb:v047Dp8055*
usb:v04E8p730B*
usb:v04F2pB61E*
usb:v04F3p036B*
usb:v04F3p0C00*
usb:v04F3p0C4C*
usb:v04F3p0C57*
usb:v04F3p0C5A*
usb:v04F3p0C5E*
usb:v04F3p0C5A*
usb:v04F3p0C60*
usb:v04F3p0C6C*
usb:v04F3p0C70*
@@ -405,28 +372,15 @@ usb:v04F3p0C7C*
usb:v04F3p0C7F*
usb:v04F3p0C80*
usb:v04F3p0C85*
usb:v04F3p0C8A*
usb:v04F3p0C8E*
usb:v04F3p0C90*
usb:v04F3p0CA2*
usb:v04F3p24CC*
usb:v04F3p2706*
usb:v04F3p3032*
usb:v04F3p3057*
usb:v04F3p3058*
usb:v04F3p3066*
usb:v04F3p3087*
usb:v04F3p309F*
usb:v04F3p30C6*
usb:v04F3p3104*
usb:v04F3p310D*
usb:v04F3p3128*
usb:v04F3p3195*
usb:v04F3p31FC*
usb:v04F3p3201*
usb:v04F3p3212*
usb:v04F3p0C8A*
usb:v05BAp000E*
usb:v05C8p03C0*
usb:v06CBp0051*
usb:v06CBp0081*
usb:v06CBp0088*
@@ -440,9 +394,8 @@ usb:v06CBp00B7*
usb:v06CBp00BB*
usb:v06CBp00BC*
usb:v06CBp00BE*
usb:v06CBp00C7*
usb:v06CBp00C9*
usb:v06CBp00CB*
usb:v06CBp00C9*
usb:v06CBp00D8*
usb:v06CBp00DA*
usb:v06CBp00DC*
@@ -450,12 +403,7 @@ usb:v06CBp00E4*
usb:v06CBp00E7*
usb:v06CBp00FD*
usb:v06CBp00FF*
usb:v06CBp0102*
usb:v06CBpCE67*
usb:v06CBpCEEF*
usb:v08FFp3400*
usb:v0A5Cp5801*
usb:v0A5Cp5802*
usb:v0A5Cp5805*
usb:v0A5Cp5834*
usb:v0A5Cp5840*
@@ -470,7 +418,6 @@ usb:v0A5Cp5864*
usb:v0A5Cp5865*
usb:v0A5Cp5866*
usb:v0A5Cp5867*
usb:v0BDAp0129*
usb:v0BDAp5812*
usb:v10A5p0007*
usb:v10A5p9200*
@@ -481,7 +428,6 @@ usb:v10A5pA900*
usb:v10A5pA921*
usb:v10A5pE340*
usb:v1188p9545*
usb:v138Ap0006*
usb:v138Ap0007*
usb:v138Ap003A*
usb:v138Ap003C*
@@ -492,8 +438,8 @@ usb:v138Ap0092*
usb:v138Ap0094*
usb:v138Ap0097*
usb:v138Ap009D*
usb:v138Ap00A6*
usb:v138Ap00AB*
usb:v138Ap00A6*
usb:v147Ep1002*
usb:v1491p0088*
usb:v16D1p1027*
@@ -502,9 +448,6 @@ usb:v1C7Ap0575*
usb:v1C7Ap0576*
usb:v1C7Ap0577*
usb:v1C7Ap057E*
usb:v1C7Ap05A5*
usb:v1C7Ap05AA*
usb:v1C7Ap0801*
usb:v2541p0236*
usb:v2541p9711*
usb:v27C6p5042*
@@ -532,25 +475,22 @@ usb:v27C6p55A2*
usb:v27C6p55A4*
usb:v27C6p55B4*
usb:v27C6p5740*
usb:v27C6p5E0A*
usb:v27C6p581A*
usb:v27C6p589A*
usb:v27C6p5E0A*
usb:v27C6p5F10*
usb:v27C6p5F91*
usb:v2808p06FC*
usb:v2808p6652*
usb:v27C6p6382*
usb:v2808p9338*
usb:v2808p9348*
usb:v2808p93A9*
usb:v2808pA553*
usb:v2808pA573*
usb:v2808pA658*
usb:v2808pC652*
usb:v2808pA553*
usb:v298Dp2020*
usb:v298Dp2033*
usb:v2DF0p0003*
usb:v3274p9008*
usb:v3274pB012*
usb:v33A7p2388*
usb:v3274p8012*
usb:v3538p0930*
ID_AUTOSUSPEND=1
ID_PERSIST=0
+1 -1
View File
@@ -1,6 +1,6 @@
if udev_hwdb_dir != ''
# This file has to be updated using
# meson compile -C <builddir> sync-udev-hwdb
# ninja -C <builddir> libfprint/sync-udev-hwdb
# Note that the unsupported device list needs to be manually synced from
# the wiki. See comment in libfprint/fprint-list-uev-hwdb.c
+33 -133
View File
@@ -75,7 +75,6 @@ fp_device_clear_storage_sync
fp_device_suspend_sync
fp_device_resume_sync
FpDevice
FpTemperature
</SECTION>
<SECTION>
@@ -101,6 +100,12 @@ FP_TYPE_IMAGE_DEVICE
FpImageDevice
</SECTION>
<SECTION>
<FILE>fp-sdcp-device</FILE>
FP_TYPE_SDCP_DEVICE
FpSdcpDevice
</SECTION>
<SECTION>
<FILE>fp-print</FILE>
FP_TYPE_PRINT
@@ -135,134 +140,6 @@ fpi_line_asmbl_ctx
fpi_assemble_lines
</SECTION>
<SECTION>
<FILE>fpi-byte-reader</FILE>
<TITLE>FpiByteReader</TITLE>
FpiByteReader
fpi_byte_reader_new
fpi_byte_reader_new_bytes
fpi_byte_reader_free
fpi_byte_reader_init
fpi_byte_reader_init_bytes
fpi_byte_reader_peek_sub_reader
fpi_byte_reader_get_sub_reader
fpi_byte_reader_set_pos
fpi_byte_reader_get_pos
fpi_byte_reader_get_remaining
fpi_byte_reader_get_size
fpi_byte_reader_skip
fpi_byte_reader_get_bytes
fpi_byte_reader_peek_bytes
fpi_byte_reader_get_uint8
fpi_byte_reader_get_int8
fpi_byte_reader_peek_uint8
fpi_byte_reader_peek_int8
fpi_byte_reader_get_uint16_le
fpi_byte_reader_get_int16_le
fpi_byte_reader_peek_uint16_le
fpi_byte_reader_peek_int16_le
fpi_byte_reader_get_uint16_be
fpi_byte_reader_get_int16_be
fpi_byte_reader_peek_uint16_be
fpi_byte_reader_peek_int16_be
fpi_byte_reader_get_uint24_le
fpi_byte_reader_get_int24_le
fpi_byte_reader_peek_uint24_le
fpi_byte_reader_peek_int24_le
fpi_byte_reader_get_uint24_be
fpi_byte_reader_get_int24_be
fpi_byte_reader_peek_uint24_be
fpi_byte_reader_peek_int24_be
fpi_byte_reader_get_uint32_le
fpi_byte_reader_get_int32_le
fpi_byte_reader_peek_uint32_le
fpi_byte_reader_peek_int32_le
fpi_byte_reader_get_uint32_be
fpi_byte_reader_get_int32_be
fpi_byte_reader_peek_uint32_be
fpi_byte_reader_peek_int32_be
fpi_byte_reader_get_uint64_le
fpi_byte_reader_get_int64_le
fpi_byte_reader_peek_uint64_le
fpi_byte_reader_peek_int64_le
fpi_byte_reader_get_uint64_be
fpi_byte_reader_get_int64_be
fpi_byte_reader_peek_uint64_be
fpi_byte_reader_peek_int64_be
fpi_byte_reader_get_float32_le
fpi_byte_reader_peek_float32_le
fpi_byte_reader_get_float32_be
fpi_byte_reader_peek_float32_be
fpi_byte_reader_get_float64_le
fpi_byte_reader_peek_float64_le
fpi_byte_reader_get_float64_be
fpi_byte_reader_peek_float64_be
fpi_byte_reader_get_data
fpi_byte_reader_peek_data
fpi_byte_reader_get_data_static
fpi_byte_reader_peek_data_static
fpi_byte_reader_dup_data
fpi_byte_reader_masked_scan_uint32
fpi_byte_reader_masked_scan_uint32_peek
fpi_byte_reader_skip_string
fpi_byte_reader_skip_string_utf8
fpi_byte_reader_skip_string_utf16
fpi_byte_reader_skip_string_utf32
fpi_byte_reader_peek_string
fpi_byte_reader_peek_string_utf8
fpi_byte_reader_get_string_utf8
fpi_byte_reader_dup_string_utf8
fpi_byte_reader_dup_string_utf16
fpi_byte_reader_dup_string_utf32
</SECTION>
<SECTION>
<FILE>fpi-byte-writer</FILE>
<TITLE>FpiByteWriter</TITLE>
FpiByteWriter
fpi_byte_writer_new
fpi_byte_writer_new_with_size
fpi_byte_writer_new_with_data
fpi_byte_writer_init
fpi_byte_writer_init_with_size
fpi_byte_writer_init_with_data
fpi_byte_writer_reset
fpi_byte_writer_reset_and_get_bytes
fpi_byte_writer_reset_and_get_data
fpi_byte_writer_free
fpi_byte_writer_free_and_get_data
fpi_byte_writer_get_remaining
fpi_byte_writer_ensure_free_space
fpi_byte_writer_put_bytes
fpi_byte_writer_put_uint8
fpi_byte_writer_put_uint16_be
fpi_byte_writer_put_uint24_be
fpi_byte_writer_put_uint32_be
fpi_byte_writer_put_uint64_be
fpi_byte_writer_put_uint16_le
fpi_byte_writer_put_uint24_le
fpi_byte_writer_put_uint32_le
fpi_byte_writer_put_uint64_le
fpi_byte_writer_put_int8
fpi_byte_writer_put_int16_be
fpi_byte_writer_put_int24_be
fpi_byte_writer_put_int32_be
fpi_byte_writer_put_int64_be
fpi_byte_writer_put_int16_le
fpi_byte_writer_put_int24_le
fpi_byte_writer_put_int32_le
fpi_byte_writer_put_int64_le
fpi_byte_writer_put_float32_be
fpi_byte_writer_put_float64_be
fpi_byte_writer_put_float32_le
fpi_byte_writer_put_float64_le
fpi_byte_writer_put_string_utf8
fpi_byte_writer_put_string_utf16
fpi_byte_writer_put_string_utf32
fpi_byte_writer_put_data
fpi_byte_writer_fill
</SECTION>
<SECTION>
<FILE>fpi-context</FILE>
fpi_get_driver_types
@@ -282,7 +159,6 @@ fpi_device_get_current_action
fpi_device_retry_new
fpi_device_error_new
fpi_device_retry_new_msg
fpi_device_emulation_mode_enabled
fpi_device_error_new_msg
fpi_device_get_driver_data
fpi_device_get_enroll_data
@@ -345,17 +221,41 @@ fpi_image_device_retry_scan
fpi_image_device_set_bz3_threshold
</SECTION>
<SECTION>
<FILE>fpi-sdcp-device</FILE>
<TITLE>Internal FpSdcpDevice</TITLE>
FpiSdcpClaim
FpSdcpDeviceClass
fpi_sdcp_claim_get_type
fpi_sdcp_claim_new
fpi_sdcp_claim_copy
fpi_sdcp_claim_free
fpi_sdcp_device_open_complete
fpi_sdcp_device_get_connect_data
fpi_sdcp_device_connect_complete
fpi_sdcp_device_get_reconnect_data
fpi_sdcp_device_reconnect_complete
fpi_sdcp_device_list_complete
fpi_sdcp_device_enroll_commit
fpi_sdcp_device_enroll_commit_complete
fpi_sdcp_device_get_identify_data
fpi_sdcp_device_set_identify_data
fpi_sdcp_device_identify_retry
fpi_sdcp_device_identify_complete
fpi_sdcp_device_get_print_id
fpi_sdcp_device_set_print_id
</SECTION>
<SECTION>
<FILE>fpi-log</FILE>
fp_dbg
fp_dbg_hex_dump_bytes
fp_dbg_hex_dump_data
fp_info
fp_warn
fp_err
fpi_log_is_debug_transfer_enabled
BUG_ON
BUG
fp_dbg_hex_dump_bytes
fp_dbg_hex_dump_gbytes
</SECTION>
<SECTION>
+2 -6
View File
@@ -28,6 +28,7 @@
<xi:include href="xml/fp-context.xml"/>
<xi:include href="xml/fp-device.xml"/>
<xi:include href="xml/fp-image-device.xml"/>
<xi:include href="xml/fp-sdcp-device.xml"/>
<xi:include href="xml/fp-print.xml"/>
<xi:include href="xml/fp-image.xml"/>
</part>
@@ -38,6 +39,7 @@
<title>Device methods for drivers</title>
<xi:include href="xml/fpi-device.xml"/>
<xi:include href="xml/fpi-image-device.xml"/>
<xi:include href="xml/fpi-sdcp-device.xml"/>
</chapter>
<chapter id="driver-helpers">
@@ -48,12 +50,6 @@
<xi:include href="xml/fpi-log.xml"/>
</chapter>
<chapter id="driver-data">
<title>Binary buffer I/O</title>
<xi:include href="xml/fpi-byte-reader.xml"/>
<xi:include href="xml/fpi-byte-writer.xml"/>
</chapter>
<chapter id="driver-img">
<title>Image manipulation</title>
<xi:include href="xml/fpi-image.xml"/>
+1 -1
View File
@@ -121,7 +121,7 @@ start_clear_storage (FpDevice *dev, ClearStorageData *clear_storage_data)
{
char buffer[20];
g_print ("Clear device storage? [y/N]? ");
g_print ("Clear device storage? [Y/n]? ");
if (fgets (buffer, sizeof (buffer), stdin) &&
(buffer[0] == 'Y' || buffer[0] == 'y'))
{
+1 -1
View File
@@ -791,7 +791,7 @@ complete_deactivation (FpImageDevice *dev)
* maybe we can do this with a master reset, unconditionally? */
self->deactivating = FALSE;
g_slist_free_full (self->strips, g_free);
g_slist_free (self->strips);
self->strips = NULL;
self->strips_len = 0;
self->blanks_count = 0;
+3 -2
View File
@@ -213,8 +213,9 @@ sum_histogram_values (unsigned char *data, guint8 threshold)
if (threshold > 0x0f)
return -1;
/* FIXME endianness */
for (i = threshold; i < 16; i++)
r += GUINT16_FROM_LE (histogram[i]);
r += histogram[i];
return r;
}
@@ -822,7 +823,7 @@ complete_deactivation (FpImageDevice *dev)
* maybe we can do this with a master reset, unconditionally? */
self->deactivating = FALSE;
g_slist_free_full (self->strips, g_free);
g_slist_free (self->strips);
self->strips = NULL;
self->strips_len = 0;
fpi_image_device_deactivate_complete (dev, NULL);
+8 -52
View File
@@ -57,7 +57,6 @@ struct _FpiDeviceAes2550
GSList *strips;
size_t strips_len;
gboolean active;
gboolean deactivating;
int heartbeat_cnt;
};
@@ -92,21 +91,11 @@ finger_det_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
FpImageDevice *dev = FP_IMAGE_DEVICE (device);
FpiDeviceAes2550 *self = FPI_DEVICE_AES2550 (device);
unsigned char *data = transfer->buffer;
if (error)
{
/* The finger-detect loop is broken; it no longer has a pending
* operation. Clear the active flag so that the deactivation
* triggered by the session error (or an in-flight cancellation)
* can complete. */
self->active = FALSE;
if (self->deactivating)
complete_deactivation (dev);
else
fpi_image_device_session_error (dev, error);
fpi_image_device_session_error (FP_IMAGE_DEVICE (device), error);
return;
}
@@ -133,20 +122,10 @@ finger_det_reqs_cb (FpiUsbTransfer *t, FpDevice *device,
{
FpiUsbTransfer *transfer;
FpImageDevice *dev = FP_IMAGE_DEVICE (device);
FpiDeviceAes2550 *self = FPI_DEVICE_AES2550 (device);
if (error)
{
/* The finger-detect loop is broken; it no longer has a pending
* operation. Clear the active flag so that the deactivation
* triggered by the session error (or an in-flight cancellation)
* can complete. */
self->active = FALSE;
if (self->deactivating)
complete_deactivation (dev);
else
fpi_image_device_session_error (dev, error);
fpi_image_device_session_error (dev, error);
return;
}
@@ -260,19 +239,13 @@ capture_set_idle_reqs_cb (FpiUsbTransfer *transfer,
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed (transfer->ssm);
}
else if (!error)
{
/* No image frames were captured (e.g. user tapped without swiping).
* Such case is quite easy to happen, since Super RSR is enabled and
* thus "Slices with 0-3 pixels of Y motion are discarded" according
* to the design specification manual. */
fpi_image_device_retry_scan (dev, FP_DEVICE_RETRY_TOO_SHORT);
fpi_image_device_report_finger_status (dev, FALSE);
fpi_ssm_mark_completed (transfer->ssm);
}
else
{
fpi_ssm_mark_failed (transfer->ssm, error);
if (error)
fpi_ssm_mark_failed (transfer->ssm, error);
else
fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
}
}
@@ -398,11 +371,6 @@ capture_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
}
else if (error)
{
/* The capture SSM has terminated, so no completion handler is left
* to observe the deactivation that the session error triggers.
* Clear the active flag so dev_deactivate() completes it directly.
*/
self->active = FALSE;
fpi_image_device_session_error (dev, error);
}
else
@@ -521,16 +489,12 @@ activate_run_state (FpiSsm *ssm, FpDevice *dev)
static void
activate_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
{
FpiDeviceAes2550 *self = FPI_DEVICE_AES2550 (_dev);
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
fpi_image_device_activate_complete (dev, error);
if (!error)
{
self->active = TRUE;
start_finger_detection (dev);
}
start_finger_detection (dev);
}
static void
@@ -547,13 +511,6 @@ dev_deactivate (FpImageDevice *dev)
{
FpiDeviceAes2550 *self = FPI_DEVICE_AES2550 (dev);
if (!self->active)
{
/* No operation is pending, so complete it right away. */
complete_deactivation (dev);
return;
}
self->deactivating = TRUE;
}
@@ -565,7 +522,6 @@ complete_deactivation (FpImageDevice *dev)
G_DEBUG_HERE ();
self->deactivating = FALSE;
self->active = FALSE;
g_slist_free (self->strips);
self->strips = NULL;
self->strips_len = 0;
+1 -10
View File
@@ -384,15 +384,6 @@ capture_read_stripe_data_cb (FpiUsbTransfer *transfer,
payload_length = priv->stripe_packet->data[AESX660_RESPONSE_SIZE_LSB_OFFSET] +
(priv->stripe_packet->data[AESX660_RESPONSE_SIZE_MSB_OFFSET] << 8);
if (payload_length > AESX660_BULK_TRANSFER_SIZE)
{
fp_dbg ("Oversized payload length %" G_GSSIZE_FORMAT ", discarding",
payload_length);
g_byte_array_set_size (priv->stripe_packet, 0);
break;
}
fp_dbg ("Got frame, type %.2x payload of size %.4lx",
priv->stripe_packet->data[AESX660_RESPONSE_TYPE_OFFSET],
(long) payload_length);
@@ -731,7 +722,7 @@ complete_deactivation (FpImageDevice *dev)
G_DEBUG_HERE ();
priv->deactivating = FALSE;
g_slist_free_full (priv->strips, g_free);
g_slist_free (priv->strips);
priv->strips = NULL;
priv->strips_len = 0;
fpi_image_device_deactivate_complete (dev, NULL);
File diff suppressed because it is too large Load Diff
-188
View File
@@ -1,188 +0,0 @@
/*
* Driver for Egis Technology (LighTuning) Match-On-Chip sensors
*
* 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 (FpiDeviceEgisEtu905, fpi_device_egis_etu905, FPI, DEVICE_EGIS_ETU905, FpDevice)
#define EGIS_ETU905_DRIVER_FULLNAME "Egis Technology (LighTuning) Match-on-Chip"
#define EGIS_ETU905_DRIVER_CHECK_PREFIX_TYPE1 (1 << 0)
#define EGIS_ETU905_DRIVER_CHECK_PREFIX_TYPE2 (1 << 1)
#define EGIS_ETU905_DRIVER_MAX_ENROLL_STAGES_20 (1 << 2)
#define EGIS_ETU905_EP_CMD_OUT (0x02 | FPI_USB_ENDPOINT_OUT)
#define EGIS_ETU905_EP_CMD_IN (0x81 | FPI_USB_ENDPOINT_IN)
#define EGIS_ETU905_EP_CMD_INTERRUPT_IN 0x83
#define EGIS_ETU905_USB_CONTROL_TIMEOUT 5000
#define EGIS_ETU905_USB_SEND_TIMEOUT 5000
#define EGIS_ETU905_USB_RECV_TIMEOUT 5000
#define EGIS_ETU905_USB_INTERRUPT_TIMEOUT 0 /* No timeout for waiting for finger down */
#define EGIS_ETU905_USB_IN_RECV_LENGTH 4096
#define EGIS_ETU905_USB_INTERRUPT_IN_RECV_LENGTH 64
#define EGIS_ETU905_MAX_ENROLL_STAGES_DEFAULT 12
#define EGIS_ETU905_MAX_ENROLL_NUM 12
#define EGIS_ETU905_FINGERPRINT_DATA_SIZE 32
#define EGIS_ETU905_LIST_RESPONSE_PREFIX_SIZE 14
#define EGIS_ETU905_LIST_RESPONSE_SUFFIX_SIZE 2
#define EGIS_ETU905_PARA_4_SIZE 68
#define EGIS_ETU905_PARA_1_VALUE 0
#define EGIS_ETU905_PARA_2_VALUE 0
#define EGIS_ETU905_PARA_3_VALUE 32
/* standard prefixes for all read/writes */
static const guchar egis_etu905_write_prefix[] = {'E', 'G', 'I', 'S', 0x00, 0x00, 0x00, 0x01};
static const guchar egis_etu905_read_prefix[] = {'S', 'I', 'G', 'E', 0x00, 0x00, 0x00, 0x01};
/* hard-coded command payloads */
static const guchar cmd_fw_version[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x0c};
static const guchar rsp_fw_version_suffix[] = {0x90, 0x00};
static const guchar cmd_init[] = {0x00, 0x00, 0x00, 0x29, 0x50, 0x81, 0x80, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12, 0x00, 0x45, 0x67, 0x69, 0x73,
0x46, 0x50, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x51, 0xc6, 0xbd, 0x71};
static const guchar cmd_list[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x19, 0x04, 0x00, 0x00, 0x01, 0x40};
static const guchar cmd_sensor_reset[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x1a, 0x00, 0x00};
static const guchar cmd_sensor_check[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x17, 0x02, 0x00};
static const guchar cmd_sensor_identify[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x17, 0x01, 0x01};
static const guchar rsp_identify_match_suffix[] = {0x90, 0x00};
static const guchar rsp_identify_notmatch_suffix[] = {0x90, 0x04};
static const guchar cmd_enroll_starting[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x01, 0x00, 0x00, 0x00, 0x20};
static const guchar cmd_enroll_discard[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x04, 0x00, 0x00, 0x00, 0x20};
static const guchar cmd_sensor_start_capture[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x16, 0x02, 0x01};
static const guchar cmd_read_capture[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x02, 0x02, 0x00, 0x00, 0x02};
static const guchar cmd_duplicate_check[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x16, 0x05, 0x00};
static const guchar rsp_read_success_prefix[] = {0x00, 0x00, 0x00, 0x04};
static const guchar rsp_read_success_suffix[] = {0x90, 0x00};
static const guchar rsp_read_offcenter_prefix[] = {0x00, 0x00, 0x00, 0x04};
static const guchar rsp_read_offcenter_suffix[] = {0x64, 0x91};
static const guchar rsp_read_dirty_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x64};
static const guchar cmd_commit_starting[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x05, 0x00, 0x00, 0x00, 0x20};
static const guchar cmd_identify_cancel[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x04, 0x01, 0x00};
static const guchar cmd_identify_cancel_result[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x04, 0x02, 0x00};
/* prefixes/suffixes and other things for dynamically created command payloads */
#define EGIS_ETU905_CHECK_BYTES_LENGTH 2
#define EGIS_ETU905_IDENTIFY_RESPONSE_PRINT_ID_OFFSET 46
#define EGIS_ETU905_CMD_CHECK_SEPARATOR_LENGTH 32
static const guchar cmd_new_print_prefix_type2[] = {0x00, 0x00, 0x00, 0x50, 0x50, 0x16, 0x03, 0x00, 0x00, 0x00, 0x49};
static const guchar cmd_delete_prefix[] = {0x50, 0x18, 0x04, 0x00, 0x00};
static const guchar rsp_delete_success_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x90, 0x00};
static const guchar cmd_check_prefix_type1[] = {0x50, 0x17, 0x03, 0x00, 0x00};
static const guchar cmd_check_prefix_type2[] = {0x50, 0x17, 0x03, 0x80, 0x00};
static const guchar cmd_check_suffix[] = {0x00, 0x40};
static const guchar rsp_check_not_yet_enrolled_suffix[] = {0x90, 0x04};
/* SSM task states and various status enums */
typedef enum {
CMD_SEND,
CMD_GET,
CMD_STATES,
} CommandStates;
typedef enum {
DEV_GET_FW_VERSION,
DEV_INIT_CONTROL,
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_START,
ENROLL_CAPTURE_SENSOR_RESET,
ENROLL_CAPTURE_SENSOR_START_CAPTURE,
ENROLL_CAPTURE_WAIT_FINGER,
ENROLL_CAPTURE_READ_RESPONSE,
ENROLL_DUPLICATE_CHECK,
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;
typedef enum {
CANCEL_SENSOR_RESET,
CANCEL_SEND_CANCEL,
CANCEL_SEND_CANCEL_RESULT,
CANCEL_STATES,
} CancelStates;
File diff suppressed because it is too large Load Diff
+65 -17
View File
@@ -1,15 +1,10 @@
/*
* Driver for Egis Technology (LighTuning) Match-On-Chip sensors
* Originally authored 2023 by Joshua Grisham <josh@joshuagrisham.com>
* Copyright (C) 2023-2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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
@@ -27,16 +22,18 @@
#pragma once
#include "fpi-device.h"
#include "fpi-ssm.h"
G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC, FpDevice)
#include "fpi-sdcp-device.h"
G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC, FpSdcpDevice)
#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_DRIVER_MAX_ENROLL_STAGES_20 (1 << 2)
#define EGISMOC_DRIVER_MAX_ENROLL_STAGES_15 (1 << 3)
#define EGISMOC_EP_CMD_OUT (0x02 | FPI_USB_ENDPOINT_OUT)
#define EGISMOC_EP_CMD_IN (0x81 | FPI_USB_ENDPOINT_IN)
@@ -52,46 +49,76 @@ G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC,
#define EGISMOC_MAX_ENROLL_STAGES_DEFAULT 10
#define EGISMOC_MAX_ENROLL_NUM 10
#define EGISMOC_FINGERPRINT_DATA_SIZE 32
#define EGISMOC_FINGER_ON_SENSOR_TIMEOUT_USEC (10 * G_USEC_PER_SEC)
#define EGISMOC_CONNECT_RESPONSE_PREFIX_SIZE 15
#define EGISMOC_IDENTIFY_RESPONSE_PREFIX_SIZE 14
#define EGISMOC_ENROLL_STARTING_RESPONSE_PREFIX_SIZE 14
#define EGISMOC_LIST_RESPONSE_PREFIX_SIZE 14
#define EGISMOC_LIST_RESPONSE_SUFFIX_SIZE 2
/* 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 rsp_sensor_has_finger_suffix[] = {0x90, 0x00, 0x90, 0x00};
static gsize rsp_sensor_has_finger_suffix_len = sizeof (rsp_sensor_has_finger_suffix) / sizeof (rsp_sensor_has_finger_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_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_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]);
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 rsp_enroll_starting_suffix[] = {0x90, 0x00};
static gsize rsp_enroll_starting_suffix_len = sizeof (rsp_enroll_starting_suffix) / sizeof (rsp_enroll_starting_suffix[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_capture_post_wait_finger[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x80};
static gsize cmd_capture_post_wait_finger_len = sizeof (cmd_capture_post_wait_finger) / sizeof (cmd_capture_post_wait_finger[0]);
static guchar cmd_read_capture[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x02, 0x02, 0x00, 0x00, 0x02};
static guchar rsp_read_success_prefix[] = {0x00, 0x00, 0x00, 0x04};
static gsize cmd_read_capture_len = sizeof (cmd_read_capture) / sizeof (cmd_read_capture[0]);
static guchar rsp_read_success_suffix[] = {0x90, 0x00};
static guchar rsp_read_offcenter_prefix[] = {0x00, 0x00, 0x00, 0x04};
static gsize rsp_read_success_suffix_len = sizeof (rsp_read_success_suffix) / sizeof (rsp_read_success_suffix[0]);
static guchar rsp_read_offcenter_suffix[] = {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]);
static guchar rsp_commit_success_suffix[] = {0x90, 0x00};
static gsize rsp_commit_success_suffix_len = sizeof (rsp_commit_success_suffix) / sizeof (rsp_commit_success_suffix[0]);
/* commands which exist on the device but are currently not used */
@@ -110,18 +137,30 @@ static guchar cmd_commit_starting[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x05,
/* 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_sdcp_connect_prefix[] = {0x00, 0x00, 0x00, 0x6b, 0x50, 0x57, 0x01, 0x00, 0x00, 0x00, 0x62, 0x20};
static gsize cmd_sdcp_connect_prefix_len = sizeof (cmd_sdcp_connect_prefix) / sizeof (cmd_sdcp_connect_prefix[0]);
static guchar cmd_sdcp_connect_suffix[] = {0x00, 0x00};
static gsize cmd_sdcp_connect_suffix_len = sizeof (cmd_sdcp_connect_suffix) / sizeof (cmd_sdcp_connect_suffix[0]);
static guchar rsp_sdcp_connect_success_suffix[] = {0x90, 0x00};
static gsize rsp_sdcp_connect_success_suffix_len = sizeof (rsp_sdcp_connect_success_suffix) / sizeof (rsp_sdcp_connect_success_suffix[0]);
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_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_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 */
@@ -142,6 +181,18 @@ typedef enum {
DEV_INIT_STATES,
} DeviceInitStates;
typedef enum {
CONNECT,
CONNECT_RESPONSE,
CONNECT_STATES,
} ConnectStates;
typedef enum {
WAIT_FINGER_NOT_ON_SENSOR,
WAIT_FINGER_ON_SENSOR,
WAIT_FINGER_STATES,
} WaitFingerStates;
typedef enum {
IDENTIFY_GET_ENROLLED_IDS,
IDENTIFY_CHECK_ENROLLED_NUM,
@@ -150,8 +201,6 @@ typedef enum {
IDENTIFY_WAIT_FINGER,
IDENTIFY_SENSOR_CHECK,
IDENTIFY_CHECK,
IDENTIFY_COMPLETE_SENSOR_RESET,
IDENTIFY_COMPLETE,
IDENTIFY_STATES,
} IdentifyStates;
@@ -167,11 +216,10 @@ typedef enum {
ENROLL_CAPTURE_SENSOR_RESET,
ENROLL_CAPTURE_SENSOR_START_CAPTURE,
ENROLL_CAPTURE_WAIT_FINGER,
ENROLL_CAPTURE_POST_WAIT_FINGER,
ENROLL_CAPTURE_READ_RESPONSE,
ENROLL_COMMIT_START,
ENROLL_COMMIT,
ENROLL_COMMIT_SENSOR_RESET,
ENROLL_COMPLETE,
ENROLL_STATES,
} EnrollStates;
+4 -21
View File
@@ -146,7 +146,7 @@ elan_save_frame (FpiDeviceElan *self, unsigned short *frame)
raw_idx = frame_margin + y + x * raw_height;
frame_idx = x + y * frame_width;
frame[frame_idx] =
GUINT16_FROM_LE (((unsigned short *) self->last_read)[raw_idx]);
((unsigned short *) self->last_read)[raw_idx];
}
}
@@ -251,12 +251,7 @@ elan_process_frame_linear (unsigned short *raw_frame,
max = raw_frame[i];
}
if (max == min)
{
memset (frame->data, 0, frame_size);
*frames = g_slist_prepend (*frames, frame);
g_return_if_reached ();
}
g_assert (max != min);
unsigned short px;
@@ -292,11 +287,6 @@ elan_process_frame_thirds (unsigned short *raw_frame,
lvl3 = sorted[frame_size - 1];
g_free (sorted);
/* Ensure levels are strictly monotonic to prevent division by zero */
lvl1 = MAX (lvl1, lvl0 + 1);
lvl2 = MAX (lvl2, lvl1 + 1);
lvl3 = MAX (lvl3, lvl2 + 1);
unsigned short px;
for (int i = 0; i < frame_size; i++)
@@ -557,17 +547,10 @@ capture_run_state (FpiSsm *ssm, FpDevice *dev)
fpi_image_device_report_finger_status (idev, TRUE);
elan_run_cmd (ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT);
}
else if (self->dev_type == ELAN_0C58 && self->last_read &&
(self->last_read[0] == 0x00 || self->last_read[0] == 0xaf))
{
/* 0x00 - not ready
* 0xaf - finger removed or sensor busy (seen on 0x0c58) */
fpi_ssm_jump_to_state_delayed (ssm, CAPTURE_WAIT_FINGER, 10);
}
else
{
/* XXX: The timeout is emulated incorrectly, resulting in a zero byte read. */
if (fpi_device_emulation_mode_enabled (FP_DEVICE (self)))
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
fpi_ssm_mark_completed (ssm);
else
fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
@@ -812,7 +795,7 @@ elan_calibrate (FpiDeviceElan *self)
g_return_if_fail (!self->active);
self->active = TRUE;
self->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS (self->dev_type);
self->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS;
FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (self), calibrate_run_state,
CALIBRATE_NUM_STATES);
+2 -5
View File
@@ -31,7 +31,6 @@
#define ELAN_0907 (1 << 0)
#define ELAN_0C03 (1 << 1)
#define ELAN_0C42 (1 << 2)
#define ELAN_0C58 (1 << 3)
/* devices which don't require frame rotation before assembling */
#define ELAN_NOT_ROTATED ELAN_0C03
@@ -45,9 +44,7 @@
/* times to retry reading calibration status during one session
* generally prevents calibration from looping indefinitely */
#define ELAN_DEFAULT_CALIBRATION_ATTEMPTS 10
#define ELAN_CALIBRATION_ATTEMPTS(dev_type) \
((dev_type) == ELAN_0C58 ? 30 : ELAN_DEFAULT_CALIBRATION_ATTEMPTS)
#define ELAN_CALIBRATION_ATTEMPTS 10
/* min and max frames in a capture */
#define ELAN_MIN_FRAMES 7
@@ -223,7 +220,7 @@ static const FpIdEntry elan_id_table[] = {
{.vid = ELAN_VEND_ID, .pid = 0x0c4f, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c63, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c6e, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c58, .driver_data = ELAN_0C58},
{.vid = ELAN_VEND_ID, .pid = 0x0c58, .driver_data = ELAN_ALL_DEV},
{.vid = 0, .pid = 0, .driver_data = 0},
};
+53 -30
View File
@@ -33,14 +33,12 @@ static const FpIdEntry id_table[] = {
{ .vid = 0x04f3, .pid = 0x0c8d, },
{ .vid = 0x04f3, .pid = 0x0c98, },
{ .vid = 0x04f3, .pid = 0x0c99, },
{ .vid = 0x04f3, .pid = 0x0c9c, },
{ .vid = 0x04f3, .pid = 0x0c9d, },
{ .vid = 0x04f3, .pid = 0x0c9f, },
{ .vid = 0x04f3, .pid = 0x0ca3, },
{ .vid = 0x04f3, .pid = 0x0ca7, },
{ .vid = 0x04f3, .pid = 0x0ca8, },
{ .vid = 0x04f3, .pid = 0x0cb0, },
{ .vid = 0x04f3, .pid = 0x0cb2, },
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
};
@@ -390,20 +388,11 @@ elanmoc_enroll_cb (FpiDeviceElanmoc *self,
}
if (self->num_frames == self->max_moc_enroll_time && buffer_in[1] == ELAN_MSG_OK)
{
fpi_ssm_next_state (self->task_ssm);
}
fpi_ssm_next_state (self->task_ssm);
else if (self->num_frames < self->max_moc_enroll_time)
{
fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER);
}
fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER);
else
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Enrollment failed (status 0x%02x)",
buffer_in[1]));
}
fpi_ssm_mark_failed (self->task_ssm, error);
}
}
@@ -648,6 +637,7 @@ elanmoc_match_report_cb (FpiDeviceElanmoc *self,
{
FpDevice *device = FP_DEVICE (self);
FpPrint *print = NULL;
FpPrint *verify_print = NULL;
GPtrArray *prints;
gboolean found = FALSE;
guint index;
@@ -676,18 +666,31 @@ elanmoc_match_report_cb (FpiDeviceElanmoc *self,
fp_info ("Verify/Identify successful for: %s", fp_print_get_description (print));
fpi_device_get_identify_data (device, &prints);
found = g_ptr_array_find_with_equal_func (prints,
print,
(GEqualFunc) fp_print_equal,
&index);
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);
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
fpi_device_identify_report (device, NULL, print, NULL);
{
fpi_device_get_verify_data (device, &verify_print);
fpi_device_identify_complete (device, NULL);
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
@@ -699,7 +702,10 @@ identify_status_report (FpiDeviceElanmoc *self, int verify_status_id,
if (error)
{
fpi_device_identify_complete (device, error);
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
fpi_device_verify_complete (device, error);
else
fpi_device_identify_complete (device, error);
return;
}
@@ -709,8 +715,16 @@ identify_status_report (FpiDeviceElanmoc *self, int verify_status_id,
{
if (data == ELAN_MSG_VERIFY_ERR)
{
fpi_device_identify_report (device, NULL, NULL, NULL);
fpi_device_identify_complete (device, NULL);
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
{
fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL);
fpi_device_verify_complete (device, NULL);
}
else
{
fpi_device_identify_report (device, NULL, NULL, NULL);
fpi_device_identify_complete (device, NULL);
}
}
else
{
@@ -729,15 +743,23 @@ identify_status_report (FpiDeviceElanmoc *self, int verify_status_id,
retry_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL);
}
fpi_device_identify_report (device, NULL, NULL, retry_error);
fpi_device_identify_complete (device, NULL);
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
{
fpi_device_verify_report (device, FPI_MATCH_ERROR, NULL, retry_error);
fpi_device_verify_complete (device, NULL);
}
else
{
fpi_device_identify_report (device, NULL, NULL, retry_error);
fpi_device_identify_complete (device, NULL);
}
}
break;
}
case RSP_VERIFY_OK:
{
fp_dbg ("Identify was successful! for user: %d mesg_code: %d ", data, verify_status_id);
fp_dbg ("Verify was successful! for user: %d mesg_code: %d ", data, verify_status_id);
cmd_buf = elanmoc_compose_cmd (&elanmoc_get_userid_cmd);
cmd_buf[2] = data;
elanmoc_get_cmd (device, cmd_buf, elanmoc_get_userid_cmd.cmd_len, elanmoc_get_userid_cmd.resp_len, 0, elanmoc_match_report_cb);
@@ -791,7 +813,7 @@ elan_identify_run_state (FpiSsm *ssm, FpDevice *dev)
break;
case IDENTIFY_WAIT_FINGER:
fp_info ("elanmoc %s IDENTIFY_WAIT_FINGER", __func__);
fp_info ("elanmoc %s VERIFY_WAIT_FINGER", __func__);
cmd_buf = elanmoc_compose_cmd (&elanmoc_verify_cmd);
elanmoc_get_cmd (dev, cmd_buf, elanmoc_verify_cmd.cmd_len, elanmoc_verify_cmd.resp_len, 1, elanmoc_identify_cb);
break;
@@ -1151,6 +1173,7 @@ fpi_device_elanmoc_class_init (FpiDeviceElanmocClass *klass)
dev_class->open = elanmoc_open;
dev_class->close = elanmoc_close;
dev_class->verify = elanmoc_identify;
dev_class->enroll = elanmoc_enroll;
dev_class->identify = elanmoc_identify;
dev_class->delete = elanmoc_delete_print;
+3 -4
View File
@@ -23,9 +23,9 @@
#include "drivers_api.h"
#include "elanspi.h"
#include <fcntl.h>
#include <linux/hidraw.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
@@ -94,7 +94,7 @@ static void
elanspi_do_hwreset (FpiDeviceElanSpi *self, GError **err)
{
/* Skip in emulation mode, since we don't mock hid devices */
if (fpi_device_emulation_mode_enabled (FP_DEVICE (self)))
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
return;
/*
@@ -485,8 +485,7 @@ elanspi_capture_old_handler (FpiSsm *ssm, FpDevice *dev)
if (!(self->sensor_status & 4))
{
/* has the timeout expired? -- disabled in testing since valgrind is very slow */
if (g_get_monotonic_time () > self->capture_timeout &&
!fpi_device_emulation_mode_enabled (FP_DEVICE (self)))
if (g_get_monotonic_time () > self->capture_timeout && g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") != 0)
{
/* end with a timeout */
fpi_ssm_mark_failed (ssm, g_error_new (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "timed out waiting for new line"));
-19
View File
@@ -338,14 +338,7 @@ msg_parse_regs (FpiDeviceEtes603 *dev)
struct egis_msg *msg_req = dev->req;
struct egis_msg *msg_ans = dev->ans;
g_return_val_if_fail (dev->ans_len >= MSG_HDR_SIZE, -1);
n_args = dev->ans_len - MSG_HDR_SIZE;
if (n_args > REG_MAX)
{
g_warn_if_reached ();
n_args = REG_MAX;
}
if (msg_header_check (msg_ans))
return -1;
@@ -582,24 +575,12 @@ static void
process_removefpi_end (FpiDeviceEtes603 *dev)
{
unsigned int i;
/* Need at least the 2-line empty pattern to compare against. */
if (dev->fp_height < 2)
g_return_if_reached ();
/* 2 last lines with Fly-Estimation are the empty pattern. */
guint8 *pattern = dev->fp + (dev->fp_height - 2) * FE_WIDTH / 2;
for (i = 2; i < dev->fp_height; i += 2)
if (memcmp (pattern, pattern - (i * FE_WIDTH / 2), FE_WIDTH))
break;
if (i > dev->fp_height)
{
g_warn_if_reached ();
i = dev->fp_height;
}
dev->fp_height -= i;
fp_dbg ("Removing %d empty lines from image", i - 2);
}
+112 -252
View File
@@ -22,9 +22,6 @@
#define FP_COMPONENT "focaltech_moc"
#define FOCALTECH_QUIRK_SINGLE_SLOT 1
#define FOCALTECH_MOC_STATIC_USER_ID "focaltech_moc_static_id"
#include "drivers_api.h"
G_DEFINE_TYPE (FpiDeviceFocaltechMoc, fpi_device_focaltech_moc, FP_TYPE_DEVICE)
@@ -37,12 +34,9 @@ static const FpIdEntry id_table[] = {
{ .vid = 0x2808, .pid = 0xa99a, },
{ .vid = 0x2808, .pid = 0xa57a, },
{ .vid = 0x2808, .pid = 0xa78a, },
{ .vid = 0x2808, .pid = 0xa97a, },
{ .vid = 0x2808, .pid = 0x1579, },
{ .vid = 0x2808, .pid = 0x077A, },
{ .vid = 0x2808, .pid = 0x079A, },
{ .vid = 0x2808, .pid = 0x5158, },
{ .vid = 0x2808, .pid = 0x6553, .driver_data = FOCALTECH_QUIRK_SINGLE_SLOT, },
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
};
@@ -199,22 +193,6 @@ focaltech_moc_check_cmd (uint8_t *response_buf, uint16_t len)
return ret;
}
static gboolean
focaltech_moc_require_response_len (FpiDeviceFocaltechMoc *self,
gsize length_in,
gsize minimum_length,
const char *context)
{
if (length_in >= minimum_length)
return TRUE;
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"%s: short response (%zu < %zu)",
context, length_in, minimum_length));
return FALSE;
}
static void
fp_cmd_receive_cb (FpiUsbTransfer *transfer,
FpDevice *device,
@@ -460,16 +438,15 @@ get_g_usb_device_direction_des (GUsbDeviceDirection dir)
}
}
static gboolean
usb_claim_interface_probe (FpDevice *device, gboolean claim, GError **error)
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);
if (interfaces == NULL)
return FALSE;
for (i = 0; i < interfaces->len; i++)
{
@@ -481,9 +458,11 @@ usb_claim_interface_probe (FpDevice *device, gboolean claim, GError **error)
g_usb_interface_get_subclass (cur_iface),
g_usb_interface_get_protocol (cur_iface));
if (claim)
if (claim == 1)
{
for (int j = 0; j < endpoints->len; j++)
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);
@@ -510,17 +489,21 @@ usb_claim_interface_probe (FpDevice *device, gboolean claim, GError **error)
if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device),
g_usb_interface_get_number (cur_iface),
0, error))
return FALSE;
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 FALSE;
return ret;
}
}
return TRUE;
ret = 0;
return ret;
}
static void
@@ -529,7 +512,7 @@ task_ssm_init_done (FpiSsm *ssm, FpDevice *device, GError *error)
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
if (error)
usb_claim_interface_probe (device, FALSE, NULL);
usb_claim_interface_probe (device, 0, &error);
fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error));
}
@@ -644,8 +627,8 @@ dev_init_handler (FpiSsm *ssm, FpDevice *device)
static void
focaltech_moc_open (FpDevice *device)
{
g_autoptr(GError) error = NULL;
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
GError *error = NULL;
if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error))
{
@@ -653,7 +636,7 @@ focaltech_moc_open (FpDevice *device)
return;
}
if (!usb_claim_interface_probe (device, TRUE, &error))
if (usb_claim_interface_probe (device, 1, &error) != 0)
{
fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error));
return;
@@ -669,7 +652,12 @@ task_ssm_exit_done (FpiSsm *ssm, FpDevice *device, GError *error)
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
if (!error)
usb_claim_interface_probe (device, FALSE, &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;
@@ -807,8 +795,16 @@ focaltech_moc_identify_capture_cb (FpiDeviceFocaltechMoc *self,
}
else
{
fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, error);
fpi_device_identify_complete (FP_DEVICE (self), NULL);
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));
}
@@ -822,27 +818,50 @@ identify_status_report (FpiDeviceFocaltechMoc *self, FpPrint *print, GError *err
if (print == NULL)
{
fpi_device_identify_report (device, NULL, NULL, NULL);
fpi_device_identify_complete (device, 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
{
GPtrArray *prints;
gboolean found = FALSE;
guint index;
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);
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);
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
fpi_device_identify_report (device, NULL, print, NULL);
{
FpPrint *verify_print = NULL;
fpi_device_get_verify_data (device, &verify_print);
fpi_device_identify_complete (device, NULL);
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);
}
}
}
@@ -1023,15 +1042,10 @@ focaltech_moc_get_enrolled_info_cb (FpiDeviceFocaltechMoc *self,
return;
}
if (!focaltech_moc_require_response_len (self, length_in,
sizeof (FpCmd) + sizeof (uint8_t),
"get enrolled info"))
return;
fp_cmd = (FpCmd *) buffer_in;
items = (struct EnrolledInfoItem *) (fp_cmd + 1);
if (fp_cmd->code != 0x04 && fp_cmd->code != 0x09)
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
@@ -1039,25 +1053,8 @@ focaltech_moc_get_enrolled_info_cb (FpiDeviceFocaltechMoc *self,
}
else
{
if (fp_cmd->code == 0x04)
{
if (!focaltech_moc_require_response_len (self, length_in,
sizeof (FpCmd) +
FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem) +
sizeof (uint8_t),
"get enrolled info"))
return;
memcpy (&data->enrolled_info->items[0], items,
FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem));
}
else if (fp_cmd->code == 0x09)
{
memset (data->enrolled_info->actived, 0, FOCALTECH_MOC_MAX_FINGERS);
memset (&data->enrolled_info->items[0], 0,
FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem));
}
memcpy (&data->enrolled_info->items[0], items,
FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem));
fpi_ssm_next_state (self->task_ssm);
}
}
@@ -1109,15 +1106,10 @@ focaltech_moc_get_enrolled_list_cb (FpiDeviceFocaltechMoc *self,
return;
}
if (!focaltech_moc_require_response_len (self, length_in,
sizeof (FpCmd) + sizeof (uint8_t),
"get enrolled list"))
return;
fp_cmd = (FpCmd *) buffer_in;
uid_list = (struct UidList *) (fp_cmd + 1);
if (fp_cmd->code != 0x04 && fp_cmd->code != 0x09)
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
@@ -1126,59 +1118,48 @@ focaltech_moc_get_enrolled_list_cb (FpiDeviceFocaltechMoc *self,
else
{
FpActionData *data = fpi_ssm_get_data (self->task_ssm);
int i;
if (fp_cmd->code == 0x04)
for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++)
{
if (!focaltech_moc_require_response_len (self, length_in,
sizeof (FpCmd) +
sizeof (struct UidList) +
sizeof (uint8_t),
"get enrolled list"))
return;
for (size_t i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++)
if (uid_list->actived[i] != 0)
{
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)
{
struct UserId *user_id = &uid_list->uid[i];
FpPrint *print = fp_print_new (FP_DEVICE (self));
struct EnrolledInfoItem *item = NULL;
int index;
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);
fp_info ("focaltechmoc add slot: %zu", 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)
g_strlcpy (data->enrolled_info->user_des[index].username,
username,
sizeof (data->enrolled_info->user_des[index].username));
}
g_ptr_array_add (data->list_result, g_object_ref_sink (print));
if (username != NULL)
strncpy (data->enrolled_info->user_des[index].username, username, 64);
}
}
for (size_t 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));
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);
}
}
@@ -1298,21 +1279,9 @@ focaltech_moc_start_enroll_cb (FpiDeviceFocaltechMoc *self,
}
else
{
guint64 quirk = fpi_device_get_driver_data (FP_DEVICE (self));
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
fprint_set_uid (print, user_id->uid, sizeof (user_id->uid));
if (quirk == FOCALTECH_QUIRK_SINGLE_SLOT)
{
fp_info ("Applying SINGLE_SLOT quirk: Forcing static User ID");
userid_safe = g_strdup (FOCALTECH_MOC_STATIC_USER_ID);
}
else
{
userid_safe = fpi_print_generate_user_id (print);
}
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);
@@ -1381,14 +1350,9 @@ focaltech_moc_set_enrolled_info_cb (FpiDeviceFocaltechMoc *self,
return;
}
if (!focaltech_moc_require_response_len (self, length_in,
sizeof (FpCmd) + sizeof (uint8_t),
"set enrolled info"))
return;
fp_cmd = (FpCmd *) buffer_in;
if (fp_cmd->code != 0x04 && fp_cmd->code != 0x09)
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
@@ -1413,14 +1377,9 @@ focaltech_moc_commit_cb (FpiDeviceFocaltechMoc *self,
return;
}
if (!focaltech_moc_require_response_len (self, length_in,
sizeof (FpCmd) + sizeof (uint8_t),
"commit"))
return;
fp_cmd = (FpCmd *) buffer_in;
if (fp_cmd->code != 0x04 && fp_cmd->code != 0x09)
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
@@ -1613,14 +1572,9 @@ focaltech_moc_delete_cb (FpiDeviceFocaltechMoc *self,
return;
}
if (!focaltech_moc_require_response_len (self, length_in,
sizeof (FpCmd) + sizeof (uint8_t),
"delete"))
return;
fp_cmd = (FpCmd *) buffer_in;
if (fp_cmd->code != 0x04 && fp_cmd->code != 0x09)
if (fp_cmd->code != 0x04)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
@@ -1815,9 +1769,8 @@ static void
focaltech_moc_delete_print (FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
FpActionData *data = NULL;
FpActionData *data = g_new0 (FpActionData, 1);
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);
@@ -1828,87 +1781,6 @@ focaltech_moc_delete_print (FpDevice *device)
fpi_ssm_start (self->task_ssm, task_ssm_done);
}
enum moc_clear_storage_states {
MOC_CLEAR_STORAGE_SEND,
MOC_CLEAR_STORAGE_NUM_STATES,
};
static void
focaltech_moc_clear_storage_cb (FpiDeviceFocaltechMoc *self,
uint8_t *buffer_in,
gsize length_in,
GError *error)
{
FpCmd *fp_cmd = NULL;
if (error)
{
fp_warn ("Mass erase command completed with warning: %s", error->message);
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
if (length_in == 0)
{
fpi_device_clear_storage_complete (FP_DEVICE (self), NULL);
fpi_ssm_next_state (self->task_ssm);
return;
}
if (!focaltech_moc_require_response_len (self, length_in,
sizeof (FpCmd) + sizeof (uint8_t),
"clear storage"))
return;
fp_cmd = (FpCmd *) buffer_in;
if (fp_cmd->code != 0x04 && fp_cmd->code != 0x09)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Clear storage failed: unexpected response code 0x%02x",
fp_cmd->code));
return;
}
fpi_device_clear_storage_complete (FP_DEVICE (self), NULL);
fpi_ssm_next_state (self->task_ssm);
}
static void
focaltech_clear_storage_run_state (FpiSsm *ssm, FpDevice *device)
{
guint64 quirk = fpi_device_get_driver_data (device);
guint8 *cmd_buf = NULL;
uint16_t cmd_len = 0;
uint16_t resp_len = 0;
switch (fpi_ssm_get_cur_state (ssm))
{
case MOC_CLEAR_STORAGE_SEND:
resp_len = quirk == FOCALTECH_QUIRK_SINGLE_SLOT ? 0 : sizeof (uint8_t);
cmd_buf = focaltech_moc_compose_cmd (0xac, NULL, cmd_len);
focaltech_moc_get_cmd (device, cmd_buf,
sizeof (FpCmd) + cmd_len + sizeof (uint8_t),
sizeof (FpCmd) + resp_len + sizeof (uint8_t),
FALSE,
focaltech_moc_clear_storage_cb);
break;
}
}
static void
focaltech_moc_clear_storage (FpDevice *device)
{
FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device);
self->task_ssm = fpi_ssm_new (device,
focaltech_clear_storage_run_state,
MOC_CLEAR_STORAGE_NUM_STATES);
fpi_ssm_start (self->task_ssm, task_ssm_done);
}
enum moc_list_states {
MOC_LIST_GET_ENROLLED_INFO,
MOC_LIST_GET_ENROLLED_LIST,
@@ -1984,17 +1856,6 @@ fpi_device_focaltech_moc_init (FpiDeviceFocaltechMoc *self)
G_DEBUG_HERE ();
}
static void
focaltech_moc_probe (FpDevice *device)
{
guint64 quirk = fpi_device_get_driver_data (device);
if (quirk == FOCALTECH_QUIRK_SINGLE_SLOT)
fpi_device_update_features (device, FP_DEVICE_FEATURE_STORAGE_DELETE, 0);
fpi_device_probe_complete (device, NULL, NULL, NULL);
}
static void
fpi_device_focaltech_moc_class_init (FpiDeviceFocaltechMocClass *klass)
{
@@ -2009,13 +1870,12 @@ fpi_device_focaltech_moc_class_init (FpiDeviceFocaltechMocClass *klass)
dev_class->nr_enroll_stages = FOCALTECH_MOC_MAX_FINGERS;
dev_class->temp_hot_seconds = -1;
dev_class->probe = focaltech_moc_probe;
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->clear_storage = focaltech_moc_clear_storage;
dev_class->list = focaltech_moc_list;
fpi_device_class_auto_initialize_features (dev_class);
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -207,11 +207,11 @@ typedef enum {
} FpcEnrollState;
typedef enum {
FPC_IDENTIFY_CAPTURE = 0,
FPC_IDENTIFY_GET_IMG,
FPC_IDENTIFY_IDENTIFY,
FPC_IDENTIFY_CANCEL,
FPC_IDENTIFY_NUM_STATES,
FPC_VERIFY_CAPTURE = 0,
FPC_VERIFY_GET_IMG,
FPC_VERIFY_IDENTIFY,
FPC_VERIFY_CANCEL,
FPC_VERIFY_NUM_STATES,
} FpcVerifyState;
typedef enum {
+78 -68
View File
@@ -19,7 +19,6 @@
#include "glib.h"
#define FP_COMPONENT "goodixmoc"
#include "drivers_api.h"
@@ -157,7 +156,8 @@ fp_cmd_receive_cb (FpiUsbTransfer *transfer,
return;
}
fpi_byte_reader_init (&reader, transfer->buffer, transfer->actual_length);
reader.data = transfer->buffer;
reader.size = transfer->actual_length;
if (gx_proto_parse_header (&reader, &header) != 0)
{
@@ -167,25 +167,17 @@ fp_cmd_receive_cb (FpiUsbTransfer *transfer,
return;
}
if (header.len < PACKAGE_CRC_SIZE ||
!fpi_byte_reader_set_pos (&reader, PACKAGE_HEADER_SIZE + header.len - PACKAGE_CRC_SIZE))
if (!fpi_byte_reader_set_pos (&reader, PACKAGE_HEADER_SIZE + header.len))
{
fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Package crc read failed"));
return;
}
/* Exclude the CRC from the package length */
header.len -= PACKAGE_CRC_SIZE;
/* The reader is positioned right at the CRC, i.e. at the end of the data the
* CRC is computed over, so use its position as the length. */
gx_proto_crc32_calc (transfer->buffer, fpi_byte_reader_get_pos (&reader),
(uint8_t *) &crc32_calc);
gx_proto_crc32_calc (transfer->buffer, PACKAGE_HEADER_SIZE + header.len, (uint8_t *) &crc32_calc);
if (!fpi_byte_reader_get_uint32_le (&reader, &crc32) ||
GUINT32_FROM_LE (crc32_calc) != crc32)
crc32_calc != crc32)
{
fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
@@ -195,7 +187,9 @@ fp_cmd_receive_cb (FpiUsbTransfer *transfer,
cmd = MAKE_CMD_EX (header.cmd0, header.cmd1);
fpi_byte_reader_init (&reader, &transfer->buffer[PACKAGE_HEADER_SIZE], header.len);
fpi_byte_reader_set_pos (&reader, 0);
reader.data = &transfer->buffer[PACKAGE_HEADER_SIZE];
reader.size = header.len;
if (gx_proto_parse_body (cmd, &reader, &cmd_reponse) != 0)
{
@@ -399,13 +393,13 @@ fp_pwr_btn_shield_cb (FpiDeviceGoodixMoc *self,
}
/******************************************************************************
*
* fp_identify_xxxx Function
* fp_verify_xxxx Function
*
*****************************************************************************/
static void
fp_identify_capture_cb (FpiDeviceGoodixMoc *self,
gxfp_cmd_response_t *resp,
GError *error)
fp_verify_capture_cb (FpiDeviceGoodixMoc *self,
gxfp_cmd_response_t *resp,
GError *error)
{
if (error)
{
@@ -438,9 +432,9 @@ fp_identify_capture_cb (FpiDeviceGoodixMoc *self,
}
static void
fp_identify_cb (FpiDeviceGoodixMoc *self,
gxfp_cmd_response_t *resp,
GError *error)
fp_verify_cb (FpiDeviceGoodixMoc *self,
gxfp_cmd_response_t *resp,
GError *error)
{
FpDevice *device = FP_DEVICE (self);
FpPrint *new_scan = NULL;
@@ -451,30 +445,45 @@ fp_identify_cb (FpiDeviceGoodixMoc *self,
fpi_ssm_mark_failed (self->task_ssm, error);
return;
}
if (resp->verify.match)
{
GPtrArray *templates = NULL;
guint matching_index;
fpi_device_get_identify_data (device, &templates);
new_scan = fp_print_from_template (self, &resp->verify.template);
if (g_ptr_array_find_with_equal_func (templates, new_scan,
(GEqualFunc) fp_print_equal,
&matching_index))
matching = g_ptr_array_index (templates, matching_index);
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
{
fpi_device_get_verify_data (device, &matching);
if (!fp_print_equal (matching, new_scan))
matching = NULL;
}
else
{
GPtrArray *templates = NULL;
fpi_device_get_identify_data (device, &templates);
for (gint i = 0; i < templates->len; i++)
{
if (fp_print_equal (g_ptr_array_index (templates, i), new_scan))
{
matching = g_ptr_array_index (templates, i);
break;
}
}
}
}
fpi_device_identify_report (device, matching, new_scan, error);
if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY)
fpi_device_verify_report (device, matching ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL, new_scan, error);
else
fpi_device_identify_report (device, matching, new_scan, error);
fpi_ssm_next_state (self->task_ssm);
}
static void
fp_identify_finger_mode_cb (FpiDeviceGoodixMoc *self,
gxfp_cmd_response_t *resp,
GError *error)
fp_verify_finger_mode_cb (FpiDeviceGoodixMoc *self,
gxfp_cmd_response_t *resp,
GError *error)
{
if (error)
{
@@ -484,7 +493,7 @@ fp_identify_finger_mode_cb (FpiDeviceGoodixMoc *self,
/* if reach max timeout(5sec) finger not up, try again */
if (resp->finger_status.status == GX_ERROR_WAIT_FINGER_UP_TIMEOUT)
{
fpi_ssm_jump_to_state (self->task_ssm, GOODIX_IDENTIFY_WAIT_FINGER_UP);
fpi_ssm_jump_to_state (self->task_ssm, GOODIX_VERIFY_WAIT_FINGER_UP);
return;
}
else if (resp->finger_status.status != GX_SUCCESS)
@@ -501,7 +510,7 @@ fp_identify_finger_mode_cb (FpiDeviceGoodixMoc *self,
}
static void
fp_identify_sm_run_state (FpiSsm *ssm, FpDevice *device)
fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device)
{
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
guint8 param[3] = { 0 };
@@ -513,7 +522,7 @@ fp_identify_sm_run_state (FpiSsm *ssm, FpDevice *device)
switch (fpi_ssm_get_cur_state (ssm))
{
case GOODIX_IDENTIFY_PWR_BTN_SHIELD_ON:
case GOODIX_VERIFY_PWR_BTN_SHIELD_ON:
goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_ON,
false,
NULL,
@@ -521,7 +530,7 @@ fp_identify_sm_run_state (FpiSsm *ssm, FpDevice *device)
fp_pwr_btn_shield_cb);
break;
case GOODIX_IDENTIFY_CAPTURE:
case GOODIX_VERIFY_CAPTURE:
fpi_device_report_finger_status_changes (device,
FP_FINGER_STATUS_NEEDED,
FP_FINGER_STATUS_NONE);
@@ -529,29 +538,29 @@ fp_identify_sm_run_state (FpiSsm *ssm, FpDevice *device)
true,
(const guint8 *) &param,
G_N_ELEMENTS (param),
fp_identify_capture_cb);
fp_verify_capture_cb);
break;
case GOODIX_IDENTIFY_IDENTIFY:
case GOODIX_VERIFY_IDENTIFY:
goodix_sensor_cmd (self, MOC_CMD0_IDENTIFY, MOC_CMD1_DEFAULT,
false,
(const guint8 *) nonce,
TEMPLATE_ID_SIZE,
fp_identify_cb);
fp_verify_cb);
break;
case GOODIX_IDENTIFY_WAIT_FINGER_UP:
case GOODIX_VERIFY_WAIT_FINGER_UP:
{
guint8 dummy = 0;
goodix_sensor_cmd (self, MOC_CMD0_FINGER_MODE, MOC_CMD1_SET_FINGER_UP,
true,
&dummy,
1,
fp_identify_finger_mode_cb);
fp_verify_finger_mode_cb);
}
break;
case GOODIX_IDENTIFY_PWR_BTN_SHIELD_OFF:
case GOODIX_VERIFY_PWR_BTN_SHIELD_OFF:
goodix_sensor_cmd (self, MOC_CMD0_PWR_BTN_SHIELD, MOC_CMD1_PWR_BTN_SHIELD_OFF,
false,
NULL,
@@ -563,16 +572,25 @@ fp_identify_sm_run_state (FpiSsm *ssm, FpDevice *device)
}
static void
fp_identify_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
fp_verify_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
{
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (dev);
fp_info ("Verify complete!");
if (error && error->domain == FP_DEVICE_RETRY)
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_report (dev, FPI_MATCH_ERROR, NULL, g_steal_pointer (&error));
else
fpi_device_identify_report (dev, NULL, NULL, g_steal_pointer (&error));
}
fpi_device_identify_complete (dev, 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;
}
@@ -753,7 +771,7 @@ fp_enroll_update_cb (FpiDeviceGoodixMoc *self,
NULL,
fpi_device_retry_new (FP_DEVICE_RETRY_REMOVE_FINGER));
}
else if (resp->enroll_update.rollback || resp->result == 0x73)
else if (resp->enroll_update.rollback)
{
fpi_device_enroll_progress (FP_DEVICE (self),
self->enroll_stage,
@@ -952,7 +970,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device)
user_id_len = MIN (100, user_id_len);
finger = 1;
if (fpi_device_emulation_mode_enabled (FP_DEVICE (self)))
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
memset (self->template_id, 0, TEMPLATE_ID_SIZE);
uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
user_id,
@@ -1372,7 +1390,7 @@ gx_fp_probe (FpDevice *device)
if (!g_usb_device_claim_interface (usb_dev, 0, 0, &error))
goto err_close;
if (fpi_device_emulation_mode_enabled (FP_DEVICE (self)))
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
{
serial = g_strdup ("emulated-device");
@@ -1399,7 +1417,6 @@ gx_fp_probe (FpDevice *device)
case 0x60A2:
case 0x60A4:
case 0x6014:
case 0x6090:
case 0x6092:
case 0x6094:
case 0x609A:
@@ -1410,7 +1427,6 @@ gx_fp_probe (FpDevice *device)
case 0x631C:
case 0x633C:
case 0x634C:
case 0x6382:
case 0x6384:
case 0x639C:
case 0x63AC:
@@ -1421,8 +1437,6 @@ gx_fp_probe (FpDevice *device)
case 0x6582:
case 0x6A94:
case 0x659A:
case 0x6890:
case 0x6984:
self->max_enroll_stage = 12;
break;
@@ -1539,16 +1553,16 @@ gx_fp_exit (FpDevice *device)
static void
gx_fp_identify (FpDevice *device)
gx_fp_verify_identify (FpDevice *device)
{
FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device);
self->task_ssm = fpi_ssm_new_full (device, fp_identify_sm_run_state,
GOODIX_IDENTIFY_NUM_STATES,
GOODIX_IDENTIFY_PWR_BTN_SHIELD_OFF,
"identify");
self->task_ssm = fpi_ssm_new_full (device, fp_verify_sm_run_state,
GOODIX_VERIFY_NUM_STATES,
GOODIX_VERIFY_PWR_BTN_SHIELD_OFF,
"verify");
fpi_ssm_start (self->task_ssm, fp_identify_ssm_done);
fpi_ssm_start (self->task_ssm, fp_verify_ssm_done);
}
@@ -1601,7 +1615,7 @@ gx_fp_template_delete (FpDevice *device)
gsize user_id_len = 0;
const guint8 *tid;
gsize tid_len = 0;
guint16 payload_len = 0;
gsize payload_len = 0;
g_autofree guint8 *payload = NULL;
fpi_device_get_delete_data (device, &print);
@@ -1614,8 +1628,7 @@ gx_fp_template_delete (FpDevice *device)
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
return;
}
if (!encode_finger_id (tid, tid_len, user_id, user_id_len, &payload, &payload_len))
if (!encode_finger_id (tid, tid_len, user_id, user_id_len, &payload, (guint16 *) &payload_len))
{
fpi_device_delete_complete (device,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
@@ -1653,7 +1666,6 @@ fpi_device_goodixmoc_init (FpiDeviceGoodixMoc *self)
static const FpIdEntry id_table[] = {
{ .vid = 0x27c6, .pid = 0x5840, },
{ .vid = 0x27c6, .pid = 0x6014, },
{ .vid = 0x27c6, .pid = 0x6090, },
{ .vid = 0x27c6, .pid = 0x6092, },
{ .vid = 0x27c6, .pid = 0x6094, },
{ .vid = 0x27c6, .pid = 0x609A, },
@@ -1666,7 +1678,6 @@ static const FpIdEntry id_table[] = {
{ .vid = 0x27c6, .pid = 0x631C, },
{ .vid = 0x27c6, .pid = 0x633C, },
{ .vid = 0x27c6, .pid = 0x634C, },
{ .vid = 0x27c6, .pid = 0x6382, },
{ .vid = 0x27c6, .pid = 0x6384, },
{ .vid = 0x27c6, .pid = 0x639C, },
{ .vid = 0x27c6, .pid = 0x63AC, },
@@ -1684,10 +1695,8 @@ static const FpIdEntry id_table[] = {
{ .vid = 0x27c6, .pid = 0x659C, },
{ .vid = 0x27c6, .pid = 0x6A94, },
{ .vid = 0x27c6, .pid = 0x6512, },
{ .vid = 0x27c6, .pid = 0x6890, },
{ .vid = 0x27c6, .pid = 0x689A, },
{ .vid = 0x27c6, .pid = 0x66A9, },
{ .vid = 0x27c6, .pid = 0x6984, },
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
};
@@ -1712,7 +1721,8 @@ fpi_device_goodixmoc_class_init (FpiDeviceGoodixMocClass *klass)
dev_class->delete = gx_fp_template_delete;
dev_class->clear_storage = gx_fp_template_delete_all;
dev_class->list = gx_fp_template_list;
dev_class->identify = gx_fp_identify;
dev_class->verify = gx_fp_verify_identify;
dev_class->identify = gx_fp_verify_identify;
fpi_device_class_auto_initialize_features (dev_class);
dev_class->features |= FP_DEVICE_FEATURE_DUPLICATES_CHECK;
+6 -6
View File
@@ -55,10 +55,10 @@ typedef enum {
} GoodixEnrollState;
typedef enum {
GOODIX_IDENTIFY_PWR_BTN_SHIELD_ON = 0,
GOODIX_IDENTIFY_CAPTURE,
GOODIX_IDENTIFY_IDENTIFY,
GOODIX_IDENTIFY_WAIT_FINGER_UP,
GOODIX_IDENTIFY_PWR_BTN_SHIELD_OFF,
GOODIX_IDENTIFY_NUM_STATES,
GOODIX_VERIFY_PWR_BTN_SHIELD_ON = 0,
GOODIX_VERIFY_CAPTURE,
GOODIX_VERIFY_IDENTIFY,
GOODIX_VERIFY_WAIT_FINGER_UP,
GOODIX_VERIFY_PWR_BTN_SHIELD_OFF,
GOODIX_VERIFY_NUM_STATES,
} GoodixVerifyState;
+17 -18
View File
@@ -253,6 +253,8 @@ gx_proto_parse_header (FpiByteReader *reader,
if (!fpi_byte_reader_get_uint8 (reader, &pheader->rev_crc8))
g_return_val_if_reached (-1);
pheader->len -= PACKAGE_CRC_SIZE;
return 0;
}
@@ -261,6 +263,7 @@ gx_proto_parse_fingerid (FpiByteReader *reader,
ptemplate_format_t template)
{
uint8_t byte;
const uint8_t *buffer;
if (!template)
return -1;
@@ -277,25 +280,24 @@ gx_proto_parse_fingerid (FpiByteReader *reader,
if (!fpi_byte_reader_skip (reader, 1))
g_return_val_if_reached (-1);
if (!fpi_byte_reader_get_data_static (reader, template->accountid))
if (!fpi_byte_reader_get_data (reader, sizeof (template->accountid), &buffer))
g_return_val_if_reached (-1);
if (!fpi_byte_reader_get_data_static (reader, template->tid))
memcpy (template->accountid, buffer, sizeof (template->accountid));
if (!fpi_byte_reader_get_data (reader, sizeof (template->tid), &buffer))
g_return_val_if_reached (-1);
memcpy (template->tid, buffer, sizeof (template->tid));
if (!fpi_byte_reader_get_uint8 (reader, &template->payload.size))
g_return_val_if_reached (-1);
if (template->payload.size > sizeof (template->payload.data))
{
g_warning ("Device reported oversized payload (%d)", template->payload.size);
return -1;
}
if (!(fpi_byte_reader_get_data_static) (reader, template->payload.size,
template->payload.data))
if (!fpi_byte_reader_get_data (reader, template->payload.size, &buffer))
g_return_val_if_reached (-1);
memcpy (template->payload.data, buffer, template->payload.size);
return 0;
}
@@ -411,13 +413,6 @@ gx_proto_parse_body (uint16_t cmd, FpiByteReader *byte_reader, pgxfp_cmd_respons
&presp->finger_list_resp.finger_num))
g_return_val_if_reached (-1);
if (presp->finger_list_resp.finger_num > FP_MAX_FINGERNUM)
{
g_warning ("Device reported too many fingers (%d)",
presp->finger_list_resp.finger_num);
return -1;
}
for(uint8_t num = 0; num < presp->finger_list_resp.finger_num; num++)
{
uint16_t fingerid_length;
@@ -498,6 +493,8 @@ static uint8_t sensor_config[26] = {
int
gx_proto_init_sensor_config (pgxfp_sensor_cfg_t pconfig)
{
uint32_t crc32_calc = 0;
if (!pconfig)
return -1;
memset (pconfig, 0, sizeof (*pconfig));
@@ -506,7 +503,9 @@ gx_proto_init_sensor_config (pgxfp_sensor_cfg_t pconfig)
memcpy (&pconfig->config, sensor_config, G_N_ELEMENTS (sensor_config));
pconfig->reserved[0] = 1;
gx_proto_crc32_calc ((uint8_t *) pconfig, sizeof (*pconfig) - PACKAGE_CRC_SIZE, pconfig->crc_value);
gx_proto_crc32_calc ((uint8_t *) pconfig, sizeof (*pconfig) - PACKAGE_CRC_SIZE, (uint8_t *) &crc32_calc);
memcpy (pconfig->crc_value, &crc32_calc, PACKAGE_CRC_SIZE);
return 0;
}
File diff suppressed because it is too large Load Diff
-261
View File
@@ -1,261 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <glib.h>
#include <math.h>
#include "fpi-device.h"
#include "fpi-ssm.h"
#include "pwd.h"
#include <sys/utsname.h>
G_DECLARE_FINAL_TYPE (FpiDeviceMafpmoc, fpi_device_mafpmoc, FPI, DEVICE_MAFPMOC, FpDevice)
#define PRINT_CMD 0
#define PRINT_SSM_DEBUG 0
#define MAFP_INTERFACE_CLASS G_USB_DEVICE_CLASS_VENDOR_SPECIFIC
#define MAFP_INTERFACE_SUB_CLASS 0x01
#define MAFP_INTERFACE_PROTOCOL 0x01
/* Usb port setting */
#define MAFP_EP_BULK_OUT 0x03
#define MAFP_EP_BULK_IN 0x83
#define MAFP_EP_INT_IN 0x82
/* Command transfer timeout: ms */
#define CMD_TIMEOUT 5000
#define DATA_TIMEOUT 5000
#define CTRL_TIMEOUT 200
#define MAFP_USB_BUFFER_SIZE 512
#define PACKAGE_CRC_SIZE 2
#define PACKAGE_HEADER_SIZE 9
#define PACKAGE_DATA_SIZE_MAX (MAFP_USB_BUFFER_SIZE - PACKAGE_HEADER_SIZE - PACKAGE_CRC_SIZE)
#define TEMPLATE_ID_SIZE 2
#define TEMPLATE_UID_SIZE 128
#define DEVICE_SN_SIZE 32
#define MAX_FINGER_NUM 10
#define MAX_USER_NUM 3
#define MAX_NOTEPAD_PAGE 16
#define MOC_CMD_GET_IMAGE 0x01
#define MOC_CMD_GEN_FEATURE 0x02
#define MOC_CMD_SEARCH 0x04
#define MOC_CMD_GEN_TEMPLATE 0x05
#define MOC_CMD_SAVE_TEMPLATE 0x06
#define MOC_CMD_READ_TEMPLATE 0x07
#define MOC_CMD_DELETE_TEMPLATE 0x0C
#define MOC_CMD_EMPTY 0x0D
#define MOC_CMD_WRITE_NOTEPAD 0x18
#define MOC_CMD_READ_NOTEPAD 0x19
#define MOC_CMD_GET_TEMPLATE_NUM 0x1D
#define MOC_CMD_GET_TEMPLATE_TABLE 0x1F
#define MOC_CMD_CANCEL 0x30
#define MOC_CMD_SLEEP 0x33
#define MOC_CMD_HANDSHAKE 0x35
#define MOC_CMD_CALIBRATE 0x36
#define MOC_CMD_FACTORY_RESET 0x3B
#define MOC_CMD_FACTORY_TEST 0x56
#define MOC_CMD_MATCH_WITHFID 0x66
#define MOC_CMD_GET_MAX_ID 0x6d
#define MOC_CMD_DUPAREA_TEST 0x6f
#define MOC_CMD_SAVE_TEMPLATE_INFO 0x86
#define MOC_CMD_GET_TEMPLATE_INFO 0x87
#define MOC_CMD_GET_INIT_STATUS 0x88
#define MAFP_SUCCESS 0
#define MAFP_RE_TPL_NUM_OVERSIZE 0x0B
#define MAFP_RE_GET_IMAGE_SUCCESS 0x00
#define MAFP_RE_GET_IMAGE_NONE 0x02
/* calibrate error (un-calibrate or calibrate failed) */
#define MAFP_RE_CALIBRATE_ERROR 0x02
#define MAFP_HANDSHAKE_CODE1 'M'
#define MAFP_HANDSHAKE_CODE2 'A'
/* Default enroll stages number */
#define DEFAULT_ENROLL_SAMPLES 12
#define MAFP_ENV_ENROLL_SAMPLES "MAFP_ENROLL_SAMPLES"
#define MAFP_ENROLL_IDENTIFY_DISABLED 0
#define MAFP_ENROLL_IDENTIFY_ENABLED 1
#define MAFP_ENROLL_IDENTIFY_ONCE 2
#define MAFP_ENROLL_DUPLICATE_DELETE_DISABLED 0
#define MAFP_ENROLL_DUPLICATE_DELETE_ENABLED 1
#define MAFP_ENROLL_DUPLICATE_AREA_DENY 0
#define MAFP_ENROLL_DUPLICATE_AREA_ALLOW 1
#define MAFP_SLEEP_INT_WAIT 0
#define MAFP_SLEEP_INT_CHECK 1
#define MAFP_SLEEP_INT_REFRESH 2
#define MAFP_PRESS_WAIT_UP 0
#define MAFP_PRESS_WAIT_DOWN 1
#define MAFP_IMAGE_ERR_TRRIGER 30
typedef enum {
MAPF_CMD_SEND = 0,
MAPF_CMD_RECEIVE,
MAPF_CMD_DATA_RECEIVE,
MAPF_CMD_TRANSFER_STATES,
} MapfCmdState;
typedef enum {
MAPF_INIT_CLEAN_EPIN = 0,
MAPF_INIT_CLEAN_EPOUT,
MAPF_INIT_CLEAN_EPIN2,
MAPF_INIT_HANDSHAKE,
MAPF_INIT_MODULE_STATUS,
MAPF_INIT_STATES,
} MapfInitState;
typedef enum {
MAFP_ENROLL_PWR_BTN_SHIELD_ON = 0,
MAFP_ENROLL_CHECK_EMPTY,
MAFP_ENROLL_TEMPLATE_TABLE,
MAFP_ENROLL_READ_TEMPLATE,
MAFP_ENROLL_VERIFY_GET_IMAGE,
MAFP_ENROLL_CHECK_INT_PARA,
MAFP_ENROLL_DETECT_MODE,
MAFP_ENROLL_ENABLE_INT,
MAFP_ENROLL_WAIT_INT,
MAFP_ENROLL_DISBALE_INT,
MAFP_ENROLL_REFRESH_INT_PARA,
MAFP_ENROLL_VERIFY_GENERATE_FEATURE,
MAFP_ENROLL_VERIFY_DUPLICATE_AREA,
MAFP_ENROLL_VERIFY_SEARCH,
MAFP_ENROLL_VERIFY_SEARCH_STEP, //match assigned id
MAFP_ENROLL_GET_TEMPLATE_INFO,
MAFP_ENROLL_SAVE_TEMPLATE_INFO,
MAFP_ENROLL_SAVE_TEMPLATE,
MAFP_ENROLL_DELETE_TEMPLATE_INFO_IF_FAILED,
MAFP_ENROLL_EXIT,
MAFP_ENROLL_STATES,
} MapfEnrollState;
typedef enum {
MAPF_IDENTIFY_PWR_BTN_SHIELD_ON = 0,
MAPF_IDENTIFY_TEMPLATE_TABLE,
MAPF_IDENTIFY_GET_STARTUP_RESULT,
MAPF_IDENTIFY_GET_IMAGE,
MAPF_IDENTIFY_CHECK_INT_PARA,
MAPF_IDENTIFY_DETECT_MODE,
MAPF_IDENTIFY_ENABLE_INT,
MAPF_IDENTIFY_WAIT_INT,
MAPF_IDENTIFY_DISBALE_INT,
MAPF_IDENTIFY_REFRESH_INT_PARA,
MAPF_IDENTIFY_GENERATE_FEATURE,
MAPF_IDENTIFY_SEARCH_STEP, //match assigned id
MAPF_IDENTIFY_GET_TEMPLATE_INFO,
MAPF_IDENTIFY_EXIT,
MAPF_IDENTIFY_STATES,
} MapfIdentifyState;
typedef enum {
MAPF_LIST_TEMPLATE_TABLE = 0,
MAPF_LIST_GET_TEMPLATE_INFO,
MAPF_LIST_STATES,
} MapfListState;
typedef enum {
MAPF_DELETE_TEMPLATE_TABLE = 0,
MAPF_DELETE_GET_TEMPLATE_INFO,
MAPF_DELETE_CLEAR_TEMPLATE_INFO,
MAPF_DELETE_TEMPLATE,
MAPF_DELETE_STATES,
} MapfDeleteState;
typedef enum {
MAPF_EMPTY_TEMPLATE = 0,
MAPF_EMPTY_STATES,
} MapfDeleteAllState;
typedef enum {
MAPF_PACK_CMD = 0x01, //cmd packet
MAPF_PACK_DATA = 0x02, //data packet, with more data packets later, must follow cmd or answer packet
MAPF_PACK_ANSWER = 0x07, //answer packet for cmd packet
MAPF_PACK_END = 0x08, //last data packet
MAPF_PACK_DATA_ANDSWER = 0x09, //answer packet for data packet
} MapfPackMark;
#pragma pack(push, 1)
typedef struct _mafp_handshake
{
uint8_t code[2];
/*uint8_t state;
uint8_t desc[16];
uint8_t ver[4];*/
} mafp_handshake_t, *pmafp_handshake_t;
typedef struct _mafp_search
{
uint8_t id[2];
uint8_t score[2];
} mafp_search_t, *pmafp_search_t;
typedef struct _mafp_tpl_table
{
uint8_t used_num;
uint8_t list[256];
} mafp_tpl_table_t, *pmafp_tpl_table_t;
typedef struct _mafp_tpl_info
{
char uid[128];
} mafp_tpl_info_t, *pmafp_tpl_info_t;
typedef struct _mafp_boot_handshake
{
char code[2];
uint8_t state;
char descrip[16];
uint32_t version;
} mafp_boot_handshake_t, *pmafp_boot_handshake_t;
typedef struct _mafp_template
{
char sn[32];
uint16_t id;
char uid[128];
} mafp_template_t, *pmafp_template_t;
#pragma pack(pop)
typedef struct _mafp_templates
{
uint16_t index;
uint16_t total_num;
uint16_t priv_num;
mafp_template_t total_list[256];
mafp_template_t priv_list[MAX_FINGER_NUM];
GPtrArray *list;
} mafp_templates_t, *pmafp_templates_t;
typedef struct _fp_cmd_response
{
uint8_t result;
union
{
mafp_handshake_t handshake;
mafp_search_t search;
mafp_tpl_table_t tpl_table;
mafp_tpl_info_t tpl_info;
mafp_boot_handshake_t boot_handshake;
};
} mafp_cmd_response_t, *pmafp_cmd_response_t;
typedef struct _pack_header
{
uint8_t head0;
uint8_t head1;
uint8_t addr0;
uint8_t addr1;
uint8_t addr2;
uint8_t addr3;
uint8_t flag;
uint8_t frame_len0;
uint8_t frame_len1;
} pack_header, *ppack_header;
+1 -2
View File
@@ -412,7 +412,6 @@ nb1010_dev_change_state (FpImageDevice *dev, FpiImageDeviceState state)
static const FpIdEntry id_table[] = {
{ .vid = 0x298d, .pid = 0x1010, },
{ .vid = 0x298d, .pid = 0x2020, },
{ .vid = 0, .pid = 0, .driver_data = 0 },
};
@@ -428,7 +427,7 @@ fpi_device_nb1010_class_init (FpiDeviceNb1010Class *klass)
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
dev_class->id = FP_COMPONENT;
dev_class->full_name = "NextBiometrics NB-1010-U/NB-2020-U";
dev_class->full_name = "NextBiometrics NB-1010-U";
dev_class->type = FP_DEVICE_TYPE_USB;
dev_class->id_table = id_table;
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
+5 -56
View File
@@ -30,7 +30,6 @@ static const FpIdEntry id_table[] = {
{ .vid = 0x0bda, .pid = 0x5813, },
{ .vid = 0x0bda, .pid = 0x5816, },
{ .vid = 0x2541, .pid = 0xfa03, },
{ .vid = 0x3274, .pid = 0x9003, },
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
};
@@ -61,7 +60,7 @@ parse_print_data (GVariant *data,
*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)
if (*user_id_len <= 0 || *user_id_len > DEFAULT_UID_LEN)
return FALSE;
if (*user_id[0] == '\0' || *user_id[0] == ' ')
@@ -234,16 +233,6 @@ fp_accept_sample_cb (FpiDeviceRealtek *self,
}
return;
}
if (in_status != FP_RTK_SUCCESS)
{
fpi_ssm_mark_failed (self->task_ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID,
"Enrollment failed (status 0x%02x)",
in_status));
return;
}
fpi_ssm_next_state (self->task_ssm);
}
}
@@ -648,14 +637,6 @@ fp_cmd_receive_cb (FpiUsbTransfer *transfer,
{
g_autofree guchar *read_buf = NULL;
if ((gsize) transfer->actual_length < self->trans_data_len)
{
fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Truncated data received"));
return;
}
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);
@@ -988,13 +969,7 @@ fp_verify_sm_run_state (FpiSsm *ssm, FpDevice *device)
switch (fpi_ssm_get_cur_state (ssm))
{
case FP_RTK_VERIFY_GET_TEMPLATE:
if (self->template_num <= 0)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"No templates enrolled"));
break;
}
g_assert (self->template_num > 0);
co_get_template.data_len[0] = GET_LEN_L (self->template_len * self->template_num);
co_get_template.data_len[1] = GET_LEN_H (self->template_len * self->template_num);
@@ -1056,13 +1031,7 @@ fp_enroll_sm_run_state (FpiSsm *ssm, FpDevice *device)
switch (fpi_ssm_get_cur_state (ssm))
{
case FP_RTK_ENROLL_GET_TEMPLATE:
if (self->template_num <= 0)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"No templates enrolled"));
break;
}
g_assert (self->template_num > 0);
co_get_template.data_len[0] = GET_LEN_L (self->template_len * self->template_num);
co_get_template.data_len[1] = GET_LEN_H (self->template_len * self->template_num);
@@ -1182,13 +1151,7 @@ fp_delete_sm_run_state (FpiSsm *ssm, FpDevice *device)
switch (fpi_ssm_get_cur_state (ssm))
{
case FP_RTK_DELETE_GET_POS:
if (self->template_num <= 0)
{
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"No templates enrolled"));
break;
}
g_assert (self->template_num > 0);
co_get_template.data_len[0] = GET_LEN_L (self->template_len * self->template_num);
co_get_template.data_len[1] = GET_LEN_H (self->template_len * self->template_num);
@@ -1380,21 +1343,7 @@ list_print (FpDevice *device)
guint8 *cmd_buf = NULL;
G_DEBUG_HERE ();
if (self->template_num < 0)
{
fpi_device_list_complete (device, NULL,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Get template number failed!"));
return;
}
if (self->template_num == 0)
{
g_autoptr(GPtrArray) list_result = g_ptr_array_new_with_free_func (g_object_unref);
fpi_device_list_complete (device, g_steal_pointer (&list_result), NULL);
return;
}
g_assert (self->template_num > 0);
co_get_template.data_len[0] = GET_LEN_L (self->template_len * self->template_num);
co_get_template.data_len[1] = GET_LEN_H (self->template_len * self->template_num);
File diff suppressed because it is too large Load Diff
+2 -25
View File
@@ -177,25 +177,17 @@ parse_get_enrolled_fingers_report (bmkt_msg_resp_t *msg_resp, bmkt_response_t *r
if (msg_resp->payload_len < 2)
return BMKT_UNRECOGNIZED_MESSAGE;
/* 2 bytes per finger so calculate the total number of fingers to process*/
int num_fingers = (msg_resp->payload_len) / 2;
bmkt_enrolled_fingers_resp_t *get_enrolled_fingers_resp = &resp->response.enrolled_fingers_resp;
if (num_fingers > (int) G_N_ELEMENTS (get_enrolled_fingers_resp->fingers))
{
g_warning ("Device reported more enrolled fingers (%d) than supported",
num_fingers);
num_fingers = G_N_ELEMENTS (get_enrolled_fingers_resp->fingers);
}
for (i = 0; i < num_fingers; i++)
{
get_enrolled_fingers_resp->fingers[i].finger_id = extract8 (msg_resp->payload, &offset);
get_enrolled_fingers_resp->fingers[i].template_status = extract8 (msg_resp->payload, &offset);
}
}
return BMKT_SUCCESS;
}
static int
@@ -222,18 +214,10 @@ parse_get_enrolled_users_report (bmkt_msg_resp_t *msg_resp, bmkt_response_t *res
get_enroll_templates_resp->templates[n].user_id_len = extract8 (msg_resp->payload, &offset) - 2;
if(get_enroll_templates_resp->templates[n].user_id_len > BMKT_MAX_USER_ID_LEN)
return BMKT_UNRECOGNIZED_MESSAGE;
if (offset >= msg_resp->payload_len)
return BMKT_CORRUPT_MESSAGE;
get_enroll_templates_resp->templates[n].template_status = extract8 (msg_resp->payload, &offset);
if (offset >= msg_resp->payload_len)
return BMKT_CORRUPT_MESSAGE;
get_enroll_templates_resp->templates[n].finger_id = extract8 (msg_resp->payload, &offset);
for (i = 0; i < get_enroll_templates_resp->templates[n].user_id_len; i++)
{
if (offset >= msg_resp->payload_len)
return BMKT_CORRUPT_MESSAGE;
get_enroll_templates_resp->templates[n].user_id[i] = extract8 (msg_resp->payload, &offset);
}
get_enroll_templates_resp->templates[n].user_id[i] = extract8 (msg_resp->payload, &offset);
get_enroll_templates_resp->templates[n].user_id[i] = '\0';
}
@@ -283,19 +267,12 @@ bmkt_compose_message (uint8_t *cmd, int *cmd_len, uint8_t msg_id, uint8_t seq_nu
int
bmkt_parse_message_header (uint8_t *resp_buf, int resp_len, bmkt_msg_resp_t *msg_resp)
{
if (resp_len < BMKT_MESSAGE_HEADER_LEN)
return BMKT_CORRUPT_MESSAGE;
if (resp_buf[BMKT_MESSAGE_HEADER_ID_FIELD] != BMKT_MESSAGE_HEADER_ID)
return BMKT_CORRUPT_MESSAGE;
msg_resp->seq_num = resp_buf[BMKT_MESSAGE_SEQ_NUM_FIELD];
msg_resp->msg_id = resp_buf[BMKT_MESSAGE_ID_FIELD];
msg_resp->payload_len = resp_buf[BMKT_MESSAGE_PAYLOAD_LEN_FIELD];
if (msg_resp->payload_len > resp_len - BMKT_MESSAGE_PAYLOAD_FIELD)
return BMKT_CORRUPT_MESSAGE;
if (msg_resp->payload_len > 0)
msg_resp->payload = &resp_buf[BMKT_MESSAGE_PAYLOAD_FIELD];
else
+2 -13
View File
@@ -48,8 +48,6 @@ static const FpIdEntry id_table[] = {
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0108, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0109, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x010A, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x010D, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x010E, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0123, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0124, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0126, },
@@ -63,7 +61,6 @@ static const FpIdEntry id_table[] = {
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x019D, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x019F, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x01A0, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x01A4, },
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
};
@@ -174,14 +171,6 @@ cmd_receive_cb (FpiUsbTransfer *transfer,
* The original code did not! */
if (msg_resp.msg_id == BMKT_RSP_GENERAL_ERROR)
{
if (msg_resp.payload_len < 2 || !msg_resp.payload)
{
fp_warn ("Received General Error with short or empty payload");
fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
return;
}
guint16 err;
/* XXX: It is weird that this is big endian. */
@@ -1221,7 +1210,7 @@ prob_msg_cb (FpiDeviceSynaptics *self,
return;
}
if (fpi_device_emulation_mode_enabled (FP_DEVICE (self)))
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
serial = g_strdup ("emulated-device");
else
serial = g_usb_device_get_string_descriptor (usb_dev,
@@ -1489,7 +1478,7 @@ suspend (FpDevice *dev)
self->cmd_suspended = TRUE;
/* Cancel the current transfer.
* The CMD SSM will go into the suspend state and signal readiness. */
* The CMD SSM will go into the suspend state and signal readyness. */
g_cancellable_cancel (self->interrupt_cancellable);
g_clear_object (&self->interrupt_cancellable);
self->interrupt_cancellable = g_cancellable_new ();
+7 -27
View File
@@ -155,8 +155,7 @@ capture_reqs_cb (FpiUsbTransfer *transfer, FpDevice *device,
}
static int
upektc_img_process_image_frame (unsigned char *image_buf, size_t image_buf_len,
unsigned char *cmd_res, size_t cmd_res_len)
upektc_img_process_image_frame (unsigned char *image_buf, unsigned char *cmd_res)
{
int offset = 8;
int len = ((cmd_res[5] & 0x0f) << 8) | (cmd_res[6]);
@@ -169,16 +168,6 @@ upektc_img_process_image_frame (unsigned char *image_buf, size_t image_buf_len,
}
if (cmd_res[7] == 0x20)
len -= 4;
if (len <= 0 ||
(size_t) offset + (size_t) len > cmd_res_len ||
(size_t) len > image_buf_len)
{
fp_dbg ("Invalid image frame length %d (offset %d, source %zu, dest %zu)",
len, offset, cmd_res_len, image_buf_len);
return 0;
}
memcpy (image_buf, cmd_res + offset, len);
return len;
@@ -332,8 +321,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
case 0x24:
self->image_size +=
upektc_img_process_image_frame (self->image_bits + self->image_size,
(self->expected_image_size * 2) - self->image_size,
data, MAX_RESPONSE_SIZE);
data);
fpi_ssm_jump_to_state (transfer->ssm,
CAPTURE_ACK_FRAME);
break;
@@ -342,16 +330,8 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
case 0x20:
self->image_size +=
upektc_img_process_image_frame (self->image_bits + self->image_size,
(self->expected_image_size * 2) - self->image_size,
data, MAX_RESPONSE_SIZE);
if (self->image_size != self->expected_image_size)
{
fp_err ("Image size mismatch: got %zu, expected %zu",
self->image_size, self->expected_image_size);
fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
return;
}
data);
BUG_ON (self->image_size != self->expected_image_size);
fp_dbg ("Image size is %lu",
(gulong) self->image_size);
img = fp_image_new (img_class->img_width, img_class->img_height);
@@ -432,10 +412,12 @@ capture_run_state (FpiSsm *ssm, FpDevice *_dev)
static void
capture_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error_arg)
{
g_autoptr(GError) error = error_arg;
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
FpiDeviceUpektcImg *self = FPI_DEVICE_UPEKTC_IMG (_dev);
g_autoptr(GError) error = error_arg;
/* Note: We assume that the error is a cancellation in the deactivation case */
if (self->deactivating)
start_deactivation (dev);
@@ -516,7 +498,6 @@ deactivate_sm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
fp_dbg ("Deactivate completed");
g_clear_pointer (&self->image_bits, g_free);
self->deactivating = FALSE;
fpi_image_device_deactivate_complete (dev, error);
}
@@ -631,7 +612,6 @@ init_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
}
self->expected_image_size = img_class->img_width * img_class->img_height;
g_clear_pointer (&self->image_bits, g_free);
self->image_bits = g_malloc0 (self->expected_image_size * 2);
}
-8
View File
@@ -308,14 +308,6 @@ __handle_incoming_msg (FpDevice *device,
fp_dbg ("non-zero bytes in cmd response");
innerlen = innerbuf[1] | (innerbuf[2] << 8);
if (innerlen < 3 || innerlen > len - 3)
{
fp_warn ("cmd response has invalid inner length (%d)", innerlen);
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"CMD response has invalid inner length");
goto err;
}
innerlen = innerlen - 3;
_subcmd = innerbuf[5];
fp_dbg ("device responds to subcmd %x with %d bytes", _subcmd, innerlen);
+4 -36
View File
@@ -587,10 +587,7 @@ image_transfer_cb (FpiUsbTransfer *transfer, FpDevice *dev,
}
else
{
struct uru4k_image *img = g_memdup2 (transfer->buffer, sizeof (struct uru4k_image));
img->num_lines = GUINT16_FROM_LE (img->num_lines);
self->img_data = g_steal_pointer (&img);
self->img_data = g_memdup2 (transfer->buffer, sizeof (struct uru4k_image));
self->img_data_actual_length = transfer->actual_length;
fpi_ssm_next_state (ssm);
}
@@ -681,10 +678,10 @@ calc_dev2 (struct uru4k_image *img)
static void
imaging_run_state (FpiSsm *ssm, FpDevice *_dev)
{
g_autoptr(FpImage) fpimg = NULL;
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
FpiDeviceUru4000 *self = FPI_DEVICE_URU4000 (_dev);
struct uru4k_image *img = self->img_data;
FpImage *fpimg;
uint32_t key;
uint8_t flags, num_lines;
int i, r, to, dev2;
@@ -755,17 +752,6 @@ imaging_run_state (FpiSsm *ssm, FpDevice *_dev)
if (num_lines == 0)
break;
/* num_lines is device-supplied; make sure decoding this block stays
* within the captured image buffer (IMAGE_HEIGHT rows). */
if ((size_t) self->img_lines_done + num_lines > IMAGE_HEIGHT)
{
fp_err ("bad captured image: block %d (%d lines) overflows buffer",
self->img_block, num_lines);
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
return;
}
fp_dbg ("%d %02x %d", self->img_block, flags,
num_lines);
if (flags & BLOCKF_CHANGE_KEY)
@@ -809,17 +795,6 @@ imaging_run_state (FpiSsm *ssm, FpDevice *_dev)
num_lines = img->block_info[i].num_lines;
if (num_lines == 0)
break;
if ((size_t) r + num_lines > IMAGE_HEIGHT ||
(size_t) to + (size_t) num_lines * IMAGE_WIDTH > fpimg->width * fpimg->height)
{
fp_err ("bad captured image: block %d (%d lines) overflows buffer",
i, num_lines);
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
return;
}
memcpy (&fpimg->data[to], &img->data[r][0],
num_lines * IMAGE_WIDTH);
if (!(flags & BLOCKF_NOT_PRESENT))
@@ -836,8 +811,7 @@ imaging_run_state (FpiSsm *ssm, FpDevice *_dev)
*/
if (self->profile->image_not_flipped)
fpimg->flags |= FPI_IMAGE_V_FLIPPED | FPI_IMAGE_H_FLIPPED;
fpi_image_device_image_captured (dev, g_steal_pointer (&fpimg));
fpi_image_device_image_captured (dev, fpimg);
if (self->activate_state == FPI_IMAGE_DEVICE_STATE_CAPTURE)
fpi_ssm_jump_to_state (ssm, IMAGING_CAPTURE);
@@ -1207,12 +1181,6 @@ init_run_state (FpiSsm *ssm, FpDevice *_dev)
static void
activate_initsm_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
{
FpiDeviceUru4000 *self = FPI_DEVICE_URU4000 (dev);
g_clear_pointer (&self->scanpwr_irq_timeout, g_source_destroy);
self->irq_cb_data = NULL;
self->irq_cb = NULL;
fpi_image_device_activate_complete (FP_IMAGE_DEVICE (dev), error);
}
@@ -1412,7 +1380,7 @@ dev_init (FpImageDevice *dev)
g_clear_pointer (&self->rand, g_rand_free);
self->rand = g_rand_new ();
if (fpi_device_emulation_mode_enabled (FP_DEVICE (dev)))
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
g_rand_set_seed (self->rand, 0xFACADE);
driver_data = fpi_device_get_driver_data (FP_DEVICE (dev));
+1 -1
View File
@@ -119,7 +119,7 @@ async_abort_callback (FpiUsbTransfer *transfer, FpDevice *device,
/* In normal case endpoint is empty */
if (g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT) ||
(fpi_device_emulation_mode_enabled (device) && transfer->actual_length == 0))
(g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0 && transfer->actual_length == 0))
{
g_clear_error (&error);
fpi_ssm_next_state (transfer->ssm);
+1 -4
View File
@@ -184,8 +184,8 @@ async_send_cb (FpiUsbTransfer *transfer, FpDevice *device,
}
else
{
fp_dbg ("Ignoring send error: %s", error->message);
g_error_free (error);
fp_dbg ("Ignoring send error: %s", error->message);
}
}
/* Reset ignore_error flag */
@@ -708,9 +708,6 @@ vfs_check_contrast (FpDeviceVfs101 *vdev)
int y;
long int count = 0;
if (vdev->height <= 0)
return;
/* Check difference from byte 4 to byte 5 for verify contrast of image */
for (y = 0; y < vdev->height; y++)
count = count + vdev->buffer[offset (5, y)] - vdev->buffer[offset (4, y)];
+6 -16
View File
@@ -85,7 +85,6 @@ enum {
static void
m_loop_state (FpiSsm *ssm, FpDevice *_dev)
{
g_autoptr(GError) error = NULL;
FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
FpDeviceVfs301 *self = FPI_DEVICE_VFS301 (_dev);
@@ -102,16 +101,10 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev)
break;
case M_CHECK_PRINT:
{
int rv = vfs301_proto_peek_event (self, &error);
if (rv == VFS301_FAILURE)
fpi_ssm_mark_failed (ssm, error);
else if (rv == VFS301_ONGOING)
fpi_ssm_jump_to_state (ssm, M_WAIT_PRINT);
else
fpi_ssm_next_state (ssm);
}
if (!vfs301_proto_peek_event (self))
fpi_ssm_jump_to_state (ssm, M_WAIT_PRINT);
else
fpi_ssm_next_state (ssm);
break;
case M_READ_PRINT_START:
@@ -128,11 +121,8 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev)
case M_READ_PRINT_POLL:
{
int rv = vfs301_proto_process_event_poll (self);
if (rv == VFS301_FAILURE)
fpi_ssm_mark_failed (ssm,
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
else if (rv == VFS301_ONGOING)
g_assert (rv != VFS301_FAILURE);
if (rv == VFS301_ONGOING)
fpi_ssm_jump_to_state (ssm, M_READ_PRINT_WAIT);
else
fpi_ssm_next_state (ssm);
+1 -2
View File
@@ -133,8 +133,7 @@ void vfs301_proto_deinit (FpDeviceVfs301 *dev);
void vfs301_proto_request_fingerprint (FpDeviceVfs301 *dev);
/** returns 0 if no event is ready, or 1 if there is one... */
int vfs301_proto_peek_event (FpDeviceVfs301 *dev,
GError **error);
int vfs301_proto_peek_event (FpDeviceVfs301 *dev);
void vfs301_proto_process_event_start (FpDeviceVfs301 *dev);
int vfs301_proto_process_event_poll (FpDeviceVfs301 *dev);
+11 -19
View File
@@ -63,7 +63,7 @@ usb_print_packet (int dir, GError *error, const guint8 *data, int length)
}
#endif
static gboolean
static void
usb_recv (FpDeviceVfs301 *dev, guint8 endpoint, int max_bytes, FpiUsbTransfer **out, GError **error)
{
GError *err = NULL;
@@ -87,14 +87,10 @@ usb_recv (FpDeviceVfs301 *dev, guint8 endpoint, int max_bytes, FpiUsbTransfer **
if (!error)
g_warning ("Unhandled receive error: %s", err->message);
g_propagate_error (error, err);
return FALSE;
}
if (out)
*out = g_steal_pointer (&transfer);
return TRUE;
}
FP_GNUC_ACCESS (read_only, 2, 3)
@@ -348,11 +344,7 @@ vfs301_extract_image (FpDeviceVfs301 *vfs, guint8 *output, int *output_height
int last_line;
int i;
if (vfs->scanline_count < 1 || scanlines == NULL)
{
*output_height = 0;
g_return_if_reached ();
}
g_assert (vfs->scanline_count >= 1);
*output_height = 1;
memcpy (output, scanlines, VFS301_FP_OUTPUT_WIDTH);
@@ -451,8 +443,7 @@ vfs301_proto_process_data (FpDeviceVfs301 *dev, int first_block, const guint8 *b
if (first_block)
{
if (len < VFS301_FP_FRAME_SIZE)
g_return_val_if_reached (img_process_data (first_block, dev, buf, 0));
g_assert (len >= VFS301_FP_FRAME_SIZE);
/* Skip bytes until start_sequence is found */
for (i = 0; i < VFS301_FP_FRAME_SIZE; i++, buf++, len--)
@@ -471,25 +462,26 @@ vfs301_proto_request_fingerprint (FpDeviceVfs301 *dev)
}
int
vfs301_proto_peek_event (FpDeviceVfs301 *dev,
GError **error)
vfs301_proto_peek_event (FpDeviceVfs301 *dev)
{
g_autoptr(GError) error = NULL;
g_autoptr(FpiUsbTransfer) transfer = NULL;
const char no_event[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char got_event[] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
USB_SEND (0x17, -1);
if (!usb_recv (dev, VFS301_RECEIVE_ENDPOINT_CTRL, 7, &transfer, error))
return -1;
usb_recv (dev, VFS301_RECEIVE_ENDPOINT_CTRL, 7, &transfer, &error);
/* XXX: This is obviously not a sane error handling! */
g_assert (!error);
if (memcmp (transfer->buffer, no_event, sizeof (no_event)) == 0)
return 0;
else if (memcmp (transfer->buffer, got_event, sizeof (no_event)) == 0)
return 1;
g_set_error (error, FP_DEVICE_ERROR_PROTO, 0, "Unexpected event response");
return -1;
else
g_assert_not_reached ();
}
/* XXX: We sometimes need to receive data on from two endpoints at the same
@@ -77,7 +77,6 @@ dev_identify (FpDevice *dev)
&idx))
{
match = g_ptr_array_index (prints, idx);
fp_print_set_finger (new_scan, fp_print_get_finger (match));
}
if (!self->match_reported)
+5 -4
View File
@@ -165,7 +165,11 @@ process_cmds (FpDeviceVirtualDevice * self,
{
g_autofree gchar *cmd = NULL;
cmd = g_ptr_array_steal_index (self->pending_commands, 0);
/* TODO: g_ptr_array_steal_index requires GLib 2.58, we depend on 2.56 */
cmd = g_ptr_array_index (self->pending_commands, 0);
g_ptr_array_index (self->pending_commands, 0) = NULL;
g_ptr_array_remove_index (self->pending_commands, 0);
g_debug ("Processing command %s", cmd);
/* These are always processed. */
@@ -545,9 +549,6 @@ dev_verify (FpDevice *dev)
else
{
success = fp_print_equal (print, new_scan);
if (success)
fp_print_set_finger (new_scan, fp_print_get_finger (print));
}
if (!self->match_reported)
+342
View File
@@ -0,0 +1,342 @@
/*
* Virtual driver for SDCP device debugging
*
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This is a virtual driver to debug the SDCP-based drivers.
* This virtual driver does not use a socket listener but instead
* embeds the simulated logic of the fake "device" directly within
* the logic of each function. This driver also allows prints to be
* registered programmatically, making it possible to test libfprint
* and fprintd.
*
* This virtual driver will override FpSdcpDevice's dynamically
* generated cryptography values and instead replace them with
* pre-generated values taken from from Microsoft's sample client
* implementation. See:
* https://github.com/Microsoft/SecureDeviceConnectionProtocol
*/
#define FP_COMPONENT "virtual_sdcp"
#include "fpi-log.h"
#include "../fpi-sdcp.h"
#include "virtual-sdcp.h"
struct _FpDeviceVirtualSdcp
{
FpSdcpDevice parent;
GPtrArray *print_ids;
};
G_DECLARE_FINAL_TYPE (FpDeviceVirtualSdcp, fpi_device_virtual_sdcp, FPI, DEVICE_VIRTUAL_SDCP, FpSdcpDevice)
G_DEFINE_TYPE (FpDeviceVirtualSdcp, fpi_device_virtual_sdcp, FP_TYPE_SDCP_DEVICE)
/******************************************************************************/
static const guint8 from_hex_map[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // @abcdef
};
static GBytes *
g_bytes_from_hex (const gchar *hex)
{
g_autoptr(GBytes) res = NULL;
guint8 b0, b1;
gsize bytes_len = strlen (hex) / 2;
guint8 *bytes = g_malloc0 (bytes_len);
for (int i = 0; i < strlen (hex) - 1; i += 2)
{
b0 = ((guint8) hex[i + 0] & 0x1F) ^ 0x10;
b1 = ((guint8) hex[i + 1] & 0x1F) ^ 0x10;
bytes[i / 2] = (guint8) (from_hex_map[b0] << 4) | from_hex_map[b1];
}
res = g_bytes_new_take (bytes, bytes_len);
return g_steal_pointer (&res);
}
static FpiSdcpClaim *
get_fake_sdcp_claim (void)
{
FpiSdcpClaim *claim = g_new0 (FpiSdcpClaim, 1);
claim->model_certificate = g_bytes_from_hex (model_certificate_hex);
claim->device_public_key = g_bytes_from_hex (device_public_key_hex);
claim->firmware_public_key = g_bytes_from_hex (firmware_public_key_hex);
claim->firmware_hash = g_bytes_from_hex (firmware_hash_hex);
claim->model_signature = g_bytes_from_hex (model_signature_hex);
claim->device_signature = g_bytes_from_hex (device_signature_hex);
return g_steal_pointer (&claim);
}
/******************************************************************************/
static void
dev_identify (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (sdcp_device);
g_autoptr(GBytes) enrollment_id = NULL;
g_autoptr(GBytes) identify_mac = NULL;
GBytes *identify_nonce = NULL;
if (self->print_ids->len > 0)
{
/*
* Pretend that the virtual device identified the first print.
* Since we used a pre-generated enrollment_id for it, we can also use the
* matching pre-generated test identify data for its identification.
*/
identify_nonce = g_bytes_from_hex (identify_nonce_hex);
enrollment_id = g_bytes_from_hex (enrollment_id_hex);
fpi_sdcp_device_set_identify_data (sdcp_device, identify_nonce);
identify_mac = g_bytes_from_hex (identify_mac_hex);
fpi_sdcp_device_identify_complete (sdcp_device, enrollment_id, identify_mac, NULL);
}
else
{
fpi_sdcp_device_identify_complete (sdcp_device, NULL, NULL,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
}
}
static void
dev_enroll_commit (FpSdcpDevice *sdcp_device, GBytes *id)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (sdcp_device);
g_autoptr(GBytes) expected_first_print_id = NULL;
GBytes *print_id;
print_id = g_bytes_new (g_bytes_get_data (id, NULL), g_bytes_get_size (id));
/*
* If this is the first print, it is probably good if we make sure the
* internal API assigned it the expected pre-known ID
*/
if (self->print_ids->len == 0)
{
expected_first_print_id = g_bytes_from_hex (enrollment_id_hex);
if (!g_bytes_equal (print_id, expected_first_print_id))
{
fpi_sdcp_device_enroll_commit_complete (sdcp_device,
fpi_device_error_new_msg (FP_DEVICE_ERROR_UNTRUSTED,
"First enrolled print ID does not match expected value"));
return;
}
}
g_ptr_array_add (self->print_ids, g_steal_pointer (&print_id));
fpi_sdcp_device_enroll_commit_complete (sdcp_device, NULL);
}
static void
dev_enroll (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (sdcp_device);
FpDevice *device = FP_DEVICE (sdcp_device);
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) nonce = NULL;
FpPrint *print;
fpi_device_get_enroll_data (device, &print);
if (self->print_ids->len == 0)
{
/* Use the hard-coded nonce for the first enrollment */
nonce = g_bytes_from_hex (enrollment_nonce_hex);
}
else
{
/* Generate a new nonce for all other enrollments */
nonce = fpi_sdcp_generate_random (&error);
if (error)
fpi_device_enroll_progress (device, 0, print, error);
}
fpi_device_enroll_progress (device, 1, print, NULL);
fpi_sdcp_device_enroll_commit (sdcp_device, nonce, error);
}
static void
dev_list (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (sdcp_device);
GPtrArray *print_ids = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
for (gint i = 0; i < self->print_ids->len; i++)
{
GBytes *print_id = g_ptr_array_index (self->print_ids, i);
fp_dbg ("print %d:", i);
fp_dbg_hex_dump_gbytes (print_id);
g_ptr_array_add (print_ids, g_bytes_new (g_bytes_get_data (print_id, NULL),
g_bytes_get_size (print_id)));
}
fpi_sdcp_device_list_complete (sdcp_device, g_steal_pointer (&print_ids), NULL);
}
static void
dev_reconnect (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) random = NULL;
g_autoptr(GBytes) reconnect_mac = g_bytes_from_hex (reconnect_mac_hex);
/*
* Normally, a driver would fetch the reconnect data and then send it to the
* device's Reconnect command. In this fake device, we will just fetch and
* verify the random was generated but do nothing with it
*/
fpi_sdcp_device_get_reconnect_data (sdcp_device, &random);
g_assert (random);
g_assert (g_bytes_get_size (random) == SDCP_RANDOM_SIZE);
/*
* In emulation mode (FP_DEVICE_EMULATION=1), a different hard-coded random is
* set in fpi-sdcp-device, which was the same random used to generate the
* reconnect_mac value provided here
*/
fpi_sdcp_device_reconnect_complete (sdcp_device, reconnect_mac, error);
}
static void
dev_connect (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) host_random = NULL;
g_autoptr(GBytes) host_public_key = NULL;
g_autoptr(GBytes) device_random = g_bytes_from_hex (device_random_hex);
g_autoptr(GBytes) connect_mac = g_bytes_from_hex (connect_mac_hex);
g_autoptr(FpiSdcpClaim) claim = get_fake_sdcp_claim ();
fpi_sdcp_device_get_connect_data (sdcp_device, &host_random, &host_public_key);
g_assert (host_random);
g_assert (g_bytes_get_size (host_random) == SDCP_RANDOM_SIZE);
g_assert (host_public_key);
g_assert (g_bytes_get_size (host_public_key) == SDCP_PUBLIC_KEY_SIZE);
fpi_sdcp_device_connect_complete (sdcp_device, device_random, claim, connect_mac, error);
}
static void
dev_close (FpDevice *device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
fpi_device_close_complete (device, NULL);
}
static void
dev_open (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpSdcpDeviceClass *klass = FP_SDCP_DEVICE_GET_CLASS (sdcp_device);
if (g_strcmp0 (g_getenv ("FP_VIRTUAL_SDCP_NO_RECONNECT"), "1") == 0)
{
fp_dbg ("Virtual SDCP device: FP_VIRTUAL_SDCP_NO_RECONNECT=1; disabling SDCP reconnect");
klass->reconnect = NULL;
}
else
{
fp_dbg ("Virtual SDCP device: FP_VIRTUAL_SDCP_NO_RECONNECT!=1; enabling SDCP reconnect");
klass->reconnect = dev_reconnect;
}
fpi_sdcp_device_open_complete (sdcp_device, NULL);
}
static void
fpi_device_virtual_sdcp_finalize (GObject *object)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (object);
g_clear_pointer (&self->print_ids, g_ptr_array_unref);
G_OBJECT_CLASS (fpi_device_virtual_sdcp_parent_class)->finalize (object);
}
static void
fpi_device_virtual_sdcp_init (FpDeviceVirtualSdcp *self)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
/* Force FP_DEVICE_EMULATION=1 when using FpDeviceVirtualSdcp */
g_setenv ("FP_DEVICE_EMULATION", "1", TRUE);
self->print_ids = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
}
static const FpIdEntry driver_ids[] = {
{ .virtual_envvar = "FP_VIRTUAL_SDCP" },
{ .virtual_envvar = NULL }
};
static void
fpi_device_virtual_sdcp_class_init (FpDeviceVirtualSdcpClass *klass)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
FpSdcpDeviceClass *sdcp_dev_class = FP_SDCP_DEVICE_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fpi_device_virtual_sdcp_finalize;
dev_class->id = FP_COMPONENT;
dev_class->full_name = "Virtual SDCP device for debugging";
dev_class->type = FP_DEVICE_TYPE_VIRTUAL;
dev_class->id_table = driver_ids;
dev_class->nr_enroll_stages = 1;
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
sdcp_dev_class->ignore_device_certificate = FALSE;
sdcp_dev_class->ignore_device_signatures = FALSE;
sdcp_dev_class->open = dev_open;
sdcp_dev_class->connect = dev_connect;
sdcp_dev_class->reconnect = dev_reconnect;
sdcp_dev_class->list = dev_list;
sdcp_dev_class->enroll = dev_enroll;
sdcp_dev_class->enroll_commit = dev_enroll_commit;
sdcp_dev_class->identify = dev_identify;
dev_class->close = dev_close;
fpi_device_class_auto_initialize_features (dev_class);
dev_class->features |= FP_DEVICE_FEATURE_STORAGE;
}
+92
View File
@@ -0,0 +1,92 @@
/*
* Virtual driver test payloads for SDCP device debugging
*
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "fpi-compat.h"
/* host keys */
static const gchar host_private_key_hex[] = "8400ed14579cdf11586477e836e8cb52708441c1c2a447c218c5bbc2d118fbc7";
static const gchar host_public_key_hex[] = "0452f056ffb9c6728654771a3629b770767b19a2106a4916fb81ba06ef6797c4"
"a3df672ade0e9116d1abe278a8223abde4958d62d4ff6882159f0617c6f8ce10"
"bf";
static const gchar host_random_hex[] = "d877403abe82f4d97e1448c5052d83a532a45e56ef049cbbf981137520e713bf";
/* device keys */
static const gchar device_random_hex[] = "6e2f6c1abef2a1973fb1315a17e209fdb0c78520f1fd6a85d294d7aeb40a04a7";
static const gchar model_certificate_hex[] = "30820323308202caa00302010202133300000004c45d661d6eed040d00000000"
"0004300a06082a8648ce3d0403023056310b3009060355040613025553311e30"
"1c060355040a13154d6963726f736f667420436f72706f726174696f6e312730"
"250603550403131e57696e646f77732048656c6c6f2032303936414443432043"
"412032303231301e170d3232303130343231303033355a170d32333034303432"
"31303033355a301c311a3018060355040313115365637572652042494f205365"
"6e736f723059301306072a8648ce3d020106082a8648ce3d0301070342000414"
"cfc287f872a2b7d3339e0b31390e3ca688e61165eaa6687c959270e07666b1fa"
"19e3efaf1750d134a886d494424fe471970c4b06838408a18d1f5d57735dd7a3"
"8201af308201ab30750603551d11046e306ca46a30683132303006082b060104"
"82376402132436423045413344382d383339372d343444332d383043392d3930"
"324130414346344343303132303006082b060104823764011324413944423730"
"32362d433646462d344341462d423134382d393133454641333730423933301d"
"0603551d0e04160414db6d66d642b7236d5e6bb2ec2186decda98067b6301f06"
"03551d23041830168014bf3748e34a632de953a3ba890298c069472a99b9305f"
"0603551d1f045830563054a052a050864e687474703a2f2f7777772e6d696372"
"6f736f66742e636f6d2f706b696f70732f63726c2f57696e646f777325323048"
"656c6c6f25323032303936414443432532304341253230323032312e63726c30"
"6c06082b060105050701010460305e305c06082b060105050730028650687474"
"703a2f2f7777772e6d6963726f736f66742e636f6d2f706b696f70732f636572"
"74732f57696e646f777325323048656c6c6f2532303230393641444343253230"
"4341253230323032312e637274300c0603551d130101ff040230003015060355"
"1d25040e300c060a2b0601040182374c2b01300a06082a8648ce3d0403020347"
"003044022077553ef520f732e03cd740c8cf807e6366e12918bc581f75bfe0f1"
"95b7b1fd4f0220324e25b93b9da7538b797a624272b21b7cc0e96ea487924250"
"4677600450f283";
static const gchar device_public_key_hex[] = "04e2787890a684f95b96b9a2316ca8d3d33d4d79ff4c89dc6f9e888e973990d1"
"d3154133dcc8bd33b99af9dbf0673390d404d092498a3f214cd93f9b9f28fb5f"
"66";
static const gchar firmware_public_key_hex[] = "04f06a84ab51a3a6e8ff46868f91dd720e4cdad21f2e090d11e8f9bfc2ea19ee"
"1b5eac850b4532968a9399f76cd779e7723e8c2ca73b597c0df5f73b94a36f2b"
"6c";
static const gchar firmware_hash_hex[] = "c3bf47ea1f4a4a605470313cacb3a44f4a461f68c6faeab07e737610cb5ac835";
static const gchar model_signature_hex[] = "febe6ba3107813e185f05189e69ae79d9f7a40802582d94324459844c8b97ec6"
"c5daed5462276cb8a193c33e350424b0305d63d79a93a9188dcfc0cb5595f6c1";
static const gchar device_signature_hex[] = "10cc57dd8dafb463510a7327a5fca49b698e999b36448e2023eaf0dff0b0d4a3"
"4f1caf4e872b77364a0a00d7476554d0324c4cc931937e232a0315837d696c06";
static const gchar connect_mac_hex[] = "422bc475a78f972bae842a28e5ad721207457fcbd9a1a3aaf71587c07b84d247";
/* expected application_secret based on above */
static const gchar application_secret_hex[] = "13330ba3135ecf5dc71cede01a886540771efab35c8ba053902b2c1ee7de6efe";
/* test verify_reconnect values */
static const gchar reconnect_random_hex[] = "8a7451c1d3a8dca1c1330ca50d73454b351a49f46c8e9dcee15c964d295c31c9";
static const gchar reconnect_mac_hex[] = "bf3f3bb3bd6ecb2784c160f526f7bc3b3ca8faf5557194c48e0024a0493903c7";
/* test enrollment_id values */
static const gchar enrollment_nonce_hex[] = "c2101c44c9a667bba397e81f48b143398603e2c9335a68b409e1dbe71e005ca2";
static const gchar enrollment_id_hex[] = "67109dc70a216331f1580ddac601915929c1ff6c9bcba6544ba572c660c3d91e";
/* test verify_identify values */
static const gchar identify_nonce_hex[] = "3a1b506f5bec089059acefb9b44dfbdea7a599ee9aa267e5252664d60b798053";
static const gchar identify_mac_hex[] = "53a723eef40713094a90c5ef9996cbd6ba268e30676cd7107705a6c3e3e1eff9";
-1
View File
@@ -22,7 +22,6 @@
#pragma once
#include "fpi-compat.h"
#include "fpi-enums.h"
#include "fpi-assembling.h"
#include "fpi-device.h"
#include "fpi-image-device.h"
+6 -51
View File
@@ -128,9 +128,6 @@ fp_device_task_cancelled_cb (GCancellable *cancellable, FpDevice *self)
{
FpDevicePrivate *priv = fp_device_get_instance_private (self);
g_debug ("Task cancellable for operation %d cancelled",
priv->current_action);
g_cancellable_cancel (priv->current_cancellable);
}
@@ -224,7 +221,7 @@ fp_device_finalize (GObject *object)
g_clear_pointer (&priv->temp_timeout, g_source_destroy);
g_clear_slist (&priv->sources, (GDestroyNotify) g_source_destroy);
g_slist_free_full (priv->sources, (GDestroyNotify) g_source_destroy);
g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
g_clear_pointer (&priv->current_task_idle_return_source, g_source_destroy);
@@ -1249,33 +1246,6 @@ match_data_free (FpMatchData *data)
g_free (data);
}
static void
on_verify_identification (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(FpPrint) match = NULL;
g_autoptr(FpPrint) print = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) verify_task = G_TASK (g_steal_pointer (&user_data));
FpDevice *device = FP_DEVICE (source_object);
FpMatchData *match_data;
if (!fp_device_identify_finish (device, result, &match, &print, &error))
{
g_task_return_error (verify_task, g_steal_pointer (&error));
return;
}
match_data = g_new0 (FpMatchData, 1);
match_data->print = g_steal_pointer (&print);
g_task_set_task_data (verify_task, g_steal_pointer (&match_data),
(GDestroyNotify) match_data_free);
g_task_return_int (verify_task, match ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL);
}
/**
* fp_device_verify:
* @device: a #FpDevice
@@ -1324,8 +1294,7 @@ fp_device_verify (FpDevice *device,
return;
}
if ((!cls->verify && !cls->identify) ||
!(priv->features & FP_DEVICE_FEATURE_VERIFY))
if (!cls->verify || !(priv->features & FP_DEVICE_FEATURE_VERIFY))
{
g_task_return_error (task,
fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED,
@@ -1333,21 +1302,6 @@ fp_device_verify (FpDevice *device,
return;
}
if (!cls->verify)
{
g_autoptr(GPtrArray) prints = NULL;
g_assert (cls->identify);
prints = g_ptr_array_sized_new (1);
g_ptr_array_add (prints, enrolled_print);
fp_device_identify (device, prints, cancellable, match_cb, match_data,
match_destroy, on_verify_identification,
g_steal_pointer (&task));
return;
}
fpi_device_update_temp (device, TRUE);
if (priv->temp_current == FP_TEMPERATURE_HOT)
{
@@ -1447,6 +1401,7 @@ fp_device_identify (FpDevice *device,
FpDevicePrivate *priv = fp_device_get_instance_private (device);
FpDeviceClass *cls = FP_DEVICE_GET_CLASS (device);
FpMatchData *data;
int i;
task = g_task_new (device, cancellable, callback, user_data);
if (g_task_return_error_if_cancelled (task))
@@ -1499,9 +1454,9 @@ fp_device_identify (FpDevice *device,
* a reference to each print. Also, the caller could in principle modify the
* GPtrArray afterwards.
*/
data->gallery = g_ptr_array_copy (prints, (GCopyFunc) g_object_ref, NULL);
g_ptr_array_set_free_func (data->gallery, g_object_unref);
data->gallery = g_ptr_array_new_full (prints->len, g_object_unref);
for (i = 0; i < prints->len; i++)
g_ptr_array_add (data->gallery, g_object_ref (g_ptr_array_index (prints, i)));
data->match_cb = match_cb;
data->match_data = match_data;
data->match_destroy = match_destroy;
+2
View File
@@ -143,6 +143,7 @@ typedef enum {
* @FP_DEVICE_ERROR_DATA_DUPLICATE: Enrolling template duplicates storaged templates
* @FP_DEVICE_ERROR_REMOVED: The device has been removed.
* @FP_DEVICE_ERROR_TOO_HOT: The device might be getting too hot
* @FP_DEVICE_ERROR_UNTRUSTED: Device cannot be trusted
*
* Error codes for device operations. More specific errors from other domains
* such as #G_IO_ERROR or #G_USB_DEVICE_ERROR may also be reported.
@@ -161,6 +162,7 @@ typedef enum {
/* Leave some room to add more DATA related errors */
FP_DEVICE_ERROR_REMOVED = 0x100,
FP_DEVICE_ERROR_TOO_HOT,
FP_DEVICE_ERROR_UNTRUSTED,
} FpDeviceError;
GQuark fp_device_retry_quark (void);
+2
View File
@@ -89,6 +89,7 @@ fp_image_device_cancel_action (FpDevice *device)
/* We can only cancel capture operations, in that case, deactivate and return
* an error immediately. */
if (action == FPI_DEVICE_ACTION_ENROLL ||
action == FPI_DEVICE_ACTION_VERIFY ||
action == FPI_DEVICE_ACTION_IDENTIFY ||
action == FPI_DEVICE_ACTION_CAPTURE)
fpi_image_device_deactivate (self, TRUE);
@@ -216,6 +217,7 @@ fp_image_device_class_init (FpImageDeviceClass *klass)
fp_device_class->open = fp_image_device_open;
fp_device_class->close = fp_image_device_close;
fp_device_class->enroll = fp_image_device_start_capture_action;
fp_device_class->verify = fp_image_device_start_capture_action;
fp_device_class->identify = fp_image_device_start_capture_action;
fp_device_class->capture = fp_image_device_start_capture_action;
+73 -80
View File
@@ -596,9 +596,6 @@ fp_print_equal (FpPrint *self, FpPrint *other)
g_return_val_if_fail (self->type != FPI_PRINT_UNDEFINED, FALSE);
g_return_val_if_fail (other->type != FPI_PRINT_UNDEFINED, FALSE);
if (self == other)
return TRUE;
if (self->type != other->type)
return FALSE;
@@ -608,16 +605,18 @@ fp_print_equal (FpPrint *self, FpPrint *other)
if (g_strcmp0 (self->device_id, other->device_id))
return FALSE;
switch (self->type)
if (self->type == FPI_PRINT_RAW || self->type == FPI_PRINT_SDCP)
{
case FPI_PRINT_RAW:
return g_variant_equal (self->data, other->data);
}
else if (self->type == FPI_PRINT_NBIS)
{
guint i;
case FPI_PRINT_NBIS:
if (self->prints->len != other->prints->len)
return FALSE;
for (guint i = 0; i < self->prints->len; i++)
for (i = 0; i < self->prints->len; i++)
{
struct xyt_struct *a = g_ptr_array_index (self->prints, i);
struct xyt_struct *b = g_ptr_array_index (other->prints, i);
@@ -627,12 +626,11 @@ fp_print_equal (FpPrint *self, FpPrint *other)
}
return TRUE;
case FPI_PRINT_UNDEFINED:
}
else
{
g_assert_not_reached ();
}
g_return_val_if_reached (FALSE);
}
#define FPI_PRINT_VARIANT_TYPE G_VARIANT_TYPE ("(issbymsmsia{sv}v)")
@@ -767,7 +765,7 @@ fp_print_deserialize (const guchar *data,
g_autoptr(GVariant) value = NULL;
g_autoptr(GVariant) print_data = NULL;
g_autoptr(GDate) date = NULL;
g_autoptr(GBytes) bytes = NULL;
guchar *aligned_data = NULL;
guint8 finger_int8;
FpFinger finger;
g_autofree gchar *username = NULL;
@@ -789,10 +787,14 @@ fp_print_deserialize (const guchar *data,
* of this function (meaning we don't need to keep the data around.
*/
/* We need to copy the backing store for the raw data that we may keep for
* longer. Using a GBytes also ensures the data is correctly aligned. */
bytes = g_bytes_new (data + 3, length - 3);
raw_value = g_variant_new_from_bytes (FPI_PRINT_VARIANT_TYPE, bytes, FALSE);
/* To support GLIB < 2.60 we need to make sure that the memory is aligned correctly.
* We also need to copy the backing store for the raw data that we may keep for
* longer. */
aligned_data = g_malloc (length - 3);
memcpy (aligned_data, data + 3, length - 3);
raw_value = g_variant_new_from_data (FPI_PRINT_VARIANT_TYPE,
aligned_data, length - 3,
FALSE, g_free, aligned_data);
if (!raw_value)
goto invalid_format;
@@ -819,81 +821,72 @@ fp_print_deserialize (const guchar *data,
finger = finger_int8;
/* Assume data is valid at this point if the values are somewhat sane. */
switch (type)
if (type == FPI_PRINT_NBIS)
{
case FPI_PRINT_NBIS:
{
g_autoptr(GVariant) prints = g_variant_get_child_value (print_data, 0);
guint i;
g_autoptr(GVariant) prints = g_variant_get_child_value (print_data, 0);
guint i;
result = g_object_new (FP_TYPE_PRINT,
"driver", driver,
"device-id", device_id,
"device-stored", device_stored,
NULL);
g_object_ref_sink (result);
fpi_print_set_type (result, FPI_PRINT_NBIS);
for (i = 0; i < g_variant_n_children (prints); i++)
{
g_autofree struct xyt_struct *xyt = NULL;
const gint32 *xcol, *ycol, *thetacol;
gsize xlen, ylen, thetalen;
g_autoptr(GVariant) xyt_data = NULL;
GVariant *child;
result = g_object_new (FP_TYPE_PRINT,
"driver", driver,
"device-id", device_id,
"device-stored", device_stored,
NULL);
g_object_ref_sink (result);
fpi_print_set_type (result, FPI_PRINT_NBIS);
for (i = 0; i < g_variant_n_children (prints); i++)
{
g_autofree struct xyt_struct *xyt = NULL;
const gint32 *xcol, *ycol, *thetacol;
gsize xlen, ylen, thetalen;
g_autoptr(GVariant) xyt_data = NULL;
GVariant *child;
xyt_data = g_variant_get_child_value (prints, i);
xyt_data = g_variant_get_child_value (prints, i);
child = g_variant_get_child_value (xyt_data, 0);
xcol = g_variant_get_fixed_array (child, &xlen, sizeof (gint32));
g_variant_unref (child);
child = g_variant_get_child_value (xyt_data, 0);
xcol = g_variant_get_fixed_array (child, &xlen, sizeof (gint32));
g_variant_unref (child);
child = g_variant_get_child_value (xyt_data, 1);
ycol = g_variant_get_fixed_array (child, &ylen, sizeof (gint32));
g_variant_unref (child);
child = g_variant_get_child_value (xyt_data, 1);
ycol = g_variant_get_fixed_array (child, &ylen, sizeof (gint32));
g_variant_unref (child);
child = g_variant_get_child_value (xyt_data, 2);
thetacol = g_variant_get_fixed_array (child, &thetalen, sizeof (gint32));
g_variant_unref (child);
child = g_variant_get_child_value (xyt_data, 2);
thetacol = g_variant_get_fixed_array (child, &thetalen, sizeof (gint32));
g_variant_unref (child);
if (xlen != ylen || xlen != thetalen)
goto invalid_format;
if (xlen != ylen || xlen != thetalen)
goto invalid_format;
if (xlen > G_N_ELEMENTS (xyt->xcol))
goto invalid_format;
if (xlen > G_N_ELEMENTS (xyt->xcol))
goto invalid_format;
xyt = g_new0 (struct xyt_struct, 1);
xyt->nrows = xlen;
memcpy (xyt->xcol, xcol, sizeof (xcol[0]) * xlen);
memcpy (xyt->ycol, ycol, sizeof (xcol[0]) * xlen);
memcpy (xyt->thetacol, thetacol, sizeof (xcol[0]) * xlen);
xyt = g_new0 (struct xyt_struct, 1);
xyt->nrows = xlen;
memcpy (xyt->xcol, xcol, sizeof (xcol[0]) * xlen);
memcpy (xyt->ycol, ycol, sizeof (xcol[0]) * xlen);
memcpy (xyt->thetacol, thetacol, sizeof (xcol[0]) * xlen);
g_ptr_array_add (result->prints, g_steal_pointer (&xyt));
}
}
break;
g_ptr_array_add (result->prints, g_steal_pointer (&xyt));
}
}
else if (type == FPI_PRINT_RAW || type == FPI_PRINT_SDCP)
{
g_autoptr(GVariant) fp_data = g_variant_get_child_value (print_data, 0);
case FPI_PRINT_RAW:
{
g_autoptr(GVariant) fp_data = g_variant_get_child_value (print_data, 0);
result = g_object_new (FP_TYPE_PRINT,
"fpi-type", type,
"driver", driver,
"device-id", device_id,
"device-stored", device_stored,
"fpi-data", fp_data,
NULL);
g_object_ref_sink (result);
}
break;
case FPI_PRINT_UNDEFINED:
{
g_autofree char *type_str = g_enum_to_string (fpi_print_type_get_type (), type);
g_warning ("Invalid print type: 0x%X (%s)", type, type_str);
goto invalid_format;
}
result = g_object_new (FP_TYPE_PRINT,
"fpi-type", type,
"driver", driver,
"device-id", device_id,
"device-stored", device_stored,
"fpi-data", fp_data,
NULL);
g_object_ref_sink (result);
}
else
{
g_warning ("Invalid print type: 0x%X", type);
goto invalid_format;
}
date = g_date_new_julian (julian_date);
+46
View File
@@ -0,0 +1,46 @@
/*
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "fpi-sdcp-device.h"
typedef struct
{
GBytes *host_private_key;
GBytes *host_public_key;
GBytes *host_random;
GBytes *reconnect_random;
GBytes *identify_nonce;
GBytes *data;
} FpSdcpDevicePrivate;
void fpi_sdcp_device_get_application_secret (FpSdcpDevice *self,
GBytes **application_secret);
void fpi_sdcp_device_set_application_secret (FpSdcpDevice *self,
GBytes *application_secret);
void fpi_sdcp_device_open (FpSdcpDevice *self);
void fpi_sdcp_device_connect (FpSdcpDevice *self);
void fpi_sdcp_device_reconnect (FpSdcpDevice *self);
void fpi_sdcp_device_list (FpSdcpDevice *self);
void fpi_sdcp_device_enroll (FpSdcpDevice *self);
void fpi_sdcp_device_identify (FpSdcpDevice *self);
+177
View File
@@ -0,0 +1,177 @@
/*
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "sdcp_device"
#include "fpi-log.h"
#include "fp-sdcp-device-private.h"
/**
* SECTION: fp-sdcp-device
* @title: FpSdcpDevice
* @short_description: SDCP device subclass
*
* This is a base class for devices implementing the SDCP security protocol.
*/
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (FpSdcpDevice, fp_sdcp_device, FP_TYPE_DEVICE)
enum {
PROP_0,
PROP_SDCP_DATA,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
/*******************************************************/
/* Callbacks/vfuncs */
static void
fp_sdcp_device_open (FpDevice *device)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (device);
fpi_sdcp_device_open (self);
}
static void
fp_sdcp_device_list (FpDevice *device)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (device);
fpi_sdcp_device_list (self);
}
static void
fp_sdcp_device_enroll (FpDevice *device)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (device);
fpi_sdcp_device_enroll (self);
}
static void
fp_sdcp_device_identify (FpDevice *device)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (device);
fpi_sdcp_device_identify (self);
}
/*********************************************************/
static void
fp_sdcp_device_finalize (GObject *object)
{
FpSdcpDevice *self = (FpSdcpDevice *) object;
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_clear_pointer (&priv->host_private_key, g_bytes_unref);
g_clear_pointer (&priv->host_public_key, g_bytes_unref);
g_clear_pointer (&priv->host_random, g_bytes_unref);
g_clear_pointer (&priv->reconnect_random, g_bytes_unref);
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
g_clear_pointer (&priv->data, g_bytes_unref);
G_OBJECT_CLASS (fp_sdcp_device_parent_class)->finalize (object);
}
static void
fp_sdcp_device_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
FpSdcpDevice *self = (FpSdcpDevice *) object;
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
switch (prop_id)
{
case PROP_SDCP_DATA:
g_value_set_boxed (value, priv->data);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
fp_sdcp_device_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (object);
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
switch (prop_id)
{
case PROP_SDCP_DATA:
g_clear_pointer (&priv->data, g_bytes_unref);
priv->data = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
fp_sdcp_device_constructed (GObject *obj)
{
G_OBJECT_CLASS (fp_sdcp_device_parent_class)->constructed (obj);
}
static void
fp_sdcp_device_class_init (FpSdcpDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
FpDeviceClass *fp_device_class = FP_DEVICE_CLASS (klass);
object_class->finalize = fp_sdcp_device_finalize;
object_class->get_property = fp_sdcp_device_get_property;
object_class->set_property = fp_sdcp_device_set_property;
object_class->constructed = fp_sdcp_device_constructed;
fp_device_class->open = fp_sdcp_device_open;
fp_device_class->list = fp_sdcp_device_list;
fp_device_class->enroll = fp_sdcp_device_enroll;
fp_device_class->verify = fp_sdcp_device_identify;
fp_device_class->identify = fp_sdcp_device_identify;
properties[PROP_SDCP_DATA] =
g_param_spec_boxed ("sdcp-data",
"SDCP Data",
"SDCP-related device data that should be persisted and used with the "
"device during the current system boot",
G_TYPE_BYTES,
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
g_object_class_install_properties (object_class, N_PROPS, properties);
fpi_device_class_auto_initialize_features (fp_device_class);
}
static void
fp_sdcp_device_init (FpSdcpDevice *self)
{
}
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2026 Canonical, Ltd.
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -16,18 +17,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "fpi-device.h"
#include "fpi-test-emulation.h"
#pragma once
gboolean
(fpi_device_emulation_mode_enabled) (FpDevice *device)
{
static gsize emulation_mode = 0;
#include <fp-device.h>
if (g_once_init_enter (&emulation_mode))
g_once_init_leave (&emulation_mode,
g_strcmp0 (g_getenv (FPI_EMULATION_ENV_VAR), "1") == 0 ?
TRUE : G_MAXSIZE);
G_BEGIN_DECLS
return emulation_mode == TRUE;
}
#define FP_TYPE_SDCP_DEVICE (fp_sdcp_device_get_type ())
G_DECLARE_DERIVABLE_TYPE (FpSdcpDevice, fp_sdcp_device, FP, SDCP_DEVICE, FpDevice)
G_END_DECLS
-84
View File
@@ -65,31 +65,6 @@ fpi_byte_reader_new (const guint8 * data, guint size)
return ret;
}
/**
* fpi_byte_reader_new_bytes: (skip)
* @bytes: (in) (transfer none): a #GBytes instance from which the
* #FpiByteReader should read
*
* Create a new #FpiByteReader instance, which will read from @bytes.
*
* Free-function: fpi_byte_reader_free
*
* Returns: (transfer full): a new #FpiByteReader instance
*/
FpiByteReader *
fpi_byte_reader_new_bytes (GBytes * bytes)
{
const guint8 *data;
gsize size = 0;
g_return_val_if_fail (bytes != NULL, NULL);
data = g_bytes_get_data (bytes, &size);
g_return_val_if_fail (size <= G_MAXUINT, NULL);
return fpi_byte_reader_new (data, (guint) size);
}
/**
* fpi_byte_reader_free:
* @reader: (in) (transfer full): a #FpiByteReader instance
@@ -125,30 +100,6 @@ fpi_byte_reader_init (FpiByteReader * reader, const guint8 * data, guint size)
reader->byte = 0;
}
/**
* fpi_byte_reader_init_bytes:
* @reader: a #FpiByteReader instance
* @bytes: (in) (transfer none): a #GBytes instance from which
* the #FpiByteReader should read
*
* Initializes a #FpiByteReader instance to read from @bytes. This function
* can be called on already initialized instances.
*/
FpiByteReader *
fpi_byte_reader_init_bytes (FpiByteReader * reader, GBytes * bytes)
{
g_return_val_if_fail (reader != NULL, NULL);
g_return_val_if_fail (bytes != NULL, NULL);
gsize size = 0;
const guint8 *data = g_bytes_get_data (bytes, &size);
g_return_val_if_fail (size <= G_MAXUINT, NULL);
fpi_byte_reader_init (reader, data, (guint) size);
return reader;
}
/**
* fpi_byte_reader_peek_sub_reader: (skip)
* @reader: an existing and initialized #FpiByteReader instance
@@ -848,41 +799,6 @@ fpi_byte_reader_peek_data (const FpiByteReader * reader, guint size,
return fpi_byte_reader_peek_data_inline (reader, size, val);
}
/**
* fpi_byte_reader_get_data_fixed:
* @reader: a #FpiByteReader instance
* @size: Size in bytes
* @val: (out) (array length=size): address of a buffer to copy data into
*
* Copies @size bytes from the current data position into @val if at
* least @size bytes are left, and updates the current position.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
(fpi_byte_reader_get_data_static) (FpiByteReader * reader, guint size,
const guint8 * val)
{
return (fpi_byte_reader_get_data_inline_static) (reader, size, val);
}
/**
* fpi_byte_reader_peek_data_fixed:
* @reader: a #FpiByteReader instance
* @size: Size in bytes
* @val: (out) (array length=size): address of a buffer to copy data into
*
* Like fpi_byte_reader_get_data_fixed() but does not advance the position.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
(fpi_byte_reader_peek_data_static) (const FpiByteReader * reader, guint size,
guint8 * val)
{
return (fpi_byte_reader_peek_data_inline_static) (reader, size, val);
}
/**
* fpi_byte_reader_dup_data:
* @reader: a #FpiByteReader instance
-100
View File
@@ -50,7 +50,6 @@ typedef struct {
FpiByteReader * fpi_byte_reader_new (const guint8 *data, guint size) G_GNUC_MALLOC;
FpiByteReader * fpi_byte_reader_new_bytes (GBytes *bytes) G_GNUC_MALLOC;
void fpi_byte_reader_free (FpiByteReader *reader);
@@ -58,9 +57,6 @@ void fpi_byte_reader_free (FpiByteReader *reader);
void fpi_byte_reader_init (FpiByteReader *reader, const guint8 *data, guint size);
FpiByteReader * fpi_byte_reader_init_bytes (FpiByteReader *reader, GBytes *bytes);
gboolean fpi_byte_reader_peek_sub_reader (FpiByteReader * reader,
FpiByteReader * sub_reader,
guint size);
@@ -222,21 +218,8 @@ gboolean fpi_byte_reader_dup_data (FpiByteReader * reader, guint s
gboolean fpi_byte_reader_get_data (FpiByteReader * reader, guint size, const guint8 ** val);
gboolean fpi_byte_reader_get_data_static (FpiByteReader * reader, guint size, const guint8 * val);
gboolean fpi_byte_reader_peek_data (const FpiByteReader * reader, guint size, const guint8 ** val);
gboolean fpi_byte_reader_peek_data_static (const FpiByteReader * reader, guint size, guint8 * val);
GBytes * fpi_byte_reader_get_bytes (FpiByteReader *reader, guint size);
GBytes * fpi_byte_reader_peek_bytes (const FpiByteReader *reader, guint size);
#define fpi_byte_reader_dup_string(reader,str) \
fpi_byte_reader_dup_string_utf8(reader,str)
@@ -659,60 +642,6 @@ fpi_byte_reader_peek_data_inline (const FpiByteReader * reader, guint size, cons
return TRUE;
}
static inline GBytes *
fpi_byte_reader_peek_bytes_inline (const FpiByteReader *reader, guint size)
{
const guint8 *data;
g_return_val_if_fail (reader != NULL, NULL);
if (G_UNLIKELY (size > reader->size || fpi_byte_reader_get_remaining_unchecked (reader) < size))
return NULL;
data = fpi_byte_reader_peek_data_unchecked (reader);
return g_bytes_new_static (data, size);
}
static inline GBytes *
fpi_byte_reader_get_bytes_inline (FpiByteReader *reader, guint size)
{
const guint8 *data;
g_return_val_if_fail (reader != NULL, NULL);
if (G_UNLIKELY (size > reader->size || fpi_byte_reader_get_remaining_unchecked (reader) < size))
return NULL;
data = fpi_byte_reader_get_data_unchecked (reader, size);
return g_bytes_new_static (data, size);
}
static inline gboolean
(fpi_byte_reader_get_data_inline_static) (FpiByteReader * reader, guint size, const guint8 * val)
{
g_return_val_if_fail (reader != NULL, FALSE);
g_return_val_if_fail (val != NULL, FALSE);
if (G_UNLIKELY (size > reader->size || fpi_byte_reader_get_remaining_unchecked (reader) < size))
return FALSE;
memcpy ((void *) val, fpi_byte_reader_get_data_unchecked (reader, size), size);
return TRUE;
}
static inline gboolean
(fpi_byte_reader_peek_data_inline_static) (const FpiByteReader * reader, guint size, guint8 * val)
{
g_return_val_if_fail (reader != NULL, FALSE);
g_return_val_if_fail (val != NULL, FALSE);
if (G_UNLIKELY (size > reader->size || fpi_byte_reader_get_remaining_unchecked (reader) < size))
return FALSE;
memcpy (val, fpi_byte_reader_peek_data_unchecked (reader), size);
return TRUE;
}
static inline guint
fpi_byte_reader_get_pos_inline (const FpiByteReader * reader)
{
@@ -743,35 +672,6 @@ fpi_byte_reader_skip_inline (FpiByteReader * reader, guint nbytes)
G_LIKELY(fpi_byte_reader_peek_data_inline(reader,size,val))
#define fpi_byte_reader_skip(reader,nbytes) \
G_LIKELY(fpi_byte_reader_skip_inline(reader,nbytes))
#define fpi_byte_reader_get_bytes(reader,size) \
fpi_byte_reader_get_bytes_inline(reader,size)
#define fpi_byte_reader_peek_bytes(reader,size) \
fpi_byte_reader_peek_bytes_inline(reader,size)
/**
* fpi_byte_reader_get_data_static:
* @reader: a #FpiByteReader
* @val: fixed-size array (e.g. `uint8_t buf[32]`)
*
* Reads @size bytes from @reader directly into @val, where @size is
* deduced via `sizeof()` - only safe with true C arrays.
*
* Returns: %TRUE on success, %FALSE otherwise.
*/
#define fpi_byte_reader_get_data_static(reader,val) \
(fpi_byte_reader_get_data_static) (reader,sizeof(val),val)
/**
* fpi_byte_reader_peek_data_static:
* @reader: a #FpiByteReader
* @val: fixed-size array (e.g. `uint8_t buf[32]`)
*
* Like fpi_byte_reader_get_data_static() but does not advance the position.
*
* Returns: %TRUE on success, %FALSE otherwise.
*/
#define fpi_byte_reader_peek_data_static(reader,val) \
(fpi_byte_reader_peek_data_static) (reader,sizeof(val),val)
#endif /* FPI_BYTE_READER_DISABLE_INLINES */
-39
View File
@@ -219,36 +219,6 @@ fpi_byte_writer_reset_and_get_data (FpiByteWriter * writer)
return data;
}
/**
* fpi_byte_writer_reset_and_get_bytes:
* @writer: #FpiByteWriter instance
*
* Resets @writer and returns the current data as a #GBytes.
*
* Returns: (transfer full): the current data as a #GBytes.
*/
GBytes *
fpi_byte_writer_reset_and_get_bytes (FpiByteWriter * writer)
{
GBytes *bytes;
g_return_val_if_fail (writer != NULL, NULL);
if (!writer->owned)
{
bytes = g_bytes_new (g_steal_pointer (&writer->parent.data),
writer->parent.size);
}
else
{
bytes = g_bytes_new_take ((gpointer) g_steal_pointer (&writer->parent.data),
writer->parent.size);
}
fpi_byte_writer_reset (writer);
return g_steal_pointer (&bytes);
}
/**
* fpi_byte_writer_free:
* @writer: (in) (transfer full): #FpiByteWriter instance
@@ -632,15 +602,6 @@ CREATE_WRITE_STRING_FUNC (32, guint32);
*
* Returns: %TRUE if the value could be written
*/
/**
* fpi_byte_writer_put_bytes:
* @writer: #FpiByteWriter instance
* @bytes: (transfer none): Data to write
*
* Writes the contents of @bytes to @writer.
*
* Returns: %TRUE if the value could be written
*/
/**
* fpi_byte_writer_fill:
* @writer: #FpiByteWriter instance
-22
View File
@@ -77,9 +77,6 @@ void fpi_byte_writer_reset (FpiByteWriter *writer);
guint8 * fpi_byte_writer_reset_and_get_data (FpiByteWriter *writer);
GBytes * fpi_byte_writer_reset_and_get_bytes (FpiByteWriter *writer);
/**
* fpi_byte_writer_get_pos:
* @writer: #FpiByteWriter instance
@@ -207,9 +204,6 @@ gboolean fpi_byte_writer_put_float64_le (FpiByteWriter *writer, gdoubl
gboolean fpi_byte_writer_put_data (FpiByteWriter *writer, const guint8 *data, guint size);
gboolean fpi_byte_writer_put_bytes (FpiByteWriter *writer, const GBytes *bytes);
gboolean fpi_byte_writer_fill (FpiByteWriter *writer, guint8 value, guint size);
@@ -344,18 +338,6 @@ fpi_byte_writer_put_data_inline (FpiByteWriter * writer, const guint8 * data,
return TRUE;
}
static inline gboolean
fpi_byte_writer_put_bytes_inline (FpiByteWriter * writer, const GBytes * bytes)
{
g_return_val_if_fail (writer != NULL, FALSE);
g_return_val_if_fail (bytes != NULL, FALSE);
const guint8 *data;
gsize size;
data = g_bytes_get_data ((GBytes *) bytes, &size);
return fpi_byte_writer_put_data_inline (writer, data, size);
}
static inline void
fpi_byte_writer_fill_unchecked (FpiByteWriter * writer, guint8 value, guint size)
{
@@ -431,10 +413,6 @@ fpi_byte_writer_fill_inline (FpiByteWriter * writer, guint8 value, guint size)
#define fpi_byte_writer_put_data(writer, data, size) \
G_LIKELY (fpi_byte_writer_put_data_inline (writer, data, size))
#define fpi_byte_writer_put_data_static(writer, data) \
G_LIKELY (fpi_byte_writer_put_data_inline (writer, data, sizeof (data)))
#define fpi_byte_writer_put_bytes(writer, bytes) \
G_LIKELY (fpi_byte_writer_put_bytes_inline (writer, bytes))
#define fpi_byte_writer_fill(writer, val, size) \
G_LIKELY (fpi_byte_writer_fill_inline (writer, val, size))
+49 -243
View File
@@ -22,12 +22,10 @@
#include <math.h>
#include <fcntl.h>
#include <errno.h>
#include <gmodule.h>
#include "fpi-log.h"
#include "fp-device-private.h"
#include "tests/fpi-test-emulation.h"
/**
* SECTION: fpi-device
@@ -52,88 +50,8 @@ fp_device_get_instance_private (FpDevice *self)
g_type_class_get_instance_private_offset (dev_class));
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GModule, g_module_close)
static char *
error_to_string (GError * error)
{
g_autofree char *domain_str = NULL;
if (!error)
return g_strdup ("none");
if (error->domain == FP_DEVICE_RETRY)
domain_str = g_enum_to_string (FP_TYPE_DEVICE_RETRY, error->code);
else if (error->domain == FP_DEVICE_ERROR)
domain_str = g_enum_to_string (FP_TYPE_DEVICE_ERROR, error->code);
else if (error->domain == G_IO_ERROR)
domain_str = g_enum_to_string (g_io_error_enum_get_type (), error->code);
else if (error->domain == G_USB_DEVICE_ERROR)
domain_str = g_strdup_printf ("G_USB_DEVICE_ERROR_%u", error->code);
else
domain_str = g_strdup_printf ("%s_%u",
g_quark_to_string (error->domain),
error->code);
return g_strdup_printf ("[%s] %s", domain_str, error->message);
}
/**
* fpi_device_emulation_mode_enabled:
* @device: The #FpDevice to check
*
* Checks if the device is running in emulation mode, which is enabled by
* setting the FP_DEVICE_EMULATION environment variable to a '1' value but
* only when the test emulation library is loaded.
* This is used by some drivers to enable special behavior for testing
* and development purposes.
*/
gboolean
(fpi_device_emulation_mode_enabled) (FpDevice *device)
{
static gboolean (*real_fn)(FpDevice *) = NULL;
static gsize emulation_mode = 0;
if (g_once_init_enter (&emulation_mode))
{
if (g_strcmp0 (g_getenv (FPI_EMULATION_ENV_VAR), "1") == 0)
{
const char *dirs[] = {
FPI_EMULATION_HELPER_BUILDDIR,
FPI_EMULATION_HELPER_INSTALLDIR,
};
for (size_t i = 0; i < G_N_ELEMENTS (dirs); ++i)
{
g_autofree char *path = NULL;
g_autoptr(GModule) mod = NULL;
gpointer sym;
path = g_build_filename (dirs[i], FPI_EMULATION_HELPER_MODULE, NULL);
if (!(mod = g_module_open (path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL)))
continue;
if (!g_module_symbol (mod, "fpi_device_emulation_mode_enabled", &sym))
continue;
real_fn = (gboolean (*)(FpDevice *)) sym;
g_steal_pointer (&mod);
break;
}
}
g_once_init_leave (&emulation_mode, real_fn ? TRUE : G_MAXSIZE);
}
if (real_fn)
return real_fn (device);
return FALSE;
}
/**
* fpi_device_class_auto_initialize_features:
* @device_class: An #FpDeviceClass to initialize
*
* Initializes the #FpDeviceClass @features flags checking what device vfuncs
* are implemented.
@@ -151,7 +69,7 @@ fpi_device_class_auto_initialize_features (FpDeviceClass *device_class)
device_class->features |= FP_DEVICE_FEATURE_VERIFY;
if (device_class->identify)
device_class->features |= FP_DEVICE_FEATURE_IDENTIFY | FP_DEVICE_FEATURE_VERIFY;
device_class->features |= FP_DEVICE_FEATURE_IDENTIFY;
if (device_class->list)
device_class->features |= FP_DEVICE_FEATURE_STORAGE_LIST;
@@ -272,6 +190,9 @@ fpi_device_error_new (FpDeviceError error)
case FP_DEVICE_ERROR_TOO_HOT:
msg = "Device disabled to prevent overheating.";
case FP_DEVICE_ERROR_UNTRUSTED:
msg = "Could not verify integrity of the device, it cannot be trusted!";
break;
default:
@@ -796,7 +717,7 @@ fpi_device_remove (FpDevice *device)
/**
* fpi_device_action_error:
* @device: The #FpDevice
* @error: (nullable) (transfer full): The #GError or %NULL on success
* @error: The #GError to return
*
* Finish an ongoing action with an error. This is the same as calling
* the corresponding complete function such as fpi_device_open_complete()
@@ -814,14 +735,11 @@ fpi_device_action_error (FpDevice *device,
if (error != NULL)
{
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_autofree char *action_str = g_enum_to_string (FPI_TYPE_DEVICE_ACTION, priv->current_action);
g_autofree char *action_str = NULL;
g_debug ("Device reported generic error (%s) during action; action was: %s",
error_str, action_str);
}
action_str = g_enum_to_string (FPI_TYPE_DEVICE_ACTION, priv->current_action);
g_debug ("Device reported generic error (%s) during action; action was: %s",
error->message, action_str);
}
else
{
@@ -1034,18 +952,17 @@ typedef struct _FpDeviceTaskReturnData
static gboolean
fp_device_task_return_in_idle_cb (gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(GError) cancellation_reason = NULL;
FpDeviceTaskReturnData *data = user_data;
FpDevicePrivate *priv = fp_device_get_instance_private (data->device);
g_autofree char *action_str = NULL;
FpiDeviceAction action;
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *action_str = g_enum_to_string (FPI_TYPE_DEVICE_ACTION, priv->current_action);
g_autoptr(GTask) task = NULL;
g_autoptr(GError) cancellation_reason = NULL;
g_debug ("Completing action %s in idle!", action_str);
}
action_str = g_enum_to_string (FPI_TYPE_DEVICE_ACTION, priv->current_action);
g_debug ("Completing action %s in idle!", action_str);
task = g_steal_pointer (&priv->current_task);
action = priv->current_action;
@@ -1213,13 +1130,7 @@ fpi_device_probe_complete (FpDevice *device,
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_PROBE);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported probe completion (ID: %s, name: %s, error: %s)",
device_id, device_name, error_str);
}
g_debug ("Device reported probe completion");
clear_device_cancel_action (device);
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE);
@@ -1262,12 +1173,7 @@ fpi_device_open_complete (FpDevice *device, GError *error)
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_OPEN);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported open completion (error: %s)", error_str);
}
g_debug ("Device reported open completion");
clear_device_cancel_action (device);
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE);
@@ -1289,18 +1195,13 @@ fpi_device_open_complete (FpDevice *device, GError *error)
void
fpi_device_close_complete (FpDevice *device, GError *error)
{
g_autoptr(GError) nested_error = NULL;
GError *nested_error = NULL;
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_CLOSE);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported close completion (error: %s)", error_str);
}
g_debug ("Device reported close completion");
clear_device_cancel_action (device);
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE);
@@ -1311,8 +1212,7 @@ fpi_device_close_complete (FpDevice *device, GError *error)
if (!g_usb_device_close (priv->usb_device, &nested_error))
{
if (error == NULL)
error = g_steal_pointer (&nested_error);
error = nested_error;
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
return;
}
@@ -1353,13 +1253,7 @@ fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error)
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_ENROLL);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported enroll completion (print: %p, error: %s)",
print, error_str);
}
g_debug ("Device reported enroll completion");
clear_device_cancel_action (device);
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE);
@@ -1369,6 +1263,7 @@ fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error)
if (FP_IS_PRINT (print))
{
FpiPrintType print_type;
g_autofree char *finger_str = NULL;
g_object_get (print, "fpi-type", &print_type, NULL);
if (print_type == FPI_PRINT_UNDEFINED)
@@ -1382,12 +1277,8 @@ fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error)
return;
}
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *finger_str = g_enum_to_string (FP_TYPE_FINGER, fp_print_get_finger (print));
g_debug ("Print for finger %s enrolled", finger_str);
}
finger_str = g_enum_to_string (FP_TYPE_FINGER, fp_print_get_finger (print));
g_debug ("Print for finger %s enrolled", finger_str);
fpi_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, print);
}
@@ -1434,12 +1325,7 @@ fpi_device_verify_complete (FpDevice *device,
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_VERIFY);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported verify completion (error: %s)", error_str);
}
g_debug ("Device reported verify completion");
data = g_task_get_task_data (priv->current_task);
@@ -1504,12 +1390,7 @@ fpi_device_identify_complete (FpDevice *device,
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_IDENTIFY);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported identify completion (error: %s)", error_str);
}
g_debug ("Device reported identify completion");
data = g_task_get_task_data (priv->current_task);
@@ -1568,13 +1449,7 @@ fpi_device_capture_complete (FpDevice *device,
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_CAPTURE);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported capture completion (image: %p, error: %s)",
image, error_str);
}
g_debug ("Device reported capture completion");
clear_device_cancel_action (device);
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE);
@@ -1620,12 +1495,7 @@ fpi_device_delete_complete (FpDevice *device,
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_DELETE);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported delete completion (error: %s)", error_str);
}
g_debug ("Device reported deletion completion");
clear_device_cancel_action (device);
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE);
@@ -1660,13 +1530,7 @@ fpi_device_list_complete (FpDevice *device,
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_LIST);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported listing completion (prints: %ld, error: %s)",
prints ? (long) prints->len : -1, error_str);
}
g_debug ("Device reported listing completion");
clear_device_cancel_action (device);
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE);
@@ -1935,7 +1799,7 @@ fpi_device_suspend_completed (FpDevice *device)
* current action will be cancelled before the error is forwarded to the
* application.
*
* It is recommended to set @error to #FP_DEVICE_ERROR_NOT_SUPPORTED.
* It is recommended to set @error to #FP_ERROR_NOT_IMPLEMENTED.
*/
void
fpi_device_suspend_complete (FpDevice *device,
@@ -1947,13 +1811,6 @@ fpi_device_suspend_complete (FpDevice *device,
g_return_if_fail (priv->suspend_resume_task);
g_return_if_fail (priv->suspend_error == NULL);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported suspend completion (error: %s)", error_str);
}
priv->suspend_error = g_steal_pointer (&error);
priv->is_suspended = TRUE;
@@ -1997,13 +1854,6 @@ fpi_device_resume_complete (FpDevice *device,
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->suspend_resume_task);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported resume completion (error: %s)", error_str);
}
priv->is_suspended = FALSE;
fpi_device_configure_wakeup (device, FALSE);
@@ -2031,12 +1881,7 @@ fpi_device_clear_storage_complete (FpDevice *device,
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FPI_DEVICE_ACTION_CLEAR_STORAGE);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported clear storage completion (error: %s)", error_str);
}
g_debug ("Device reported deletion completion");
clear_device_cancel_action (device);
fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE);
@@ -2125,14 +1970,7 @@ fpi_device_verify_report (FpDevice *device,
data->result_reported = TRUE;
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *result_str = g_enum_to_string (FPI_TYPE_MATCH_RESULT, result);
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported verify result (result: %s, print: %p, error: %s)",
result_str, print, error_str);
}
g_debug ("Device reported verify result");
if (print)
print = g_object_ref_sink (print);
@@ -2168,14 +2006,6 @@ fpi_device_verify_report (FpDevice *device,
{
fpi_device_get_verify_data (device, &data->match);
g_object_ref (data->match);
if (print &&
fpi_print_get_type (print) != FPI_PRINT_NBIS &&
!fp_print_equal (print, data->match))
{
g_warning ("Driver reported a match providing a scanned print that is not matching it.");
g_clear_object (&print);
}
}
data->print = g_steal_pointer (&print);
@@ -2232,20 +2062,14 @@ fpi_device_identify_report (FpDevice *device,
if (print)
print = g_object_ref_sink (print);
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *error_str = error_to_string (error);
g_debug ("Device reported identify result (match: %p, print: %p, error: %s)",
match, print, error_str);
}
if (match && !g_ptr_array_find (data->gallery, match, NULL))
{
g_warning ("Driver reported a match to a print that was not in the gallery, ignoring match.");
g_clear_object (&match);
}
g_debug ("Device reported identify result");
if (error)
{
if (match != NULL)
@@ -2270,14 +2094,6 @@ fpi_device_identify_report (FpDevice *device,
}
else
{
if (match && print && fpi_print_get_type (print) != FPI_PRINT_NBIS &&
!g_ptr_array_find_with_equal_func (data->gallery, print,
(GEqualFunc) fp_print_equal, NULL))
{
g_warning ("Driver reported a match providing a scanned print that is not matching any in the gallery.");
g_clear_object (&print);
}
if (match)
data->match = g_steal_pointer (&match);
@@ -2304,21 +2120,13 @@ fpi_device_report_finger_status (FpDevice *device,
FpFingerStatusFlags finger_status)
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_autofree char *status_string = NULL;
if (priv->finger_status == finger_status)
return FALSE;
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *status_string = NULL;
g_autofree char *old_status_string = NULL;
old_status_string = g_flags_to_string (FP_TYPE_FINGER_STATUS_FLAGS, priv->finger_status);
status_string = g_flags_to_string (FP_TYPE_FINGER_STATUS_FLAGS, finger_status);
g_debug ("Device reported finger status change: %s -> %s",
old_status_string, status_string);
}
status_string = g_flags_to_string (FP_TYPE_FINGER_STATUS_FLAGS, finger_status);
fp_dbg ("Device reported finger status change: %s", status_string);
priv->finger_status = finger_status;
g_object_notify (G_OBJECT (device), "finger-status");
@@ -2379,6 +2187,8 @@ fpi_device_update_temp (FpDevice *device, gboolean is_active)
gdouble next_threshold;
gdouble old_ratio;
FpTemperature old_temp;
g_autofree char *old_temp_str = NULL;
g_autofree char *new_temp_str = NULL;
if (priv->temp_hot_seconds < 0)
{
@@ -2428,20 +2238,16 @@ fpi_device_update_temp (FpDevice *device, gboolean is_active)
next_threshold = is_active ? -1.0 : TEMP_HOT_WARM_THRESH;
}
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
g_autofree char *old_temp_str = g_enum_to_string (FP_TYPE_TEMPERATURE, old_temp);
g_autofree char *new_temp_str = g_enum_to_string (FP_TYPE_TEMPERATURE, priv->temp_current);
g_debug ("Updated temperature model after %0.2f seconds, ratio %0.2f -> %0.2f, active %d -> %d, %s -> %s",
passed_seconds,
old_ratio,
priv->temp_current_ratio,
priv->temp_last_active,
is_active,
old_temp_str,
new_temp_str);
}
old_temp_str = g_enum_to_string (FP_TYPE_TEMPERATURE, old_temp);
new_temp_str = g_enum_to_string (FP_TYPE_TEMPERATURE, priv->temp_current);
g_debug ("Updated temperature model after %0.2f seconds, ratio %0.2f -> %0.2f, active %d -> %d, %s -> %s",
passed_seconds,
old_ratio,
priv->temp_current_ratio,
priv->temp_last_active,
is_active,
old_temp_str,
new_temp_str);
if (priv->temp_current != old_temp)
g_object_notify (G_OBJECT (device), "temperature");
+2 -16
View File
@@ -38,13 +38,6 @@ typedef enum {
/**
* FpIdEntry:
* @pid: The USB product ID (for USB devices)
* @vid: The USB vendor ID (for USB devices)
* @virtual_envvar: Environment variable name (for virtual devices)
* @driver_data: Optional driver-specific data, defaults to 0
* @udev_types: Subtypes for udev-based devices
* @spi_acpi_id: ACPI ID (for SPI devices)
* @hid_id: HID device identifier (for HID devices)
*
* An entry in the table of supported hardware. For USB devices, the product ID
* and vendor ID should be provided. The optional @driver_data field defaults
@@ -107,8 +100,7 @@ struct _FpIdEntry
* guaranteed to only happen when the device is open (this includes delete).
* @close: Close the device again
* @enroll: Start an enroll operation
* @verify: Start a verify operation (it will be implemented via @identify
* if not overriden).
* @verify: Start a verify operation
* @identify: Start an identify operation
* @capture: Start a capture operation
* @list: List prints stored on the device
@@ -229,6 +221,7 @@ GUsbDevice *fpi_device_get_usb_device (FpDevice *device);
const gchar *fpi_device_get_virtual_env (FpDevice *device);
gpointer fpi_device_get_udev_data (FpDevice *device,
FpiDeviceUdevSubtypeFlags subtype);
//const gchar *fpi_device_get_spi_dev (FpDevice *device);
FpiDeviceAction fpi_device_get_current_action (FpDevice *device);
@@ -332,11 +325,4 @@ gboolean fpi_device_report_finger_status_changes (FpDevice *device,
FpFingerStatusFlags added_status,
FpFingerStatusFlags removed_status);
/* Debugging utilities */
#define fpi_device_emulation_mode_enabled(dev) \
G_UNLIKELY ((fpi_device_emulation_mode_enabled) ((dev)))
gboolean (fpi_device_emulation_mode_enabled) (FpDevice *device);
G_END_DECLS
+36 -16
View File
@@ -120,13 +120,10 @@ fp_image_device_change_state (FpImageDevice *self, FpiImageDeviceState state)
{ FPI_IMAGE_DEVICE_STATE_DEACTIVATING, FPI_IMAGE_DEVICE_STATE_INACTIVE },
};
if (!g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN))
{
prev_state_str = g_enum_to_string (FPI_TYPE_IMAGE_DEVICE_STATE, priv->state);
state_str = g_enum_to_string (FPI_TYPE_IMAGE_DEVICE_STATE, state);
fp_dbg ("Image device internal state change from %s to %s",
prev_state_str, state_str);
}
prev_state_str = g_enum_to_string (FPI_TYPE_IMAGE_DEVICE_STATE, priv->state);
state_str = g_enum_to_string (FPI_TYPE_IMAGE_DEVICE_STATE, state);
fp_dbg ("Image device internal state change from %s to %s",
prev_state_str, state_str);
for (i = 0; i < G_N_ELEMENTS (valid_transitions); i++)
{
@@ -137,15 +134,8 @@ fp_image_device_change_state (FpImageDevice *self, FpiImageDeviceState state)
}
}
if (!transition_is_valid)
{
if (!prev_state_str)
prev_state_str = g_enum_to_string (FPI_TYPE_IMAGE_DEVICE_STATE, priv->state);
if (!state_str)
state_str = g_enum_to_string (FPI_TYPE_IMAGE_DEVICE_STATE, state);
g_warning ("Internal state machine issue: transition from %s to %s should not happen!",
prev_state_str, state_str);
}
g_warning ("Internal state machine issue: transition from %s to %s should not happen!",
prev_state_str, state_str);
priv->state = state;
g_object_notify (G_OBJECT (self), "fpi-image-device-state");
@@ -224,6 +214,10 @@ fp_image_device_maybe_complete_action (FpImageDevice *self, GError *error)
fpi_device_enroll_complete (device, g_object_ref (enroll_print), NULL);
}
else if (action == FPI_DEVICE_ACTION_VERIFY)
{
fpi_device_verify_complete (device, NULL);
}
else if (action == FPI_DEVICE_ACTION_IDENTIFY)
{
fpi_device_identify_complete (device, NULL);
@@ -322,6 +316,22 @@ fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, g
fp_image_device_enroll_maybe_await_finger_on (FP_IMAGE_DEVICE (device));
}
}
else if (action == FPI_DEVICE_ACTION_VERIFY)
{
FpPrint *template;
FpiMatchResult result;
fpi_device_get_verify_data (device, &template);
if (print)
result = fpi_print_bz3_match (template, print, priv->bz3_threshold, &error);
else
result = FPI_MATCH_ERROR;
if (!error || error->domain == FP_DEVICE_RETRY)
fpi_device_verify_report (device, result, g_steal_pointer (&print), g_steal_pointer (&error));
fp_image_device_maybe_complete_action (self, g_steal_pointer (&error));
}
else if (action == FPI_DEVICE_ACTION_IDENTIFY)
{
gint i;
@@ -476,6 +486,7 @@ fpi_image_device_image_captured (FpImageDevice *self, FpImage *image)
g_return_if_fail (image != NULL);
g_return_if_fail (priv->state == FPI_IMAGE_DEVICE_STATE_CAPTURE);
g_return_if_fail (action == FPI_DEVICE_ACTION_ENROLL ||
action == FPI_DEVICE_ACTION_VERIFY ||
action == FPI_DEVICE_ACTION_IDENTIFY ||
action == FPI_DEVICE_ACTION_CAPTURE);
@@ -516,6 +527,7 @@ fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry)
* all but INACTIVE */
g_return_if_fail (priv->state != FPI_IMAGE_DEVICE_STATE_INACTIVE);
g_return_if_fail (action == FPI_DEVICE_ACTION_ENROLL ||
action == FPI_DEVICE_ACTION_VERIFY ||
action == FPI_DEVICE_ACTION_IDENTIFY ||
action == FPI_DEVICE_ACTION_CAPTURE);
@@ -528,6 +540,13 @@ fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry)
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF);
}
else if (action == FPI_DEVICE_ACTION_VERIFY)
{
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_ERROR, NULL, error);
fp_image_device_maybe_complete_action (self, NULL);
fpi_image_device_deactivate (self, TRUE);
}
else if (action == FPI_DEVICE_ACTION_IDENTIFY)
{
fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, error);
@@ -620,6 +639,7 @@ fpi_image_device_activate_complete (FpImageDevice *self, GError *error)
g_return_if_fail (priv->active == FALSE);
g_return_if_fail (priv->state == FPI_IMAGE_DEVICE_STATE_ACTIVATING);
g_return_if_fail (action == FPI_DEVICE_ACTION_ENROLL ||
action == FPI_DEVICE_ACTION_VERIFY ||
action == FPI_DEVICE_ACTION_IDENTIFY ||
action == FPI_DEVICE_ACTION_CAPTURE);
-1
View File
@@ -27,7 +27,6 @@
* @FPI_IMAGE_V_FLIPPED: the image is vertically flipped
* @FPI_IMAGE_H_FLIPPED: the image is horizontally flipped
* @FPI_IMAGE_COLORS_INVERTED: the colours are inverted
* @FPI_IMAGE_PARTIAL: the image is a partial scan
*
* Flags used in an #FpImage structure to describe the contained image.
* This is useful for image drivers as they can simply set these flags and
+18 -49
View File
@@ -2,7 +2,6 @@
* FpiLog - Internal logging functions
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
* Copyright (C) 2026 Marco Trevisan (Treviño) <mail@3v1n0.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -21,68 +20,38 @@
#include "fpi-log.h"
/**
* fpi_log_is_debug_transfer_enabled:
*
* Checks if the %FP_DEBUG_TRANSFER environment variable is set.
*
* Returns: %TRUE if %FP_DEBUG_TRANSFER is set, %FALSE otherwise
*/
gboolean
fpi_log_is_debug_transfer_enabled (void)
{
static gsize debug_transfer_enabled = 0;
#undef fp_dbg_hex_dump_bytes
#undef fp_dbg_hex_dump_gbytes
if (g_once_init_enter (&debug_transfer_enabled))
{
gsize enabled = g_getenv ("FP_DEBUG_TRANSFER") != NULL ? TRUE : G_MAXSIZE;
g_once_init_leave (&debug_transfer_enabled, enabled);
}
return debug_transfer_enabled == TRUE;
}
/**
* fpi_dbg_hex_dump_data:
* @buf: Bytes buffer to dump
* @len: Length of @buf to dump
*
* Prints hex dump of @buf to fp_dbg()
*/
void
(fpi_dbg_hex_dump_data) (const gchar *log_domain,
const guint8 *buf,
gsize len)
fp_dbg_hex_dump_bytes (const gchar *log_domain,
const guint8 *buf,
gsize len)
{
g_autoptr(GString) line = NULL;
if (g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, log_domain))
return;
if (G_UNLIKELY (len == 0 || !buf))
return;
line = g_string_new ("");
for (gint i = 0; i < len; i++)
g_string_append_printf (line, "%02x", buf[i]);
{
g_string_append_printf (line, "%02x ", buf[i]);
if ((i + 1) % 16 == 0)
{
g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", line->str);
g_string_set_size (line, 0);
}
}
if (line->len)
g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", line->str);
}
/**
* fpi_dbg_hex_dump_bytes:
* @bytes: #GBytes to dump
*
* Prints hex dump of @bytes to fp_dbg()
*/
void
(fpi_dbg_hex_dump_bytes) (const gchar *log_domain,
GBytes *bytes)
fp_dbg_hex_dump_gbytes (const gchar *log_domain,
GBytes *gbytes)
{
gsize length = 0;
const guint8 *data = g_bytes_get_data (bytes, &length);
gsize len = 0;
const guint8 *buf = g_bytes_get_data (gbytes, &len);
(fpi_dbg_hex_dump_data) (log_domain, data, length);
fp_dbg_hex_dump_bytes (log_domain, buf, len);
}
+16 -17
View File
@@ -1,7 +1,6 @@
/*
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
* Copyright (C) 2026 Marco Trevisan (Treviño) <mail@3v1n0.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -98,31 +97,31 @@
*/
#define BUG() BUG_ON (1)
void (fpi_dbg_hex_dump_data) (const gchar *log_domain,
const guint8 *buf,
gsize len);
/*
* Custom-defined logging functions are wrapped in macros for convenience so
* that the caller does not have to pass G_LOG_DOMAIN every time.
*/
void fp_dbg_hex_dump_bytes (const gchar *log_domain,
const guint8 *buf,
gsize len);
/**
* fp_dbg_hex_dump_data:
* fp_dbg_hex_dump_bytes:
* @buf: Bytes buffer to dump
* @len: Length of @buf to dump
*
* Prints hex dump of @buf to fp_dbg()
*/
#define fp_dbg_hex_dump_data(buf, len) \
(fpi_dbg_hex_dump_data) (G_LOG_DOMAIN, (buf), (len))
#define fp_dbg_hex_dump_bytes(buf, len) fp_dbg_hex_dump_bytes (G_LOG_DOMAIN, buf, len)
void (fpi_dbg_hex_dump_bytes) (const gchar *log_domain,
GBytes *bytes);
void fp_dbg_hex_dump_gbytes (const gchar *log_domain,
GBytes *gbytes);
/**
* fp_dbg_hex_dump_bytes:
* @bytes: #GBytes to dump
* fp_dbg_hex_dump_gbytes:
* @gbytes: #GBytes to dump
*
* Prints hex dump of @bytes to fp_dbg()
* Prints hex dump of @gbytes to fp_dbg()
*/
#define fp_dbg_hex_dump_bytes(bytes) \
(fpi_dbg_hex_dump_bytes) \
(G_LOG_DOMAIN, (bytes))
gboolean fpi_log_is_debug_transfer_enabled (void);
#define fp_dbg_hex_dump_gbytes(gbytes) fp_dbg_hex_dump_gbytes (G_LOG_DOMAIN, gbytes)
+6 -25
View File
@@ -79,22 +79,6 @@ fpi_print_set_type (FpPrint *print,
g_object_notify (G_OBJECT (print), "fpi-type");
}
/**
* fpi_print_get_type:
* @print: A #FpPrint
*
* Retrieves the type of the print data.
*
* Returns: The #FpiPrintType of the print.
*/
FpiPrintType
fpi_print_get_type (FpPrint *print)
{
g_return_val_if_fail (FP_IS_PRINT (print), FPI_PRINT_UNDEFINED);
return print->type;
}
/**
* fpi_print_set_device_stored:
* @print: A #FpPrint
@@ -212,7 +196,7 @@ fpi_print_add_from_image (FpPrint *print,
/**
* fpi_print_bz3_match:
* @print_template: A #FpPrint containing one or more prints
* @template: A #FpPrint containing one or more prints
* @print: A newly scanned #FpPrint to test
* @bz3_threshold: The BZ3 match threshold
* @error: Return location for error
@@ -226,17 +210,14 @@ fpi_print_add_from_image (FpPrint *print,
* Returns: Whether the prints match, @error will be set if #FPI_MATCH_ERROR is returned
*/
FpiMatchResult
fpi_print_bz3_match (FpPrint *print_template,
FpPrint *print,
gint bz3_threshold,
GError **error)
fpi_print_bz3_match (FpPrint *template, FpPrint *print, gint bz3_threshold, GError **error)
{
struct xyt_struct *pstruct;
gint probe_len;
gint i;
/* XXX: Use a different error type? */
if (print_template->type != FPI_PRINT_NBIS || print->type != FPI_PRINT_NBIS)
if (template->type != FPI_PRINT_NBIS || print->type != FPI_PRINT_NBIS)
{
*error = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED,
"It is only possible to match NBIS type print data");
@@ -253,11 +234,11 @@ fpi_print_bz3_match (FpPrint *print_template,
pstruct = g_ptr_array_index (print->prints, 0);
probe_len = bozorth_probe_init (pstruct);
for (i = 0; i < print_template->prints->len; i++)
for (i = 0; i < template->prints->len; i++)
{
struct xyt_struct *gstruct;
gint score;
gstruct = g_ptr_array_index (print_template->prints, i);
gstruct = g_ptr_array_index (template->prints, i);
score = bozorth_to_gallery (probe_len, pstruct, gstruct);
fp_dbg ("score %d/%d", score, bz3_threshold);
@@ -311,7 +292,7 @@ fpi_print_generate_user_id (FpPrint *print)
if (!username)
username = "nobody";
if (fpi_device_emulation_mode_enabled (NULL))
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
rand_id = 0;
else
rand_id = g_random_int ();
+3 -4
View File
@@ -11,11 +11,13 @@ G_BEGIN_DECLS
* @FPI_PRINT_UNDEFINED: Undefined type, this happens prior to enrollment
* @FPI_PRINT_RAW: A raw print where the data is directly compared
* @FPI_PRINT_NBIS: NBIS minutiae comparison
* @FPI_PRINT_SDCP: Print from an SDCP conforming device
*/
typedef enum {
FPI_PRINT_UNDEFINED = 0,
FPI_PRINT_RAW,
FPI_PRINT_NBIS,
FPI_PRINT_SDCP,
} FpiPrintType;
/**
@@ -35,9 +37,6 @@ void fpi_print_add_print (FpPrint *print,
void fpi_print_set_type (FpPrint *print,
FpiPrintType type);
FpiPrintType fpi_print_get_type (FpPrint *print);
void fpi_print_set_device_stored (FpPrint *print,
gboolean device_stored);
@@ -45,7 +44,7 @@ gboolean fpi_print_add_from_image (FpPrint *print,
FpImage *image,
GError **error);
FpiMatchResult fpi_print_bz3_match (FpPrint *print_template,
FpiMatchResult fpi_print_bz3_match (FpPrint *temp,
FpPrint *print,
gint bz3_threshold,
GError **error);
+980
View File
@@ -0,0 +1,980 @@
/*
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "sdcp_device"
#include "fpi-log.h"
#include "fpi-compat.h"
#include "fpi-print.h"
#include "fp-sdcp-device-private.h"
#include "fpi-sdcp.h"
#include "fpi-sdcp-device.h"
/**
* SECTION: fpi-sdcp-device
* @title: Internal FpSdcpDevice
* @short_description: Internal SDCP device routines
*
* Internal SDCP handling routines. See #FpSdcpDevice for public routines.
*/
G_DEFINE_BOXED_TYPE (FpiSdcpClaim, fpi_sdcp_claim, fpi_sdcp_claim_copy, fpi_sdcp_claim_free)
/**
* fpi_sdcp_claim_new:
*
* Create an empty #FpiSdcpClaim to provide to the base class.
*
* Returns: (transfer full): A newly created #FpiSdcpClaim
*/
FpiSdcpClaim *
fpi_sdcp_claim_new (void)
{
FpiSdcpClaim *res = NULL;
res = g_new0 (FpiSdcpClaim, 1);
return res;
}
/**
* fpi_sdcp_claim_free:
* @claim: a #FpiSdcpClaim
*
* Release the memory used by an #FpiSdcpClaim.
*/
void
fpi_sdcp_claim_free (FpiSdcpClaim *claim)
{
g_return_if_fail (claim);
g_clear_pointer (&claim->model_certificate, g_bytes_unref);
g_clear_pointer (&claim->device_public_key, g_bytes_unref);
g_clear_pointer (&claim->firmware_public_key, g_bytes_unref);
g_clear_pointer (&claim->firmware_hash, g_bytes_unref);
g_clear_pointer (&claim->model_signature, g_bytes_unref);
g_clear_pointer (&claim->device_signature, g_bytes_unref);
g_free (claim);
}
/**
* fpi_sdcp_claim_copy:
* @other: The #FpiSdcpClaim to copy
*
* Create a (shallow) copy of a #FpiSdcpClaim.
*
* Returns: (transfer full): A newly created #FpiSdcpClaim
*/
FpiSdcpClaim *
fpi_sdcp_claim_copy (FpiSdcpClaim *other)
{
FpiSdcpClaim *res = NULL;
res = fpi_sdcp_claim_new ();
if (other->model_certificate)
res->model_certificate = g_bytes_ref (other->model_certificate);
if (other->device_public_key)
res->device_public_key = g_bytes_ref (other->device_public_key);
if (other->firmware_public_key)
res->firmware_public_key = g_bytes_ref (other->firmware_public_key);
if (other->firmware_hash)
res->firmware_hash = g_bytes_ref (other->firmware_hash);
if (other->model_signature)
res->model_signature = g_bytes_ref (other->model_signature);
if (other->device_signature)
res->device_signature = g_bytes_ref (other->device_signature);
return res;
}
/* Manually redefine what G_DEFINE_* macro does */
static inline gpointer
fp_sdcp_device_get_instance_private (FpSdcpDevice *self)
{
FpSdcpDeviceClass *sdcp_class = g_type_class_peek_static (FP_TYPE_SDCP_DEVICE);
return G_STRUCT_MEMBER_P (self,
g_type_class_get_instance_private_offset (sdcp_class));
}
/* Example values from Microsoft's SDCP documentation to use when testing (FP_DEVICE_EMULATION=1) */
static const guchar test_host_private_key[] = {
0x84, 0x00, 0xed, 0x14, 0x57, 0x9c, 0xdf, 0x11, 0x58, 0x64, 0x77, 0xe8, 0x36, 0xe8, 0xcb, 0x52,
0x70, 0x84, 0x41, 0xc1, 0xc2, 0xa4, 0x47, 0xc2, 0x18, 0xc5, 0xbb, 0xc2, 0xd1, 0x18, 0xfb, 0xc7
};
static const guchar test_host_public_key[] = {
0x04, 0x52, 0xf0, 0x56, 0xff, 0xb9, 0xc6, 0x72, 0x86, 0x54, 0x77, 0x1a, 0x36, 0x29, 0xb7, 0x70,
0x76, 0x7b, 0x19, 0xa2, 0x10, 0x6a, 0x49, 0x16, 0xfb, 0x81, 0xba, 0x06, 0xef, 0x67, 0x97, 0xc4,
0xa3, 0xdf, 0x67, 0x2a, 0xde, 0x0e, 0x91, 0x16, 0xd1, 0xab, 0xe2, 0x78, 0xa8, 0x22, 0x3a, 0xbd,
0xe4, 0x95, 0x8d, 0x62, 0xd4, 0xff, 0x68, 0x82, 0x15, 0x9f, 0x06, 0x17, 0xc6, 0xf8, 0xce, 0x10,
0xbf
};
static const gchar test_host_random[] = {
0xd8, 0x77, 0x40, 0x3a, 0xbe, 0x82, 0xf4, 0xd9, 0x7e, 0x14, 0x48, 0xc5, 0x05, 0x2d, 0x83, 0xa5,
0x32, 0xa4, 0x5e, 0x56, 0xef, 0x04, 0x9c, 0xbb, 0xf9, 0x81, 0x13, 0x75, 0x20, 0xe7, 0x13, 0xbf
};
static const gchar test_reconnect_random[] = {
0x8a, 0x74, 0x51, 0xc1, 0xd3, 0xa8, 0xdc, 0xa1, 0xc1, 0x33, 0x0c, 0xa5, 0x0d, 0x73, 0x45, 0x4b,
0x35, 0x1a, 0x49, 0xf4, 0x6c, 0x8e, 0x9d, 0xce, 0xe1, 0x5c, 0x96, 0x4d, 0x29, 0x5c, 0x31, 0xc9
};
static const gchar test_identify_nonce[] = {
0x3a, 0x1b, 0x50, 0x6f, 0x5b, 0xec, 0x08, 0x90, 0x59, 0xac, 0xef, 0xb9, 0xb4, 0x4d, 0xfb, 0xde,
0xa7, 0xa5, 0x99, 0xee, 0x9a, 0xa2, 0x67, 0xe5, 0x25, 0x26, 0x64, 0xd6, 0x0b, 0x79, 0x80, 0x53
};
/* FpiSdcpDevice */
/* Internal functions of FpSdcpDevice */
void
fpi_sdcp_device_get_application_secret (FpSdcpDevice *self,
GBytes **application_secret)
{
GBytes *data = NULL;
g_return_if_fail (*application_secret == NULL);
g_object_get (G_OBJECT (self), "sdcp-data", &data, NULL);
if (!data)
return;
*application_secret = g_steal_pointer (&data);
}
void
fpi_sdcp_device_set_application_secret (FpSdcpDevice *self,
GBytes *application_secret)
{
g_return_if_fail (application_secret);
g_object_set (G_OBJECT (self), "sdcp-data", application_secret, NULL);
}
void
fpi_sdcp_device_open (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_OPEN);
cls->open (self);
}
void
fpi_sdcp_device_connect (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
GError *error = NULL;
g_clear_pointer (&priv->host_private_key, g_bytes_unref);
g_clear_pointer (&priv->host_public_key, g_bytes_unref);
g_clear_pointer (&priv->host_random, g_bytes_unref);
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") != 0)
{
/* SDCP Connect: 3.i. Generate host ephemeral ECDH key pair */
fpi_sdcp_generate_host_key (&priv->host_private_key, &priv->host_public_key, &error);
if (error)
{
fpi_sdcp_device_connect_complete (self,
NULL, NULL, NULL,
error);
return;
}
/* SDCP Connect: 3.ii. Generate host random */
priv->host_random = fpi_sdcp_generate_random (&error);
if (error)
{
fpi_sdcp_device_connect_complete (self,
NULL, NULL, NULL,
error);
return;
}
}
else
{
/* Use Microsoft's SDCP documentation example values in emulation mode */
priv->host_private_key = g_bytes_new (test_host_private_key, sizeof (test_host_private_key));
priv->host_public_key = g_bytes_new (test_host_public_key, sizeof (test_host_public_key));
priv->host_random = g_bytes_new (test_host_random, sizeof (test_host_random));
}
/* SDCP Connect: 3.iii. Send the Connect message */
cls->connect (self);
return;
}
void
fpi_sdcp_device_reconnect (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
GError *error = NULL;
g_clear_pointer (&priv->reconnect_random, g_bytes_unref);
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") != 0)
{
/* SDCP Reconnect: 2.i. Generate host random */
priv->reconnect_random = fpi_sdcp_generate_random (&error);
if (error)
{
fpi_sdcp_device_reconnect_complete (self, NULL, error);
return;
}
}
else
{
/* Use Microsoft's SDCP documentation example value in emulation mode */
priv->reconnect_random = g_bytes_new (test_reconnect_random, sizeof (test_reconnect_random));
}
/* SDCP Reconnect: 2.ii. Send the Reconnect message */
if (cls->reconnect)
cls->reconnect (self);
else
fpi_sdcp_device_connect (self);
}
void
fpi_sdcp_device_list (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_LIST);
cls->list (self);
}
void
fpi_sdcp_device_enroll (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
FpPrint *print;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_ENROLL);
fpi_sdcp_device_get_application_secret (self, &application_secret);
g_return_if_fail (application_secret != NULL);
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
fpi_print_set_device_stored (print, FALSE);
g_object_set (print, "fpi-data", NULL, NULL);
cls->enroll (self);
}
void
fpi_sdcp_device_identify (FpSdcpDevice *self)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
FpiDeviceAction action;
GError *error = NULL;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_IDENTIFY || action == FPI_DEVICE_ACTION_VERIFY);
fpi_sdcp_device_get_application_secret (self, &application_secret);
g_return_if_fail (application_secret != NULL);
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") != 0)
{
/* Generate a new nonce. */
priv->identify_nonce = fpi_sdcp_generate_random (&error);
if (error)
{
fpi_device_action_error (FP_DEVICE (self), error);
return;
}
}
else
{
/* Use Microsoft's SDCP documentation example value in emulation mode */
priv->identify_nonce = g_bytes_new (test_identify_nonce, sizeof (test_identify_nonce));
}
cls->identify (self);
}
/*********************************************************/
/* Private API */
/**
* fpi_sdcp_device_open_complete:
* @self: a #FpSdcpDevice fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of open operation. Responsible for triggering SDCP connect
* or reconnect as necessary.
*/
void
fpi_sdcp_device_open_complete (FpSdcpDevice *self,
GError *error)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
if (!error)
{
fpi_sdcp_device_get_application_secret (self, &application_secret);
/* Try a reconnect if implemented and we already have an application_secret */
if (cls->reconnect && application_secret)
fpi_sdcp_device_reconnect (self);
/* Connect if we don't already have an application_secret */
else if (!application_secret)
fpi_sdcp_device_connect (self);
/* Complete open if we are already connected */
else
fpi_device_open_complete (FP_DEVICE (self), NULL);
}
else
{
fpi_device_open_complete (FP_DEVICE (self), error);
}
}
/**
* fp_sdcp_device_get_connect_data:
* @self: a #FpSdcpDevice fingerprint device
* @host_random: (out) (transfer full): The host-generated random
* @host_public_key: (out) (transfer full): The host public key
*
* Get data required to connect to (i.e. open) the device securely.
*/
void
fpi_sdcp_device_get_connect_data (FpSdcpDevice *self,
GBytes **host_random,
GBytes **host_public_key)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_return_if_fail (host_random != NULL);
g_return_if_fail (host_public_key != NULL);
g_return_if_fail (priv->host_random);
g_return_if_fail (priv->host_public_key);
*host_random = g_bytes_new_from_bytes (priv->host_random, 0,
g_bytes_get_size (priv->host_random));
*host_public_key = g_bytes_new_from_bytes (priv->host_public_key, 0,
g_bytes_get_size (priv->host_public_key));
}
/**
* fp_sdcp_device_get_reconnect_data:
* @self: a #FpSdcpDevice fingerprint device
* @reconnect_random: (out) (transfer full): The host-generated random
*
* Get data required to reconnect to (i.e. open) to the device securely.
*/
void
fpi_sdcp_device_get_reconnect_data (FpSdcpDevice *self,
GBytes **reconnect_random)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_return_if_fail (reconnect_random != NULL);
g_return_if_fail (priv->reconnect_random);
*reconnect_random = g_bytes_new_from_bytes (priv->reconnect_random, 0,
g_bytes_get_size (priv->reconnect_random));
}
/**
* fp_sdcp_device_get_identify_data:
* @self: a #FpSdcpDevice fingerprint device
* @nonce: (out) (transfer full): A new host-generated nonce
*
* Get data required to identify a new print.
*/
void
fpi_sdcp_device_get_identify_data (FpSdcpDevice *self,
GBytes **nonce)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_return_if_fail (nonce != NULL);
g_return_if_fail (priv->identify_nonce);
*nonce = g_bytes_new_from_bytes (priv->identify_nonce, 0,
g_bytes_get_size (priv->identify_nonce));
}
/**
* fp_sdcp_device_set_identify_data:
* @self: a #FpSdcpDevice fingerprint device
* @nonce: A driver-specified nonce
*
* Sets data required to identify a new print.
*
* Most drivers should not use this function, but instead use the automatically
* generated values retrieved from fpi_sdcp_device_get_identify_data() when
* executing the device-specific Identify command.
*
* In cases where a device's Identify command does not accept a randomly
* generated nonce, this function can be used to override the randomly generated
* nonce to the nonce that was actually sent to the device.
*/
void
fpi_sdcp_device_set_identify_data (FpSdcpDevice *self,
GBytes *nonce)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_return_if_fail (nonce != NULL);
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
priv->identify_nonce = g_steal_pointer (&nonce);
}
/**
* fpi_sdcp_device_connect_complete:
* @self: a #FpSdcpDevice fingerprint device
* @device_random: The device random
* @claim: The device #FpiSdcpClaim
* @mac: The MAC authenticating @claim
* @error: A #GError or %NULL on success
*
* Reports completion of connect operation. Responsible for performing SDCP key
* agreement, deriving secrets necessary for processing all other SDCP-related
* payloads, and verifying the device connection is trusted.
*/
void
fpi_sdcp_device_connect_complete (FpSdcpDevice *self,
GBytes *device_random,
FpiSdcpClaim *claim,
GBytes *mac,
GError *error)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_OPEN);
g_return_if_fail (priv->host_private_key);
g_return_if_fail (priv->host_random);
if (error)
{
if (device_random || claim || mac)
{
g_clear_pointer (&device_random, g_bytes_unref);
g_clear_pointer (&claim, fpi_sdcp_claim_free);
g_clear_pointer (&mac, g_bytes_unref);
fp_warn ("Driver provided SDCP Connect information but also reported error.");
}
fpi_device_open_complete (FP_DEVICE (self), error);
return;
}
if (!device_random || !claim || !mac ||
(!claim->model_certificate || !claim->device_public_key || !claim->firmware_public_key ||
!claim->firmware_hash || !claim->model_signature || !claim->device_signature))
{
fp_dbg ("Driver did not provide all required information to callback; returning error instead.");
g_clear_pointer (&device_random, g_bytes_unref);
g_clear_pointer (&claim, fpi_sdcp_claim_free);
g_clear_pointer (&mac, g_bytes_unref);
fpi_device_open_complete (FP_DEVICE (self),
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"Driver called connect complete with "
"incomplete arguments"));
return;
}
/* Verify connect and store the application_secret */
if (!fpi_sdcp_verify_connect (priv->host_private_key,
priv->host_random,
device_random,
claim,
mac,
!cls->ignore_device_certificate,
!cls->ignore_device_signatures,
&application_secret,
&error))
{
fpi_device_open_complete (FP_DEVICE (self),
fpi_device_error_new_msg (FP_DEVICE_ERROR_UNTRUSTED,
"SDCP Connect verification failed: %s",
error->message));
return;
}
fpi_sdcp_device_set_application_secret (self, application_secret);
/* Clear no longer needed private data */
g_clear_pointer (&priv->host_private_key, g_bytes_unref);
g_clear_pointer (&priv->host_public_key, g_bytes_unref);
g_clear_pointer (&priv->host_random, g_bytes_unref);
fpi_device_open_complete (FP_DEVICE (self), NULL);
}
/**
* fpi_sdcp_device_reconnect_complete:
* @self: a #FpSdcpDevice fingerprint device
* @mac: The MAC authenticating @claim
* @error: A #GError or %NULL on success
*
* Reports completion of a reconnect (i.e. open) operation.
*/
void
fpi_sdcp_device_reconnect_complete (FpSdcpDevice *self,
GBytes *mac,
GError *error)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_autoptr(GBytes) application_secret = NULL;
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_OPEN);
g_return_if_fail (priv->reconnect_random);
if (error)
{
if (mac)
{
fp_warn ("Driver provided a reconnect MAC but also reported an error.");
g_clear_pointer (&mac, g_bytes_unref);
}
/* Silently try a normal connect instead. */
fpi_sdcp_device_connect (self);
}
else if (mac)
{
fpi_sdcp_device_get_application_secret (self, &application_secret);
if (fpi_sdcp_verify_reconnect (application_secret, priv->reconnect_random, mac, &error))
{
fp_dbg ("SDCP Reconnect succeeded");
fpi_device_open_complete (FP_DEVICE (self), NULL);
}
else
{
fp_dbg ("SDCP Reconnect failed; doing a full connect.");
fpi_sdcp_device_connect (self);
}
}
else
{
fpi_device_open_complete (FP_DEVICE (self),
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"Driver called reconnect complete with wrong arguments"));
}
/* Clear no longer needed private data */
g_clear_pointer (&priv->reconnect_random, g_bytes_unref);
}
/**
* fpi_sdcp_device_list_complete:
* @self: a #FpSdcpDevice fingerprint device
* @ids: A #GPtrArray of #GBytes of each SDCP enrollment ID stored on the device
* @error: A #GError or %NULL on success
*
* Convenience function to create the minimally required #FpPrint list for
* #FpSdcpDevice prints using the provided @ids, then uses that #FpPrint list to
* report completion of the list operation.
*
* If the device provides additional attributes that should be stored on each
* #FpPrint as part of the list operation, a #GPtrArray of #FpPrint can instead
* be created with the additional attributes and fpi_device_list_complete() can
* be used instead of this function.
*
* Please note that the @ids array will be freed using g_ptr_array_unref() and
* the elements are destroyed automatically. As such, you must use
* g_ptr_array_new_with_free_func() with `(GDestroyNotify) g_bytes_unref` as the
* free func when creating the #GPtrArray.
*/
void
fpi_sdcp_device_list_complete (FpSdcpDevice *self,
GPtrArray *ids,
GError *error)
{
g_autoptr(GPtrArray) prints = NULL;
gint prints_len = 0;
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_LIST);
if (error)
{
fpi_device_list_complete (FP_DEVICE (self), NULL, error);
return;
}
prints = g_ptr_array_new_with_free_func (g_object_unref);
/* Allow an empty array (prints_len=0) but if ids has been passed, use it */
if (ids)
prints_len = ids->len;
for (gint i = 0; i < prints_len; i++)
{
FpPrint *print = fp_print_new (FP_DEVICE (self));
fpi_print_set_type (print, FPI_PRINT_SDCP);
fpi_print_set_device_stored (print, FALSE);
fpi_sdcp_device_set_print_id (print, g_ptr_array_index (ids, i));
g_ptr_array_add (prints, g_object_ref_sink (print));
}
fpi_device_list_complete (FP_DEVICE (self), g_steal_pointer (&prints), NULL);
g_clear_pointer (&ids, g_ptr_array_unref);
}
/**
* fpi_sdcp_device_enroll_commit:
* @self: a #FpSdcpDevice fingerprint device
* @nonce: The device generated nonce
* @error: a #GError or %NULL on success
*
* Called when the print is ready to be committed to device memory.
* During enrollment, fpi_device_enroll_progress() must be called for each
* successful stage before the print can be committed.
* The @nonce generated by the device-specific EnrollmentNonce response must be
* provided in order for the enrollment ID to be generated.
* The driver's enroll_commit() vfunc will be triggered upon successfully
* generating the enrollment ID.
*/
void
fpi_sdcp_device_enroll_commit (FpSdcpDevice *self,
GBytes *nonce,
GError *error)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
GBytes *id = NULL;
FpPrint *print;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_ENROLL);
g_return_if_fail (nonce != NULL);
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
fpi_sdcp_device_get_application_secret (self, &application_secret);
id = fpi_sdcp_generate_enrollment_id (application_secret, nonce, &error);
if (!id || error)
{
fp_warn ("Could not generate SDCP enrollment ID");
fpi_device_enroll_complete (FP_DEVICE (self), NULL, error);
g_object_set (print, "fpi-data", NULL, NULL);
return;
}
/* Set to true once committed */
fpi_print_set_device_stored (print, FALSE);
/* Attach the ID to the print */
fpi_sdcp_device_set_print_id (print, id);
cls->enroll_commit (self, id);
g_clear_pointer (&id, g_bytes_unref);
}
/**
* fpi_sdcp_device_enroll_commit_complete:
* @self: a #FpSdcpDevice fingerprint device
* @error: a #GError or %NULL on success
*
* Called when device has committed the given print to memory.
* This finalizes the enroll operation.
*/
void
fpi_sdcp_device_enroll_commit_complete (FpSdcpDevice *self,
GError *error)
{
g_autoptr(GBytes) id = NULL;
FpPrint *print;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_ENROLL);
if (error)
{
fpi_device_enroll_complete (FP_DEVICE (self), NULL, error);
return;
}
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
fpi_sdcp_device_get_print_id (print, &id);
if (!id)
{
g_error ("Inconsistent state; the print must have the enrolled ID attached at this point");
return;
}
fpi_print_set_type (print, FPI_PRINT_SDCP);
fpi_print_set_device_stored (print, TRUE);
fpi_device_enroll_complete (FP_DEVICE (self), g_object_ref (print), NULL);
}
/**
* fpi_sdcp_device_identify_retry:
* @self: a #FpSdcpDevice fingerprint device
* @error: a #GError containing the retry condition
*
* Called when the device requires the finger to be presented again.
* This should not be called for a verified no-match, it should only
* be called if e.g. the finger was not centered properly or similar.
*
* Effectively this simply raises the error up. This function exists
* to bridge the difference in semantics that SDPC has from how
* libfprint works internally.
*/
void
fpi_sdcp_device_identify_retry (FpSdcpDevice *self,
GError *error)
{
FpiDeviceAction action;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_IDENTIFY || action == FPI_DEVICE_ACTION_VERIFY);
if (action == FPI_DEVICE_ACTION_VERIFY)
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_ERROR, NULL, error);
else if (action == FPI_DEVICE_ACTION_IDENTIFY)
fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, error);
}
/**
* fpi_sdcp_device_identify_complete:
* @self: a #FpSdcpDevice fingerprint device
* @id: (transfer none): the ID as reported by the device
* @mac: (transfer none): MAC authenticating the message
* @error: (transfer full): #GError if an error occured
*
* Called when device is done with the identification routine. The
* returned ID may be %NULL if none of the in-device templates matched.
*/
void
fpi_sdcp_device_identify_complete (FpSdcpDevice *self,
GBytes *id,
GBytes *mac,
GError *error)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_autoptr(GBytes) application_secret = NULL;
FpPrint *identified_print;
FpiDeviceAction action;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_IDENTIFY || action == FPI_DEVICE_ACTION_VERIFY);
g_return_if_fail (priv->identify_nonce);
if (error)
{
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
fpi_device_action_error (FP_DEVICE (self), error);
return;
}
/* No error and no valid id/mac provided means that there was no match from the device */
if (!id || !mac || g_bytes_get_size (id) != SDCP_ENROLLMENT_ID_SIZE ||
g_bytes_get_size (mac) != SDCP_MAC_SIZE)
{
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
if (action == FPI_DEVICE_ACTION_VERIFY)
{
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_FAIL, NULL, NULL);
fpi_device_verify_complete (FP_DEVICE (self), NULL);
}
else
{
fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, NULL);
fpi_device_identify_complete (FP_DEVICE (self), NULL);
}
return;
}
fpi_sdcp_device_get_application_secret (self, &application_secret);
if (!fpi_sdcp_verify_identify (application_secret, priv->identify_nonce, id, mac, &error))
{
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
fpi_device_action_error (FP_DEVICE (self), error);
return;
}
/* Clear no longer needed private data */
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
/* Create a new print */
identified_print = fp_print_new (FP_DEVICE (self));
fpi_print_set_type (identified_print, FPI_PRINT_SDCP);
/* Set to true once committed */
fpi_print_set_device_stored (identified_print, FALSE);
/* Attach the ID to the print */
fpi_sdcp_device_set_print_id (identified_print, id);
/* The surrounding API expects a match/no-match against a given set. */
if (action == FPI_DEVICE_ACTION_VERIFY)
{
FpPrint *print;
fpi_device_get_verify_data (FP_DEVICE (self), &print);
if (fp_print_equal (print, identified_print))
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_SUCCESS, identified_print, NULL);
else
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_FAIL, identified_print, NULL);
fpi_device_verify_complete (FP_DEVICE (self), NULL);
}
else
{
GPtrArray *prints;
gint i;
fpi_device_get_identify_data (FP_DEVICE (self), &prints);
for (i = 0; i < prints->len; i++)
{
FpPrint *print = g_ptr_array_index (prints, i);
if (fp_print_equal (print, identified_print))
{
fpi_device_identify_report (FP_DEVICE (self), print, identified_print, NULL);
fpi_device_identify_complete (FP_DEVICE (self), NULL);
return;
}
}
/* Print wasn't in database. */
fpi_device_identify_report (FP_DEVICE (self), NULL, identified_print, NULL);
fpi_device_identify_complete (FP_DEVICE (self), NULL);
}
}
/**
* fpi_sdcp_device_get_print_id:
* @print: an SDCP device #FpPrint
* @id: (out) (transfer full): the ID gotten from the @print data
*
* Gets the SDCP enrollment ID from the @print data.
*
* The returned @id may be %NULL if the data was not set or in the wrong format.
*/
void
fpi_sdcp_device_get_print_id (FpPrint *print,
GBytes **id)
{
g_autoptr(GVariant) id_var = NULL;
g_autoptr(GVariant) data = NULL;
const guint8 *id_data;
gsize id_len;
g_return_if_fail (print);
g_return_if_fail (*id == NULL);
g_object_get (G_OBJECT (print), "fpi-data", &data, NULL);
if (!data)
{
fp_warn ("SDCP print data has not been set.");
return;
}
if (!g_variant_check_format_string (data, "(@ay)", FALSE))
{
fp_warn ("SDCP print data is not in expected format.");
return;
}
g_variant_get (data, "(@ay)", &id_var);
id_data = g_variant_get_fixed_array (id_var, &id_len, sizeof (guint8));
*id = g_bytes_new (id_data, id_len);
}
/**
* fpi_sdcp_device_set_print_id:
* @print: an SDCP device #FpPrint
* @id: the ID to set in the @print data
*
* Sets the SDCP enrollment ID in the @print data.
*/
void
fpi_sdcp_device_set_print_id (FpPrint *print,
GBytes *id)
{
GVariant *id_var;
GVariant *data;
g_return_if_fail (print);
g_return_if_fail (id);
id_var = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
g_bytes_get_data (id, NULL),
g_bytes_get_size (id),
1);
data = g_variant_new ("(@ay)", id_var);
g_object_set (G_OBJECT (print), "fpi-data", data, NULL);
}
+185
View File
@@ -0,0 +1,185 @@
/*
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "fpi-device.h"
#include "fp-sdcp-device.h"
#define SDCP_PUBLIC_KEY_SIZE 65
#define SDCP_APPLICATION_SECRET_SIZE 32
#define SDCP_RANDOM_SIZE 32
#define SDCP_MAC_SIZE 32
#define SDCP_NONCE_SIZE 32
#define SDCP_ENROLLMENT_ID_SIZE 32
#define SDCP_SIGNATURE_SIZE 64
/**
* FpiSdcpClaim:
* @model_certificate: Microsoft-issued per-model certificate encoded in x509
* ASN.1 DER format (`cert_m`)
* @device_public_key: The per-device ECDSA public key (`pk_d`)
* @firmware_public_key: The ephemeral public key generated by the device
* firmware (`pk_f`)
* @firmware_hash: Hash of the firmware and firmware public key (`h_f`)
* @model_signature: Device public key signed by the model key (`s_m`)
* @device_signature: Firmware hash and public key signed by the device private
* key (`s_d`)
*
* Structure to hold the claim as produced by the device during a secure
* connect. See the SDCP specification for more details.
*
* Note all of these may simply be memory views into a larger #GBytes created
* using g_bytes_new_from_bytes().
*/
struct _FpiSdcpClaim
{
/*< public >*/
GBytes *model_certificate; /* cert_m */
GBytes *device_public_key; /* pk_d */
GBytes *firmware_public_key; /* pk_f */
GBytes *firmware_hash; /* h_f */
GBytes *model_signature; /* s_m */
GBytes *device_signature; /* s_d */
};
typedef struct _FpiSdcpClaim FpiSdcpClaim;
GType fpi_sdcp_claim_get_type (void) G_GNUC_CONST;
FpiSdcpClaim *fpi_sdcp_claim_new (void);
FpiSdcpClaim *fpi_sdcp_claim_copy (FpiSdcpClaim *other);
void fpi_sdcp_claim_free (FpiSdcpClaim *claim);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpiSdcpClaim, fpi_sdcp_claim_free)
/**
* FpSdcpDeviceClass:
* @ignore_device_certificate: Set to %TRUE to skip validating the device's
* #FpiSdcpClaim.model_certificate against the SDCP truststore.
* @ignore_device_signatures: Set to %TRUE to skip verifying the device's
* #FpiSdcpClaim.model_signature and #FpiSdcpClaim.device_signature.
* @open: Open the device. Similar to #FpDeviceClass.open except that
* completion with fpi_sdcp_device_open_complete() will also take care of
* executing @connect and @reconnect as necessary.
* @connect: Establish SDCP connection.
* @reconnect: Perform a faster reconnect. Drivers do not need to provide this
* function. If reconnect fails, then a normal connect will be tried.
* @list: List prints stored on the device. The driver must create a #GPtrArray
* of #GBytes with each enrollment ID stored on the device and use it to call
* fpi_sdcp_device_list_complete() in order to complete the operation.
* @enroll: Start the enrollment procedure and capture all samples. The driver
* must report enrollment progress using fpi_device_enroll_progress(). It
* should also store available metadata about the print in device memory. The
* driver must call fpi_sdcp_device_enroll_commit() when all enrollment stages
* are complete and the print is ready to be commited to the device.
* @enroll_commit: Commit the newly-enrolled print to the device memory using
* the passed id. id may be %NULL, in which case the driver must abort the
* enrollment process. id is owned by the base class and remains valid
* throughout the operation. On completion, the driver must call
* fpi_sdcp_device_enroll_commit_complete().
* @identify: Start identification process. On completion, the driver must call
* fpi_sdcp_device_identify_complete(). To request the user to retry the
* fpi_sdcp_device_identify_retry() function is used.
*
* These are the main entry points for drivers implementing SDCP.
*
* Drivers *must* eventually call the corresponding function to finish the
* operation.
*
* The following #FpDeviceClass entry points are also compatible and can be set
* on the #FpDeviceClass if supported for a given device:
* - #FpDeviceClass.probe
* - #FpDeviceClass.close
* - #FpDeviceClass.delete
* - #FpDeviceClass.clear_storage
* - #FpDeviceClass.cancel
* - #FpDeviceClass.suspend
* - #FpDeviceClass.resume
*
* XXX: Is the use of fpi_device_action_error() acceptable?
*
* Drivers *must* also handle cancellation properly for any long running
* operation (i.e. any operation that requires capturing). It is entirely fine
* to ignore cancellation requests for short operations (e.g. open/close).
*
* This API is solely intended for drivers. It is purely internal and neither
* API nor ABI stable.
*/
struct _FpSdcpDeviceClass
{
FpDeviceClass parent_class;
gboolean ignore_device_certificate;
gboolean ignore_device_signatures;
void (*open) (FpSdcpDevice *sdcp_device);
void (*connect) (FpSdcpDevice *sdcp_device);
void (*reconnect) (FpSdcpDevice *sdcp_device);
void (*list) (FpSdcpDevice *sdcp_device);
void (*enroll) (FpSdcpDevice *sdcp_device);
void (*enroll_commit) (FpSdcpDevice *sdcp_device,
GBytes *id);
void (*identify) (FpSdcpDevice *sdcp_device);
};
void fpi_sdcp_device_open_complete (FpSdcpDevice *self,
GError *error);
void fpi_sdcp_device_get_connect_data (FpSdcpDevice *self,
GBytes **host_random,
GBytes **host_public_key);
void fpi_sdcp_device_connect_complete (FpSdcpDevice *self,
GBytes *device_random,
FpiSdcpClaim *claim,
GBytes *mac,
GError *error);
void fpi_sdcp_device_get_reconnect_data (FpSdcpDevice *self,
GBytes **reconnect_random);
void fpi_sdcp_device_reconnect_complete (FpSdcpDevice *self,
GBytes *mac,
GError *error);
void fpi_sdcp_device_list_complete (FpSdcpDevice *self,
GPtrArray *ids,
GError *error);
void fpi_sdcp_device_enroll_commit (FpSdcpDevice *self,
GBytes *nonce,
GError *error);
void fpi_sdcp_device_enroll_commit_complete (FpSdcpDevice *self,
GError *error);
void fpi_sdcp_device_get_identify_data (FpSdcpDevice *self,
GBytes **nonce);
void fpi_sdcp_device_set_identify_data (FpSdcpDevice *self,
GBytes *nonce);
void fpi_sdcp_device_identify_retry (FpSdcpDevice *self,
GError *error);
void fpi_sdcp_device_identify_complete (FpSdcpDevice *self,
GBytes *id,
GBytes *mac,
GError *error);
void fpi_sdcp_device_get_print_id (FpPrint *print,
GBytes **id);
void fpi_sdcp_device_set_print_id (FpPrint *print,
GBytes *id);
+1079
View File
File diff suppressed because it is too large Load Diff
+132
View File
@@ -0,0 +1,132 @@
/*
* Secure Device Connection Protocol (SDCP) support implementation
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "fpi-compat.h"
#include "fpi-sdcp-device.h"
/**
* fpi_sdcp_generate_host_key:
* @private_key: (out) (transfer full): The host private key (sk_h)
* @public_key: (out) (transfer full): The host public key (pk_h)
* @error: (out): #GError in case the out values are %NULL
*
* Function to generate a new ephemeral ECDH key pair for use with SDCP.
**/
void fpi_sdcp_generate_host_key (GBytes **private_key,
GBytes **public_key,
GError **error);
/**
* fpi_sdcp_generate_random:
* @error: (out): #GError in case the return value is %NULL
*
* Returns: A new #GBytes with a secure random of length %SDCP_RANDOM_SIZE
**/
GBytes *fpi_sdcp_generate_random (GError **error);
/**
* fpi_sdcp_verify_connect:
* @host_private_key: Private key generated using fpi_sdcp_generate_host_key() (sk_h)
* @host_random: Random generated using fpi_sdcp_generate_random() (r_h)
* @device_random: The random provided in the device's ConnectResponse (r_d)
* @claim: #FpiSdcpClaim provided in the device's ConnectResponse (c)
* @mac: The MAC provided in the device's ConnectResponse (m)
* @validate_certificate: If the model certificate (cert_m) should be parsed and
* its trust chain validated as issued from Microsoft's well-known issuers
* @verify_signatures: If the model signature (s_m) and device signature (s_d)
* should be validated against the certificate and keys provided in the claim
* @application_secret: (out) (transfer full): A new #GBytes with the derived
* application secret (s) of length %SDCP_APPLICATION_SECRET_SIZE
* @error: (out): #GError in case the return value is %NULL
*
* High level function which internally handles the derivation of all necessary
* SDCP-related keys and secrets from the device's ConnectResponse and derives
* the application secret for use with all other SDCP-related functions.
*
* This function will also perform a validation of the ConnectResponse MAC and
* optionally perform additional verifications based on the provided
* @validate_certificate and @verify_signatures booleans. If any of these these
* validations fail then %NULL will be returned, indicating that the SDCP secure
* connection channel could not be established.
*
* Returns: %TRUE if the @application_secret was successfully derived and the
* ConnectResponse has been successfully verified
**/
gboolean fpi_sdcp_verify_connect (GBytes *host_private_key,
GBytes *host_random,
GBytes *device_random,
FpiSdcpClaim *claim,
GBytes *mac,
gboolean validate_certificate,
gboolean verify_signatures,
GBytes **application_secret,
GError **error);
/**
* fpi_sdcp_verify_reconnect:
* @application_secret: The host's derived application secret (s)
* @random: The host-generated random sent to the device's Reconnect command (r)
* @mac: The MAC provided in the device's ReconnectResponse (m)
* @error: (out): #GError in case the return value is %FALSE
*
* Verifies the SDCP ReconnectResponse.
*
* Returns: %TRUE if the ReconnectResponse is verified successfully
**/
gboolean fpi_sdcp_verify_reconnect (GBytes *application_secret,
GBytes *random,
GBytes *mac,
GError **error);
/**
* fpi_sdcp_verify_identify:
* @application_secret: The host's derived application secret (s)
* @nonce: The host-generated nonce sent to the device's Identify command (n)
* @id: The ID provided in the device's AuthorizedIdentity (id)
* @mac: The MAC provided in the device's AuthorizedIdentity (m)
* @error: (out): #GError in case the return value is %FALSE
*
* Verifies the SDCP ReconnectResponse.
*
* Returns: %TRUE if the ReconnectResponse is verified successfully
**/
gboolean fpi_sdcp_verify_identify (GBytes *application_secret,
GBytes *nonce,
GBytes *id,
GBytes *mac,
GError **error);
/**
* fpi_sdcp_generate_enrollment_id:
* @application_secret: The host's derived application secret (s)
* @nonce: The nonce received from the device in response to the EnrollBegin
* command (n)
* @error: (out): #GError in case the return value is %NULL
*
* Generates a new id for use with the device's EnrollCommit command.
*
* Returns: A new #GBytes with the generated enrollment id of length
* %SDCP_ENROLLMENT_ID_SIZE
**/
GBytes *fpi_sdcp_generate_enrollment_id (GBytes *application_secret,
GBytes *nonce,
GError **error);
+25 -5
View File
@@ -18,7 +18,6 @@
*/
#include "fpi-spi-transfer.h"
#include "fpi-log.h"
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <errno.h>
@@ -42,17 +41,38 @@ static gsize block_size = 0;
* Drivers should always use this API rather than calling read/write/ioctl on
* the spidev device.
*
* Setting %G_MESSAGES_DEBUG and %FP_DEBUG_TRANSFER will result in the message
* Setting G_MESSAGES_DEBUG and FP_DEBUG_TRANSFER will result in the message
* content to be dumped.
*/
G_DEFINE_BOXED_TYPE (FpiSpiTransfer, fpi_spi_transfer, fpi_spi_transfer_ref, fpi_spi_transfer_unref)
static void
dump_buffer (guchar *buf, gssize dump_len)
{
g_autoptr(GString) line = NULL;
line = g_string_new ("");
/* Dump the buffer. */
for (gssize i = 0; i < dump_len; i++)
{
g_string_append_printf (line, "%02x ", buf[i]);
if ((i + 1) % 16 == 0)
{
g_debug ("%s", line->str);
g_string_set_size (line, 0);
}
}
if (line->len)
g_debug ("%s", line->str);
}
static void
log_transfer (FpiSpiTransfer *transfer, gboolean submit, GError *error)
{
if (fpi_log_is_debug_transfer_enabled ())
if (g_getenv ("FP_DEBUG_TRANSFER"))
{
if (submit)
{
@@ -62,7 +82,7 @@ log_transfer (FpiSpiTransfer *transfer, gboolean submit, GError *error)
transfer->length_rd);
if (transfer->buffer_wr)
fp_dbg_hex_dump_data (transfer->buffer_wr, transfer->length_wr);
dump_buffer (transfer->buffer_wr, transfer->length_wr);
}
else
{
@@ -78,7 +98,7 @@ log_transfer (FpiSpiTransfer *transfer, gboolean submit, GError *error)
transfer->length_wr,
transfer->length_rd);
if (transfer->buffer_rd)
fp_dbg_hex_dump_data (transfer->buffer_rd, transfer->length_rd);
dump_buffer (transfer->buffer_rd, transfer->length_rd);
}
}
}
+14 -20
View File
@@ -615,7 +615,7 @@ fpi_ssm_get_cur_state (FpiSsm *machine)
*
* Returns the error code set by fpi_ssm_mark_failed().
*
* Returns: (transfer none): a #GError or %NULL if not set.
* Returns: (transfer none): a error code
*/
GError *
fpi_ssm_get_error (FpiSsm *machine)
@@ -631,7 +631,7 @@ fpi_ssm_get_error (FpiSsm *machine)
*
* Returns the error code set by fpi_ssm_mark_failed().
*
* Returns: (transfer full): a #GError or %NULL if not set.
* Returns: (transfer full): a error code
*/
GError *
fpi_ssm_dup_error (FpiSsm *machine)
@@ -657,8 +657,6 @@ fpi_ssm_dup_error (FpiSsm *machine)
void
fpi_ssm_silence_debug (FpiSsm *machine)
{
g_return_if_fail (machine != NULL);
machine->silence = TRUE;
}
@@ -667,10 +665,10 @@ fpi_ssm_silence_debug (FpiSsm *machine)
* @transfer: a #FpiUsbTransfer
* @device: a #FpDevice
* @unused_data: User data (unused)
* @error: (transfer full): a #GError or %NULL.
* @error: The #GError or %NULL
*
* Can be used in as a #FpiUsbTransfer callback handler to automatically
* advance or fail a state machine on transfer completion.
* advance or fail a statemachine on transfer completion.
*
* Make sure to set the #FpiSsm on the transfer.
*/
@@ -678,11 +676,10 @@ void
fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device,
gpointer unused_data, GError *error)
{
g_return_if_fail (transfer != NULL);
g_return_if_fail (transfer->ssm);
if (error)
fpi_ssm_mark_failed (transfer->ssm, g_steal_pointer (&error));
fpi_ssm_mark_failed (transfer->ssm, error);
else
fpi_ssm_next_state (transfer->ssm);
}
@@ -694,10 +691,10 @@ fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device,
* @weak_ptr: A #gpointer pointer to nullify. You can pass a pointer to any
* #gpointer to nullify when the callback is completed. I.e a
* pointer to the current #FpiUsbTransfer.
* @error: (transfer full): a #GError or %NULL.
* @error: The #GError or %NULL
*
* Can be used in as a #FpiUsbTransfer callback handler to automatically
* advance or fail a state machine on transfer completion.
* advance or fail a statemachine on transfer completion.
* Passing a #gpointer* as @weak_ptr permits to nullify it once we're done
* with the transfer.
*
@@ -708,13 +705,12 @@ fpi_ssm_usb_transfer_with_weak_pointer_cb (FpiUsbTransfer *transfer,
FpDevice *device, gpointer weak_ptr,
GError *error)
{
g_return_if_fail (transfer != NULL);
g_return_if_fail (transfer->ssm);
if (weak_ptr)
g_nullify_pointer ((gpointer *) weak_ptr);
fpi_ssm_usb_transfer_cb (transfer, device, weak_ptr, g_steal_pointer (&error));
fpi_ssm_usb_transfer_cb (transfer, device, weak_ptr, error);
}
/**
@@ -722,10 +718,10 @@ fpi_ssm_usb_transfer_with_weak_pointer_cb (FpiUsbTransfer *transfer,
* @transfer: a #FpiSpiTransfer
* @device: a #FpDevice
* @unused_data: User data (unused)
* @error: (transfer full): a #GError or %NULL.
* @error: The #GError or %NULL
*
* Can be used in as a #FpiSpiTransfer callback handler to automatically
* advance or fail a state machine on transfer completion.
* advance or fail a statemachine on transfer completion.
*
* Make sure to set the #FpiSsm on the transfer.
*/
@@ -733,11 +729,10 @@ void
fpi_ssm_spi_transfer_cb (FpiSpiTransfer *transfer, FpDevice *device,
gpointer unused_data, GError *error)
{
g_return_if_fail (transfer != NULL);
g_return_if_fail (transfer->ssm);
if (error)
fpi_ssm_mark_failed (transfer->ssm, g_steal_pointer (&error));
fpi_ssm_mark_failed (transfer->ssm, error);
else
fpi_ssm_next_state (transfer->ssm);
}
@@ -749,10 +744,10 @@ fpi_ssm_spi_transfer_cb (FpiSpiTransfer *transfer, FpDevice *device,
* @weak_ptr: A #gpointer pointer to nullify. You can pass a pointer to any
* #gpointer to nullify when the callback is completed. I.e a
* pointer to the current #FpiSpiTransfer.
* @error: (transfer full): a #GError or %NULL.
* @error: The #GError or %NULL
*
* Can be used in as a #FpiSpiTransfer callback handler to automatically
* advance or fail a state machine on transfer completion.
* advance or fail a statemachine on transfer completion.
* Passing a #gpointer* as @weak_ptr permits to nullify it once we're done
* with the transfer.
*
@@ -763,11 +758,10 @@ fpi_ssm_spi_transfer_with_weak_pointer_cb (FpiSpiTransfer *transfer,
FpDevice *device, gpointer weak_ptr,
GError *error)
{
g_return_if_fail (transfer != NULL);
g_return_if_fail (transfer->ssm);
if (weak_ptr)
g_nullify_pointer ((gpointer *) weak_ptr);
fpi_ssm_spi_transfer_cb (transfer, device, weak_ptr, g_steal_pointer (&error));
fpi_ssm_spi_transfer_cb (transfer, device, weak_ptr, error);
}
+21 -34
View File
@@ -18,7 +18,6 @@
*/
#include "fpi-usb-transfer.h"
#include "fpi-log.h"
/**
* SECTION:fpi-usb-transfer
@@ -31,9 +30,6 @@
*
* Drivers should use this API only rather than accessing the GUsbDevice
* directly in most cases.
*
* Setting %G_MESSAGES_DEBUG and %FP_DEBUG_TRANSFER will result in the message
* content to be dumped.
*/
@@ -42,10 +38,8 @@ G_DEFINE_BOXED_TYPE (FpiUsbTransfer, fpi_usb_transfer, fpi_usb_transfer_ref, fpi
static void
log_transfer (FpiUsbTransfer *transfer, gboolean submit, GError *error)
{
if (fpi_log_is_debug_transfer_enabled ())
if (g_getenv ("FP_DEBUG_TRANSFER"))
{
gboolean is_incoming = !!(transfer->endpoint & FPI_USB_ENDPOINT_IN);
if (!submit)
{
g_autofree gchar *error_str = NULL;
@@ -69,13 +63,27 @@ log_transfer (FpiUsbTransfer *transfer, gboolean submit, GError *error)
transfer->endpoint);
}
if (submit != is_incoming)
if (!submit == !!(transfer->endpoint & FPI_USB_ENDPOINT_IN))
{
gsize dump_length = is_incoming ? transfer->actual_length : transfer->length;
g_autoptr(GString) line = NULL;
gssize dump_len;
/* Skip hex dump if actual_length is invalid (e.g., -1 on cancelled IN transfers) */
if (dump_length <= transfer->length)
fp_dbg_hex_dump_data (transfer->buffer, dump_length);
dump_len = (transfer->endpoint & FPI_USB_ENDPOINT_IN) ? transfer->actual_length : transfer->length;
line = g_string_new ("");
/* Dump the buffer. */
for (gint i = 0; i < dump_len; i++)
{
g_string_append_printf (line, "%02x ", transfer->buffer[i]);
if ((i + 1) % 16 == 0)
{
g_debug ("%s", line->str);
g_string_set_size (line, 0);
}
}
if (line->len)
g_debug ("%s", line->str);
}
}
}
@@ -336,8 +344,7 @@ transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_dat
{
error = g_error_new (G_USB_DEVICE_ERROR,
G_USB_DEVICE_ERROR_IO,
"Unexpected short error of %zd size (expected %zd)",
transfer->actual_length, transfer->length);
"Unexpected short error of %zd size (expected %zd)", transfer->actual_length, transfer->length);
}
callback = transfer->callback;
@@ -541,23 +548,3 @@ fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer,
return res;
}
/**
* fpi_usb_transfer_set_short_error:
* @transfer: The transfer to submit, must have been filled.
* @short_is_error: Whether a short transfer should be considered an error
*
* Sets whether a short transfer (a transfer in which the transferred length
* does not match the expected length) should be considered an error
*
* By default, short transfers are not considered an error, but
* drivers can enforce a further check by setting this flag.
*/
void
fpi_usb_transfer_set_short_error (FpiUsbTransfer *transfer,
gboolean short_is_error)
{
g_return_if_fail (transfer);
transfer->short_is_error = short_is_error;
}
+1 -3
View File
@@ -30,7 +30,7 @@
" <url type=\"homepage\">https://fprint.freedesktop.org</url>\n" \
" <url type=\"vcs-browser\">https://gitlab.freedesktop.org/libfprint/libfprint</url>\n" \
" <url type=\"help\">https://fprint.freedesktop.org/libfprint-dev</url>\n" \
" <url type=\"bugtracker\">https://gitlab.freedesktop.org/libfprint/libfprint/-/work_items</url>\n" \
" <url type=\"bugtracker\">https://gitlab.freedesktop.org/libfprint/libfprint/-/issues</url>\n" \
" <summary>Async fingerprint readers library</summary>\n" \
" <description>\n" \
" <p>\n" \
@@ -78,8 +78,6 @@ main (void)
g_autoptr(GString) provided_modules = g_string_new (NULL);
g_autoptr(GError) error = NULL;
g_log_writer_default_set_use_stderr (TRUE);
g_array_sort (drivers, driver_compare);
for (guint i = 0; i < drivers->len; ++i)
+12 -45
View File
@@ -26,25 +26,20 @@
static const FpIdEntry allowlist_id_table[] = {
/* Currently known and unsupported devices.
* You can regenerate this list from the wiki page with:
* meson compile -C <build-dir> sync-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'
*/
/* --- BEGIN GENERATED IDS --- */
{ .vid = 0x006b, .pid = 0x00da },
{ .vid = 0x0413, .pid = 0x2766 },
{ .vid = 0x045e, .pid = 0x0815 },
{ .vid = 0x0a5c, .pid = 0x5802 },
{ .vid = 0x047d, .pid = 0x00f2 },
{ .vid = 0x047d, .pid = 0x8054 },
{ .vid = 0x047d, .pid = 0x8055 },
{ .vid = 0x0483, .pid = 0x2016 },
{ .vid = 0x04e8, .pid = 0x730b },
{ .vid = 0x04f2, .pid = 0xb61e },
{ .vid = 0x04f3, .pid = 0x036b },
{ .vid = 0x04f3, .pid = 0x0c00 },
{ .vid = 0x04f3, .pid = 0x0c4c },
{ .vid = 0x04f3, .pid = 0x0c57 },
{ .vid = 0x04f3, .pid = 0x0c5a },
{ .vid = 0x04f3, .pid = 0x0c5e },
{ .vid = 0x04f3, .pid = 0x0c5a },
{ .vid = 0x04f3, .pid = 0x0c60 },
{ .vid = 0x04f3, .pid = 0x0c6c },
{ .vid = 0x04f3, .pid = 0x0c70 },
@@ -54,28 +49,15 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x04f3, .pid = 0x0c7f },
{ .vid = 0x04f3, .pid = 0x0c80 },
{ .vid = 0x04f3, .pid = 0x0c85 },
{ .vid = 0x04f3, .pid = 0x0c8a },
{ .vid = 0x04f3, .pid = 0x0c8e },
{ .vid = 0x04f3, .pid = 0x0c90 },
{ .vid = 0x04f3, .pid = 0x0ca2 },
{ .vid = 0x04f3, .pid = 0x24cc },
{ .vid = 0x04f3, .pid = 0x2706 },
{ .vid = 0x04f3, .pid = 0x3032 },
{ .vid = 0x04f3, .pid = 0x3057 },
{ .vid = 0x04f3, .pid = 0x3058 },
{ .vid = 0x04f3, .pid = 0x3066 },
{ .vid = 0x04f3, .pid = 0x3087 },
{ .vid = 0x04f3, .pid = 0x309f },
{ .vid = 0x04f3, .pid = 0x30c6 },
{ .vid = 0x04f3, .pid = 0x3104 },
{ .vid = 0x04f3, .pid = 0x310d },
{ .vid = 0x04f3, .pid = 0x3128 },
{ .vid = 0x04f3, .pid = 0x3195 },
{ .vid = 0x04f3, .pid = 0x31fc },
{ .vid = 0x04f3, .pid = 0x3201 },
{ .vid = 0x04f3, .pid = 0x3212 },
{ .vid = 0x04f3, .pid = 0x0c8a },
{ .vid = 0x05ba, .pid = 0x000e },
{ .vid = 0x05c8, .pid = 0x03c0 },
{ .vid = 0x06cb, .pid = 0x0051 },
{ .vid = 0x06cb, .pid = 0x0081 },
{ .vid = 0x06cb, .pid = 0x0088 },
@@ -89,9 +71,8 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x06cb, .pid = 0x00bb },
{ .vid = 0x06cb, .pid = 0x00bc },
{ .vid = 0x06cb, .pid = 0x00be },
{ .vid = 0x06cb, .pid = 0x00c7 },
{ .vid = 0x06cb, .pid = 0x00c9 },
{ .vid = 0x06cb, .pid = 0x00cb },
{ .vid = 0x06cb, .pid = 0x00c9 },
{ .vid = 0x06cb, .pid = 0x00d8 },
{ .vid = 0x06cb, .pid = 0x00da },
{ .vid = 0x06cb, .pid = 0x00dc },
@@ -99,12 +80,7 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x06cb, .pid = 0x00e7 },
{ .vid = 0x06cb, .pid = 0x00fd },
{ .vid = 0x06cb, .pid = 0x00ff },
{ .vid = 0x06cb, .pid = 0x0102 },
{ .vid = 0x06cb, .pid = 0xce67 },
{ .vid = 0x06cb, .pid = 0xceef },
{ .vid = 0x08ff, .pid = 0x3400 },
{ .vid = 0x0a5c, .pid = 0x5801 },
{ .vid = 0x0a5c, .pid = 0x5802 },
{ .vid = 0x0a5c, .pid = 0x5805 },
{ .vid = 0x0a5c, .pid = 0x5834 },
{ .vid = 0x0a5c, .pid = 0x5840 },
@@ -119,7 +95,6 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x0a5c, .pid = 0x5865 },
{ .vid = 0x0a5c, .pid = 0x5866 },
{ .vid = 0x0a5c, .pid = 0x5867 },
{ .vid = 0x0bda, .pid = 0x0129 },
{ .vid = 0x0bda, .pid = 0x5812 },
{ .vid = 0x10a5, .pid = 0x0007 },
{ .vid = 0x10a5, .pid = 0x9200 },
@@ -130,7 +105,6 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x10a5, .pid = 0xa921 },
{ .vid = 0x10a5, .pid = 0xe340 },
{ .vid = 0x1188, .pid = 0x9545 },
{ .vid = 0x138a, .pid = 0x0006 },
{ .vid = 0x138a, .pid = 0x0007 },
{ .vid = 0x138a, .pid = 0x003a },
{ .vid = 0x138a, .pid = 0x003c },
@@ -141,8 +115,8 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x138a, .pid = 0x0094 },
{ .vid = 0x138a, .pid = 0x0097 },
{ .vid = 0x138a, .pid = 0x009d },
{ .vid = 0x138a, .pid = 0x00a6 },
{ .vid = 0x138a, .pid = 0x00ab },
{ .vid = 0x138a, .pid = 0x00a6 },
{ .vid = 0x147e, .pid = 0x1002 },
{ .vid = 0x1491, .pid = 0x0088 },
{ .vid = 0x16d1, .pid = 0x1027 },
@@ -151,9 +125,6 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x1c7a, .pid = 0x0576 },
{ .vid = 0x1c7a, .pid = 0x0577 },
{ .vid = 0x1c7a, .pid = 0x057e },
{ .vid = 0x1c7a, .pid = 0x05a5 },
{ .vid = 0x1c7a, .pid = 0x05aa },
{ .vid = 0x1c7a, .pid = 0x0801 },
{ .vid = 0x2541, .pid = 0x0236 },
{ .vid = 0x2541, .pid = 0x9711 },
{ .vid = 0x27c6, .pid = 0x5042 },
@@ -181,27 +152,23 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x27c6, .pid = 0x55a4 },
{ .vid = 0x27c6, .pid = 0x55b4 },
{ .vid = 0x27c6, .pid = 0x5740 },
{ .vid = 0x27c6, .pid = 0x5e0a },
{ .vid = 0x27c6, .pid = 0x581a },
{ .vid = 0x27c6, .pid = 0x589a },
{ .vid = 0x27c6, .pid = 0x5e0a },
{ .vid = 0x27c6, .pid = 0x5f10 },
{ .vid = 0x27c6, .pid = 0x5f91 },
{ .vid = 0x2808, .pid = 0x06fc },
{ .vid = 0x2808, .pid = 0x6652 },
{ .vid = 0x27c6, .pid = 0x6382 },
{ .vid = 0x2808, .pid = 0x9338 },
{ .vid = 0x2808, .pid = 0x9348 },
{ .vid = 0x2808, .pid = 0x93a9 },
{ .vid = 0x2808, .pid = 0xa553 },
{ .vid = 0x2808, .pid = 0xa573 },
{ .vid = 0x2808, .pid = 0xa658 },
{ .vid = 0x2808, .pid = 0xc652 },
{ .vid = 0x2808, .pid = 0xa553 },
{ .vid = 0x298d, .pid = 0x2020 },
{ .vid = 0x298d, .pid = 0x2033 },
{ .vid = 0x2df0, .pid = 0x0003 },
{ .vid = 0x3274, .pid = 0x9008 },
{ .vid = 0x3274, .pid = 0xb012 },
{ .vid = 0x33a7, .pid = 0x2388 },
{ .vid = 0x3274, .pid = 0x8012 },
{ .vid = 0x3538, .pid = 0x0930 },
/* --- END GENERATED IDS --- */
{ .vid = 0 },
};
+111 -81
View File
@@ -1,7 +1,7 @@
spi_sources = []
spi_headers = []
if have_spi
if enabled_spi_drivers.length() > 0
spi_headers = ['fpi-spi-transfer.h']
spi_sources = ['fpi-spi-transfer.c']
endif
@@ -48,8 +48,8 @@ libfprint_private_headers = [
'fpi-log.h',
'fpi-minutiae.h',
'fpi-print.h',
'fpi-usb-transfer.h',
'fpi-ssm.h',
'fpi-usb-transfer.h',
] + spi_headers
nbis_sources = [
@@ -88,67 +88,91 @@ nbis_sources = [
]
driver_sources = {
'upekts' : files(
'drivers/upekts.c',
'drivers/upek_proto.c',
),
'upektc' : files('drivers/upektc.c'),
'upeksonly' : files('drivers/upeksonly.c'),
'uru4000' : files('drivers/uru4000.c'),
'aes1610' : files('drivers/aes1610.c'),
'aes1660' : files('drivers/aes1660.c'),
'aes2501' : files('drivers/aes2501.c'),
'aes2550' : files('drivers/aes2550.c'),
'aes2660' : files('drivers/aes2660.c'),
'aes3500' : files('drivers/aes3500.c'),
'aes4000' : files('drivers/aes4000.c'),
'vcom5s' : files('drivers/vcom5s.c'),
'vfs101' : files('drivers/vfs101.c'),
'vfs301' : files(
'drivers/vfs301.c',
'drivers/vfs301_proto.c',
),
'vfs5011' : files('drivers/vfs5011.c'),
'vfs7552' : files('drivers/vfs7552.c'),
'upektc_img' : files(
'drivers/upektc_img.c',
'drivers/upek_proto.c',
),
'etes603' : files('drivers/etes603.c'),
'egis0570' : files('drivers/egis0570.c'),
'egismoc' : files('drivers/egismoc/egismoc.c'),
'egis_etu905' : files('drivers/egismoc/egis_etu905.c'),
'vfs0050' : files('drivers/vfs0050.c'),
'elan' : files('drivers/elan.c'),
'elanmoc' : files('drivers/elanmoc/elanmoc.c'),
'elanspi' : files('drivers/elanspi.c'),
'nb1010' : files('drivers/nb1010.c'),
'virtual_image' : files('drivers/virtual-image.c'),
'virtual_device' : files('drivers/virtual-device.c'),
'virtual_device_storage' : files('drivers/virtual-device-storage.c'),
'synaptics' : files(
'drivers/synaptics/synaptics.c',
'drivers/synaptics/bmkt_message.c',
),
'goodixmoc' : files(
'drivers/goodixmoc/goodix.c',
'drivers/goodixmoc/goodix_proto.c',
),
'fpcmoc' : files('drivers/fpcmoc/fpc.c'),
'realtek' : files('drivers/realtek/realtek.c'),
'focaltech_moc' : files('drivers/focaltech_moc/focaltech_moc.c'),
'mafpmoc' : files('drivers/mafpmoc/mafpmoc.c'),
'secugen' : files('drivers/secugen.c'),
'upekts' :
[ 'drivers/upekts.c', 'drivers/upek_proto.c' ],
'upektc' :
[ 'drivers/upektc.c' ],
'upeksonly' :
[ 'drivers/upeksonly.c' ],
'uru4000' :
[ 'drivers/uru4000.c' ],
'aes1610' :
[ 'drivers/aes1610.c' ],
'aes1660' :
[ 'drivers/aes1660.c' ],
'aes2501' :
[ 'drivers/aes2501.c' ],
'aes2550' :
[ 'drivers/aes2550.c' ],
'aes2660' :
[ 'drivers/aes2660.c' ],
'aes3500' :
[ 'drivers/aes3500.c' ],
'aes4000' :
[ 'drivers/aes4000.c' ],
'vcom5s' :
[ 'drivers/vcom5s.c' ],
'vfs101' :
[ 'drivers/vfs101.c' ],
'vfs301' :
[ 'drivers/vfs301.c', 'drivers/vfs301_proto.c' ],
'vfs5011' :
[ 'drivers/vfs5011.c' ],
'vfs7552' :
[ 'drivers/vfs7552.c' ],
'upektc_img' :
[ 'drivers/upektc_img.c', 'drivers/upek_proto.c' ],
'etes603' :
[ 'drivers/etes603.c' ],
'egis0570' :
[ 'drivers/egis0570.c' ],
'egismoc' :
[ 'drivers/egismoc/egismoc.c' ],
'vfs0050' :
[ 'drivers/vfs0050.c' ],
'elan' :
[ 'drivers/elan.c' ],
'elanmoc' :
[ 'drivers/elanmoc/elanmoc.c' ],
'elanspi' :
[ 'drivers/elanspi.c' ],
'nb1010' :
[ 'drivers/nb1010.c' ],
'virtual_image' :
[ 'drivers/virtual-image.c' ],
'virtual_device' :
[ 'drivers/virtual-device.c' ],
'virtual_device_storage' :
[ 'drivers/virtual-device-storage.c' ],
'virtual_sdcp' :
[ 'drivers/virtual-sdcp.c' ],
'synaptics' :
[ 'drivers/synaptics/synaptics.c', 'drivers/synaptics/bmkt_message.c' ],
'goodixmoc' :
[ 'drivers/goodixmoc/goodix.c', 'drivers/goodixmoc/goodix_proto.c' ],
'fpcmoc' :
[ 'drivers/fpcmoc/fpc.c' ],
'realtek' :
[ 'drivers/realtek/realtek.c' ],
'focaltech_moc' :
[ 'drivers/focaltech_moc/focaltech_moc.c' ],
}
helper_sources = {
'aeslib': files('drivers/aeslib.c'),
'aesx660': files('drivers/aesx660.c'),
'aes3k': files('drivers/aes3k.c'),
'openssl': files(),
'pixman': files(),
'udev': files(),
'virtual': files('drivers/virtual-device-listener.c'),
'aeslib' :
[ 'drivers/aeslib.c' ],
'aesx660' :
[ 'drivers/aesx660.c' ],
'aes3k' :
[ 'drivers/aes3k.c' ],
'openssl' :
[ ],
'sdcp' :
[ ],
'udev' :
[ ],
'virtual' :
[ 'drivers/virtual-device-listener.c' ],
}
drivers_sources = []
@@ -160,6 +184,24 @@ foreach helper : driver_helpers
drivers_sources += helper_sources[helper]
endforeach
subdir('sdcp')
if 'sdcp' in driver_helpers
libfprint_sources += [
'fp-sdcp-device.c',
]
libfprint_private_sources += [
'fpi-sdcp.c',
'fpi-sdcp-device.c',
sdcp_truststore_resource_c,
]
libfprint_public_headers += [
'fp-sdcp-device.h',
]
libfprint_private_headers += [
'fpi-sdcp.h',
'fpi-sdcp-device.h',
]
endif
fp_enums = gnome.mkenums_simple('fp-enums',
sources: libfprint_public_headers,
@@ -212,7 +254,6 @@ deps = [
enums_dep,
gio_dep,
glib_dep,
gmodule_dep,
gobject_dep,
gusb_dep,
mathlib_dep,
@@ -301,15 +342,11 @@ libfprint_private_dep = declare_dependency(
]
)
udev_hwdb_list = 'fprint-list-udev-hwdb.c'
udev_hwdb_list_file = files(udev_hwdb_list)
udev_hwdb = executable('fprint-list-udev-hwdb',
udev_hwdb_list_file,
'fprint-list-udev-hwdb.c',
dependencies: libfprint_private_dep,
link_with: libfprint_drivers,
install: false,
link_args: ['-Wl,--disable-new-dtags'],
)
install: false)
udev_hwdb_generator = custom_target('udev-hwdb',
output: 'autosuspend.hwdb',
@@ -323,9 +360,7 @@ metainfo = executable('fprint-list-metainfo',
'fprint-list-metainfo.c',
dependencies: libfprint_private_dep,
link_with: libfprint_drivers,
install: false,
link_args: ['-Wl,--disable-new-dtags'],
)
install: false)
metainfo_generator = custom_target('metainfo',
output: 'org.freedesktop.libfprint.metainfo.xml',
@@ -341,9 +376,7 @@ if install_udev_rules
'fprint-list-udev-rules.c',
dependencies: libfprint_private_dep,
link_with: libfprint_drivers,
install: false,
link_args: ['-Wl,--disable-new-dtags'],
)
install: false)
custom_target('udev-rules',
output: '70-@0@.rules'.format(versioned_libname),
@@ -355,13 +388,10 @@ if install_udev_rules
)
endif
udev_hwdb_list_updater = custom_target('udev-hwdb-list-updater',
output: udev_hwdb_list,
command: [ sync_unsupported_files, udev_hwdb_list_file ],
)
run_target('sync-udev-hwdb',
depends: udev_hwdb_list_updater,
sync_udev_udb = custom_target('sync-udev-hwdb',
depends: udev_hwdb_generator,
output: 'sync-udev-hwdb',
install: false,
command: [
'cp', '-v',
udev_hwdb_generator.full_path(),
@@ -369,7 +399,7 @@ run_target('sync-udev-hwdb',
]
)
alias_target('sync-unsupported-devices', udev_hwdb_list_updater)
alias_target('sync-udev-hwdb', sync_udev_udb)
supported_devices = executable('fprint-list-supported-devices',
'fprint-list-supported-devices.c',
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/python3
import os
import sys
if len(sys.argv) != 3:
print("generate-gresource.py: Generates SDCP Truststore GResource XML file from certificates in ./truststore/*.pem")
print("Usage: generate-gresource.py <libfprint_sdcp_source_dir> <output_xml_file>")
sys.exit(1)
gresource_prefix = "/org/freedesktop/fprint/sdcp"
relative_folder = "truststore"
full_folder = os.path.join(sys.argv[1], relative_folder)
output = sys.argv[2]
files = [f for f in os.listdir(full_folder) if os.path.isfile(os.path.join(full_folder, f)) and f.endswith('.pem')]
with open(output, 'w') as f:
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
f.write('<gresources>\n')
f.write(f' <gresource prefix="{gresource_prefix}">\n')
for file in files:
f.write(f' <file compressed="true">{relative_folder}/{file}</file>\n')
f.write(' </gresource>\n')
f.write('</gresources>\n')
+32
View File
@@ -0,0 +1,32 @@
sdcp_truststore_gresource_xml = custom_target('sdcp-truststore.gresource',
input : 'generate-gresource.py',
output : 'sdcp-truststore.gresource.xml',
command : [find_program('python3'), '@INPUT@', meson.current_source_dir(), '@OUTPUT@'],
)
sdcp_truststore_resource_h = custom_target('fpi-sdcp-truststore-resource.h',
input : sdcp_truststore_gresource_xml,
output : 'fpi-sdcp-truststore-resource.h',
command : ['glib-compile-resources',
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
'--internal',
'--generate',
'--c-name', 'fpi_sdcp_truststore',
'--manual-register',
'@INPUT@']
)
sdcp_truststore_resource_c = custom_target('fpi-sdcp-truststore-resource.c',
depends : [sdcp_truststore_resource_h],
input : sdcp_truststore_gresource_xml,
output : 'fpi-sdcp-truststore-resource.c',
command : ['glib-compile-resources',
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
'--internal',
'--generate',
'--c-name', 'fpi_sdcp_truststore',
'--manual-register',
'@INPUT@']
)
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIDCCAqWgAwIBAgIQKs9yK9kUXqlMVB+fSF1UMjAKBggqhkjOPQQDAzCBlDEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE+MDwGA1UEAxM1TWlj
cm9zb2Z0IEVDQyBEZXZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw
MTcwHhcNMTcxMTA5MTk0MDQ4WhcNNDIxMTA5MTk0ODE5WjCBlDELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE+MDwGA1UEAxM1TWljcm9zb2Z0IEVD
QyBEZXZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq
hkjOPQIBBgUrgQQAIgNiAARiivDX0DS0EXoGlfbd2PwxSC87Cszr6/aAjSx6pMwU
4kzXcId0dhrjSkPSIO5UCz50ggQGQiTwqRzyhM44FlEyzbzl6OHGDwR1vAg3wdmm
WEXWySzyAZKsfkwg0G7bPkijgbkwgbYwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFBTaW/EOZkfRXRNfW3rr618BCLVJMBAGCSsGAQQBgjcV
AQQDAgEAMGUGA1UdIAReMFwwBgYEVR0gADBSBgwrBgEEAYI3TIN9AQEwQjBABggr
BgEFBQcCARY0aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1Jl
cG9zaXRvcnkuaHRtADAKBggqhkjOPQQDAwNpADBmAjEAxxAFFL8juLXiulXvZgBQ
pGGPCcV2Tr3CorZ4p/uO2/rtBemqhL3CjKAm40VlhEz8AjEArE5fhA54SEDjoTwZ
VUosaqXa8ych31qjZI+e1ttbOPebAZTt9ac7+lTzJcLTEQch
-----END CERTIFICATE-----
@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDwzCCA0qgAwIBAgITMwAAAAuNaMBkOddK4wAAAAAACzAKBggqhkjOPQQDAjCB
hDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEuMCwGA1UEAxMl
V2luZG93cyBIZWxsbyBTZWN1cmUgRGV2aWNlcyBQQ0EgMjAxODAeFw0yMTEyMDky
MzI1MDFaFw0zMDEyMDkyMzM1MDFaMFYxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVN
aWNyb3NvZnQgQ29ycG9yYXRpb24xJzAlBgNVBAMTHldpbmRvd3MgSGVsbG8gMjA5
NkFEQ0MgQ0EgMjAyMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJWgPvl44Gei
RrTuA3f1eT60pAlBWM7ym7WSchqz3hge1WS8RUxPVedu0f7MCe/R6O6RVjV7HWq4
c6jo9FwoiAGjggHGMIIBwjAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUvzdI
40pjLelTo7qJApjAaUcqmbkwVAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUH
AgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0
b3J5Lmh0bTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYw
DwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTaykvQTFYDJ1+X63WjAsO/RZz4
sTBoBgNVHR8EYTBfMF2gW6BZhldodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
b3BzL2NybC9XaW5kb3dzJTIwSGVsbG8lMjBTZWN1cmUlMjBEZXZpY2VzJTIwUENB
JTIwMjAxOC5jcmwwdQYIKwYBBQUHAQEEaTBnMGUGCCsGAQUFBzAChllodHRwOi8v
d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL1dpbmRvd3MlMjBIZWxsbyUy
MFNlY3VyZSUyMERldmljZXMlMjBQQ0ElMjAyMDE4LmNydDAKBggqhkjOPQQDAgNn
ADBkAjAeGyYlzf+uBQXI/EW84I5CGFbo/U6dL4k1Y83f2p94d0wrNwjUb/yprb4+
L9+OKfQCME8PgRyJQxsvsne+WI6gr0ZzJilotoiRdvDzlMK4+hx5TGJWTV17AsAo
z2330epHQQ==
-----END CERTIFICATE-----
@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEVjCCA9ygAwIBAgITMwAAAANsz+3iRHAZvwAAAAAAAzAKBggqhkjOPQQDAzCB
lDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE+MDwGA1UEAxM1
TWljcm9zb2Z0IEVDQyBEZXZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
IDIwMTcwHhcNMTgwMTI1MTk0OTM4WhcNMzMwMTI1MTk1OTM4WjCBhDELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEuMCwGA1UEAxMlV2luZG93cyBI
ZWxsbyBTZWN1cmUgRGV2aWNlcyBQQ0EgMjAxODB2MBAGByqGSM49AgEGBSuBBAAi
A2IABB3dCAIDJXUg4nGLrSgJgukG7oPFOmxLcZJQTiDpcrT8UyrvXcyatM12uJSX
RLJxDsmxFgOhZSu56F1f8jAu3bErIPy+AIjqH6d/mYSYfHE+TTSDaZsIy3iyS73X
Pr5noKOCAfwwggH4MBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTaykvQTFYD
J1+X63WjAsO/RZz4sTBlBgNVHSAEXjBcMAYGBFUdIAAwUgYMKwYBBAGCN0yDfQEB
MEIwQAYIKwYBBQUHAgEWNGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv
RG9jcy9SZXBvc2l0b3J5Lmh0bQAwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEw
CwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUFNpb8Q5m
R9FdE19beuvrXwEItUkwegYDVR0fBHMwcTBvoG2ga4ZpaHR0cDovL3d3dy5taWNy
b3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwRUNDJTIwRGV2aWNlcyUy
MFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMTcuY3JsMIGHBggr
BgEFBQcBAQR7MHkwdwYIKwYBBQUHMAKGa2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv
bS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwRUNDJTIwRGV2aWNlcyUyMFJvb3Ql
MjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMTcuY3J0MAoGCCqGSM49BAMD
A2gAMGUCMFYqrXJMuYyzI4D1X/ghlGYPdnfiewPdMF7LkMp45gstEuX3ZzFYcebz
ZMEEs4vp4gIxALkgYbnQXjqkoor+HfwnYQuYFowCnCB/7vPLHwo3YrGOztmanqzm
GtS48agrsbRAmw==
-----END CERTIFICATE-----
+110 -75
View File
@@ -1,18 +1,16 @@
project('libfprint', [ 'c', 'cpp' ],
version: '1.94.100',
version: '1.94.10',
license: 'LGPLv2.1+',
default_options: [
'buildtype=debugoptimized',
'warning_level=1',
'c_std=gnu99',
],
meson_version: '>= 0.62.0')
meson_version: '>= 0.59.0')
fs = import('fs')
gnome = import('gnome')
python3 = find_program('python3')
libfprint_conf = configuration_data()
libfprint_conf.set_quoted('LIBFPRINT_VERSION', meson.project_version())
@@ -95,7 +93,6 @@ versioned_libname = meson.project_name() + '-' + soversion.to_string()
glib_dep = dependency('glib-2.0', version: '>=' + glib_min_version)
gio_dep = dependency('gio-unix-2.0', version: '>=' + glib_min_version)
gobject_dep = dependency('gobject-2.0', version: '>=' + glib_min_version)
gmodule_dep = dependency('gmodule-2.0', version: '>=' + glib_min_version)
gusb_dep = dependency('gusb', version: '>= 0.2.0')
mathlib_dep = cc.find_library('m', required: false)
@@ -103,8 +100,6 @@ mathlib_dep = cc.find_library('m', required: false)
sh = find_program('sh', required: true)
cairo_dep = dependency('cairo', required: false)
sync_unsupported_files = files('scripts' / 'sync-unsupported-devices.py')
# introspection scanning and Gio-2.0.gir
gobject_introspection = dependency('gobject-introspection-1.0', required: get_option('introspection'))
@@ -113,60 +108,77 @@ have_spi = host_machine.system() == 'linux'
# Drivers
drivers = get_option('drivers').split(',')
virtual_drivers = [
'virtual_image',
'virtual_device',
'virtual_device_storage',
'virtual_sdcp',
]
default_drivers = [
'upektc_img',
'vfs5011',
'vfs7552',
'aes3500',
'aes4000',
'aes1610',
'aes1660',
'aes2660',
'aes2501',
'aes2550',
'vfs101',
'vfs301',
'vfs0050',
'etes603',
'egis0570',
'egismoc',
'vcom5s',
'synaptics',
'elan',
'elanmoc',
'uru4000',
'upektc',
'upeksonly',
'upekts',
'goodixmoc',
'nb1010',
'fpcmoc',
'realtek',
'focaltech_moc',
]
drivers_info = {
'upektc_img': {},
'vfs5011': { 'endian_dependent': true },
'vfs7552': {},
'aes3500': { 'helper': ['aeslib', 'aes3k', 'pixman'] },
'aes4000': { 'helper': ['aeslib', 'aes3k', 'pixman'] },
'aes1610': { 'helper': ['aeslib'] },
'aes1660': { 'helper': ['aeslib', 'aesx660'] },
'aes2660': { 'helper': ['aeslib', 'aesx660'] },
'aes2501': { 'helper': ['aeslib'] },
'aes2550': { 'helper': ['aeslib'] },
'vfs101': {},
'vfs301': { 'endian_dependent': true },
'vfs0050': { 'endian_dependent': true },
'etes603': {},
'egis0570': {},
'egismoc': {},
'egis_etu905': {},
'vcom5s': {},
'synaptics': {},
'elan': { 'endian_dependent': true },
'elanmoc': {},
'uru4000': { 'helper': ['openssl'] },
'upektc': {},
'upeksonly': {},
'upekts': {},
'goodixmoc': {},
'nb1010': {},
'fpcmoc': {},
'realtek': {},
'focaltech_moc': {},
'mafpmoc': {},
'secugen': {},
spi_drivers = [
'elanspi'
]
# SPI driver (non-optional if SPI is available)
'elanspi': { 'spi': true, 'helper': ['udev'], 'optional': not have_spi },
if have_spi
default_drivers += spi_drivers
endif
# Virtual drivers (test-only, optional)
'virtual_image': { 'virtual': true, 'helper': ['virtual'], 'optional': true },
'virtual_device': { 'virtual': true, 'helper': ['virtual'], 'optional': true },
'virtual_device_storage': { 'virtual': true, 'helper': ['virtual'], 'optional': true },
}
default_drivers = []
virtual_drivers = []
foreach name, info : drivers_info
if not info.get('optional', false)
default_drivers += name
endif
if info.get('virtual', false)
virtual_drivers += name
endif
endforeach
# 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',
'egismoc',
'elanmoc',
'etes603',
'focaltech_moc',
'nb1010',
'realtek',
'synaptics',
'upeksonly',
'upektc',
'upektc_img',
'upekts',
'vcom5s',
'vfs101',
'vfs7552',
]
all_drivers = default_drivers + virtual_drivers
@@ -178,18 +190,38 @@ if drivers == [ 'default' ]
drivers = default_drivers
endif
if not have_spi
foreach driver: drivers
if drivers_info[driver].get('spi', false)
error('SPI driver @0@ is not supported'.format(driver))
endif
endforeach
enabled_spi_drivers = []
foreach driver : spi_drivers
if driver in drivers
enabled_spi_drivers += driver
endif
endforeach
if enabled_spi_drivers.length() > 0 and not have_spi
error('SPI drivers @0@ are not supported'.format(enabled_spi_drivers))
endif
driver_helper_mapping = {
'aes1610' : [ 'aeslib' ],
'aes1660' : [ 'aeslib', 'aesx660' ],
'aes2501' : [ 'aeslib' ],
'aes2550' : [ 'aeslib' ],
'aes2660' : [ 'aeslib', 'aesx660' ],
'aes3500' : [ 'aeslib', 'aes3k' ],
'aes4000' : [ 'aeslib', 'aes3k' ],
'egismoc' : [ 'sdcp' ],
'elanspi' : [ 'udev' ],
'uru4000' : [ 'openssl' ],
'virtual_image' : [ 'virtual' ],
'virtual_device' : [ 'virtual' ],
'virtual_device_storage' : [ 'virtual' ],
'virtual_sdcp' : [ 'virtual', 'sdcp' ],
}
driver_helpers = []
foreach driver : drivers
if driver in drivers_info
foreach helper : drivers_info[driver].get('helper', [])
if driver in driver_helper_mapping
foreach helper : driver_helper_mapping[driver]
if helper not in driver_helpers
driver_helpers += helper
endif
@@ -221,14 +253,14 @@ optional_deps = []
# Resolve extra dependencies
foreach i : driver_helpers
foreach name, info : drivers_info
if i in info.get('helper', [])
driver = name
foreach d, helpers : driver_helper_mapping
if i in helpers
driver = d
break
endif
endforeach
if i == 'pixman'
if i == 'aes3k'
imaging_dep = dependency('pixman-1', required: false)
if not imaging_dep.found()
error('pixman is required for @0@ and possibly others'.format(driver))
@@ -242,6 +274,13 @@ foreach i : driver_helpers
error('OpenSSL is required for @0@ and possibly others'.format(driver))
endif
optional_deps += openssl_dep
elif i == 'sdcp'
openssl_dep = dependency('openssl', version: '>= 3.0.8', required: false)
if not openssl_dep.found()
error('OpenSSL >= 3.0.8 is required for SDCP support (@0@ and possibly others)'.format(driver))
endif
optional_deps += openssl_dep
elif i == 'udev'
install_udev_rules = true
@@ -269,11 +308,9 @@ if install_udev_rules
endif
endif
# FIXME: All the drivers should be fixed by adjusting the byte order.
# See https://gitlab.freedesktop.org/libfprint/libfprint/-/issues/236
supported_drivers = []
foreach driver: drivers
if build_machine.endian() == 'little' or not drivers_info[driver].get('endian_dependent', false)
if build_machine.endian() == 'little' or driver in endian_independent_drivers
supported_drivers += driver
else
warning('Driver @0@ is not supported by big endian cpu @1@. Please, fix it!'.format(
@@ -334,8 +371,6 @@ subdir('tests')
subdir('examples')
meson.add_dist_script(sync_unsupported_files, '--check', udev_hwdb_list_file)
pkgconfig = import('pkgconfig')
pkgconfig.generate(
name: versioned_libname,
-81
View File
@@ -1,81 +0,0 @@
#!/usr/bin/env python3
#
# Update the hardcoded allowlist of currently unsupported devices in
# fprint-list-udev-hwdb.c from the libfprint wiki page.
import argparse
import os
import re
import sys
from urllib.request import urlopen
DEFAULT_URL = (
"https://gitlab.freedesktop.org/libfprint/wiki/-/wikis/Unsupported-Devices.md"
)
ID_RE = re.compile(r"\|.*([0-9a-fA-F]{4}):([0-9a-fA-F]{4}).*\|.*")
BEGIN_MARKER = " /* --- BEGIN GENERATED IDS --- */\n"
END_MARKER = " /* --- END GENERATED IDS --- */\n"
def fetch(url):
try:
with urlopen(url) as response:
return response.read().decode("utf-8")
except OSError as exc:
print(f"Could not download {url}: {exc}", file=sys.stderr)
sys.exit(77)
def parse_entries(text):
devices = set()
for line in text.splitlines():
match = ID_RE.match(line)
if match:
devices.add((match.group(1).lower(), match.group(2).lower()))
return [f" {{ .vid = 0x{vid}, .pid = 0x{pid} }}," for vid, pid in sorted(devices)]
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("source_file", help="Path to hwdb file")
parser.add_argument("--url", default=DEFAULT_URL, help="Wiki page URL")
parser.add_argument(
"--check",
action="store_true",
help="Do not modify files, fail if an update is needed",
)
args = parser.parse_args()
entries = parse_entries(fetch(args.url))
if not entries:
sys.exit("No device ids found on the wiki page")
with open(args.source_file, "r", encoding="utf-8") as f:
content = f.read()
before, begin, rest = content.partition(BEGIN_MARKER)
_, end, after = rest.partition(END_MARKER)
if not begin or not end:
sys.exit("Could not locate generated id markers in " + args.source_file)
block = BEGIN_MARKER + "\n".join(entries) + "\n" + END_MARKER
new_content = before + block + after
if new_content == content:
print("allowlist_id_table is already up to date")
return
if args.check:
print("allowlist_id_table is out of date", file=sys.stderr)
sys.exit(1)
with open(args.source_file, "w", encoding="utf-8") as f:
f.write(new_content)
print(f"Updated allowlist_id_table with {len(entries)} entries")
if __name__ == "__main__":
main()
+1 -1
View File
@@ -54,7 +54,7 @@ sp_attribute_paren remove
sp_macro Force
sp_func_call_paren Force
sp_func_call_user_paren Remove
set func_call_user _ N_ C_ g_autoptr g_auto g_autolist g_autoslist
set func_call_user _ N_ C_ g_autoptr g_auto
sp_brace_typedef add
sp_cond_colon add
sp_cond_question add
+4 -7
View File
@@ -14,9 +14,9 @@ script, capture it and store the capture to `custom.pcapng`.
'capture' and 'custom' Test Creation
------------------------------------
For image devices, use the `capture` test to capture one reference image. For
non-image drivers, create a `custom.py` script in advance and select the
`custom` test instead.
For image devices the `capture.py` script will be used to capture one reference
image. If the driver is a non-image driver, then a `custom.py` script should be
created in advance, which will be run instead.
1. Make sure that libfprint is built with support for the device driver
that you want to create a test case for.
@@ -26,12 +26,9 @@ non-image drivers, create a `custom.py` script in advance and select the
but the hardware is slightly different, you might want to pass a variant
name as a command-line options, for example:
```sh
$ sudo tests/create-driver-test.py [--test capture|custom] driver [variant]
$ sudo tests/create-driver-test.py driver [variant]
```
By default, the tool runs `capture.py` and, when present, the test directory's
`custom.py`. Use `--test capture` or `--test custom` to record only that test.
3. If the capture is not successful, run the tool again to start another capture.
4. Add driver test name to `drivers_tests` in the `meson.build`, as instructed,
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.
-41
View File
@@ -1,41 +0,0 @@
#!/usr/bin/env python3
import sys
import traceback
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))
if len(sys.argv) != 1:
print("This test does not accept arguments")
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()
# We're testing that the device returns an error when only tapping the it.
print("Please tap the fingerprint reader, but do not swipe your finger across it.")
try:
d.capture_sync(True)
except GLib.Error as error:
assert error.matches(FPrint.device_retry_quark(),
FPrint.DeviceRetry.TOO_SHORT)
else:
raise AssertionError("Capture did not raise FP_DEVICE_RETRY_TOO_SHORT")
finally:
d.close_sync()
del d
del c
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/python3
import cairo
import sys
+67 -73
View File
@@ -1,12 +1,11 @@
#!/usr/bin/env python3
#!/usr/bin/python3
BUILDDIR='@BUILDDIR@'
SRCDIR='@SRCDIR@'
import signal
import argparse
import os
import sys
import signal
library_path = BUILDDIR + '/libfprint/'
# Relaunch ourselves with a changed environment so
@@ -28,31 +27,35 @@ from gi.repository import FPrint
gi.require_version('GUsb', '1.0')
from gi.repository import GUsb
import re
import shutil
import subprocess
import tempfile
import time
def test_variant(value):
if (not value or any(not (char.islower() or char == '-') for char in value) or
value.startswith('-') or value.endswith('-')):
raise argparse.ArgumentTypeError(
'must contain only lowercase letters and dashes')
return value
def print_usage():
print(f'Usage: {sys.argv[0]} driver [test-variant-name]')
print('A test variant name is optional, and must be all lower case letters, or dashes, with no spaces')
print(f'The captured data will be stored in {SRCDIR}/tests/[driver name]-[test variant name]')
print(f'Create custom.py prior to execution for non image device tests.')
parser = argparse.ArgumentParser(
description='Capture USB traffic for a libfprint driver test.')
parser.add_argument('driver')
parser.add_argument('variant', nargs='?', type=test_variant,
help='Optional, lowercase test variant name')
parser.add_argument('--test', choices=('capture', 'custom'),
help='Run only the selected test (default: run both)')
options = parser.parse_args()
if len(sys.argv) > 3:
print_usage()
sys.exit(1)
driver_name = options.driver
driver_name = sys.argv[1]
os.environ['FP_DRIVERS_ALLOWLIST'] = driver_name
test_variant = options.variant
test_variant = None
if len(sys.argv) == 3:
valid_re = re.compile('[a-z-]*')
test_variant = sys.argv[2]
if (not valid_re.match(test_variant) or
test_variant.startswith('-') or
test_variant.endswith('-')):
print(f'Invalid variant name {test_variant}\n')
print_usage()
sys.exit(1)
# Check that running as root
@@ -91,9 +94,6 @@ print(f'### Detected USB device /dev/bus/usb/{bus_num:03d}/{device_num:03d}')
test_dir = SRCDIR + '/tests/' + test_name
os.makedirs(test_dir, mode=0o775, exist_ok=True)
custom_script = os.path.join(test_dir, 'custom.py')
if options.test == 'custom' and not os.path.exists(custom_script):
parser.error(f'custom test script does not exist: {custom_script}')
# Capture device info
@@ -110,54 +110,32 @@ usb_device.open()
usb_device.reset()
usb_device.close()
def capture_test(cmd, capture_file, prompt):
print(f'### Starting USB capture on usbmon{bus_num}')
capture_pid = os.fork()
assert(capture_pid >= 0)
print(f'### Starting USB capture on usbmon{bus_num}')
capture_pid = os.fork()
assert(capture_pid >= 0)
unfiltered_cap_path = os.path.join(tempfile.gettempdir(),
f'{capture_file}-unfiltered.pcapng')
if capture_pid == 0:
os.setpgrp()
args = ['tshark', '-q', '-i', f'usbmon{bus_num}', '-w', unfiltered_cap_path]
os.execv(tshark, args)
unfiltered_cap_path = os.path.join(tempfile.gettempdir(), 'capture-unfiltered.pcapng')
if capture_pid == 0:
os.setpgrp()
args = ['tshark', '-q', '-i', f'usbmon{bus_num}', '-w', unfiltered_cap_path]
os.execv(tshark, args)
# Wait 1 sec to settle (we can assume setpgrp happened)
time.sleep(1)
# Wait 1 sec to settle (we can assume setpgrp happened)
time.sleep(1)
print(prompt)
with subprocess.Popen([sys.executable] + cmd) as capture_process:
capture_process.wait()
if capture_process.returncode != 0:
print('Failed to capture fingerprint')
os.killpg(capture_pid, signal.SIGKILL)
sys.exit(1)
print('### Capturing fingerprint, please swipe or press your finger on the reader')
cmd = ['python3', SRCDIR + '/tests/capture.py', test_dir + '/capture.png']
capture_file = 'capture.pcapng' # capture for "capture" test
if os.path.exists(os.path.join(test_dir, "custom.py")):
cmd = ['python3', os.path.join(test_dir, "custom.py")]
capture_file = "custom.pcapng"
os.kill(capture_pid, signal.SIGTERM)
try:
r = t_waitpid(capture_pid, 2)
# Kill if nothing died
if r[0] == 0:
os.kill(capture_pid, signal.SIGKILL)
except ChildProcessError:
pass
try:
while True:
r = t_waitpid(-capture_pid, timeout=2)
# Kill the process group, if nothing died (and there are children)
if r[0] == 0:
os.killpg(capture_pid, signal.SIGKILL)
except ChildProcessError:
pass
# Filter the capture
print(f'\n### Saving USB capture as test case {test_name}')
args = ['tshark', '-r', unfiltered_cap_path,
'-Y', f'usb.bus_id == {bus_num} and usb.device_address == {device_num}',
'-w', os.path.join(test_dir, capture_file)]
with subprocess.Popen(args, stderr=subprocess.DEVNULL) as filter_process:
filter_process.wait()
with subprocess.Popen(cmd) as capture_process:
capture_process.wait()
if capture_process.returncode != 0:
print('Failed to capture fingerprint')
os.killpg(capture_pid, signal.SIGKILL)
sys.exit(1)
def t_waitpid(pid, timeout):
timeout = time.time() + timeout
@@ -168,13 +146,29 @@ def t_waitpid(pid, timeout):
return r
if options.test in (None, 'capture'):
capture_test([SRCDIR + '/tests/capture.py', test_dir + '/capture.png'],
'capture.pcapng',
'### Capturing fingerprint, please swipe or press your finger on the reader')
os.kill(capture_pid, signal.SIGTERM)
try:
r = t_waitpid(capture_pid, 2)
# Kill if nothing died
if r[0] == 0:
os.kill(capture_pid, signal.SIGKILL)
except ChildProcessError:
pass
if options.test in (None, 'custom') and os.path.exists(custom_script):
capture_test([custom_script], 'custom.pcapng',
'### Running the custom fingerprint capture')
try:
while True:
r = t_waitpid(-capture_pid, timeout=2)
# Kill the process group, if nothing died (and there are children)
if r[0] == 0:
os.killpg(capture_pid, signal.SIGKILL)
except ChildProcessError:
pass
# Filter the capture
print(f'\n### Saving USB capture as test case {test_name}')
args = ['tshark', '-r', unfiltered_cap_path, '-Y', f'usb.bus_id == {bus_num} and usb.device_address == {device_num}',
'-w', os.path.join(test_dir, capture_file)]
with subprocess.Popen(args, stderr=subprocess.DEVNULL) as filter_process:
filter_process.wait()
print(f"\nDone! Don't forget to add {test_name} to tests/meson.build")
Binary file not shown.
-171
View File
@@ -1,171 +0,0 @@
#!/usr/bin/python3
import traceback
import sys
import gi
gi.require_version('FPrint', '2.0')
from gi.repository import FPrint, GLib, Gio
# 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() == "egis_etu905"
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.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()
def enroll_progress(*args):
assert d.get_finger_status() == FPrint.FingerStatusFlags.PRESENT
print("enroll progress stage: " + str(args[1]))
def identify_done(dev, res):
global identified
identified = True
identify_match, identify_print = dev.identify_finish(res)
print("MATCH FOUND!" if identify_match else "NO MATCH FOUND")
if identify_match:
assert identify_match.equal(identify_print)
# List
print("--- LISTING ---")
stored = d.list_prints_sync()
prints1 = len(stored)
print(f"--- LIST DONE: Found {prints1} prints before enroll---")
# Enroll
print("--- ENROLLING ---")
template = FPrint.Print.new(d)
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 ---")
del template
# List
print("--- LISTING ---")
stored = d.list_prints_sync()
prints2 = len(stored)
print(f"--- LIST DONE: Found {prints2} prints after enroll---")
assert (prints2 - prints1) == 1
# Verify
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(f"--- VERIFY DONE: Result {verify_res} ---")
# Identify
print("--- ASYNC IDENTIFYING ---")
identified = False
deserialized_prints = []
for sp in stored:
deserialized_prints.append(FPrint.Print.deserialize(sp.serialize()))
# The last stored print should be the newly enrolled one
assert deserialized_prints[-1].equal(p)
d.identify(deserialized_prints, callback=identify_done)
del deserialized_prints
while not identified:
ctx.iteration(True)
print("--- IDENTIFY DONE ---")
# Cancel test - start async identify and cancel it
print("--- TESTING CANCELLATION ---")
deserialized_prints = []
for sp in stored:
deserialized_prints.append(FPrint.Print.deserialize(sp.serialize()))
cancellable = Gio.Cancellable()
identify_cancelled = False
cancel_result = None
def identify_cancelled_cb(dev, res):
global identify_cancelled, cancel_result
identify_cancelled = True
try:
result = dev.identify_finish(res)
cancel_result = result
except Exception as e:
cancel_result = e
print(f"Identify cancelled with error: {e}")
# Test 1: Cancel immediately after starting identify
d.identify(deserialized_prints, cancellable=cancellable, callback=identify_cancelled_cb)
print("--- IDENTIFY STARTED, CANCELLING IMMEDIATELY ---")
cancellable.cancel()
while not identify_cancelled:
ctx.iteration(True)
print(f"--- CANCELLATION TEST DONE, result: {cancel_result} ---")
# Reopen device to ensure clean state for next cancel test
print("--- REOPENING DEVICE BEFORE NEXT CANCEL TEST ---")
d.close_sync()
d.open_sync()
print("--- DEVICE REOPENED ---")
# Test 2: Cancel after device reaches wait-for-finger stage
cancellable = Gio.Cancellable()
identify_cancelled = False
cancel_result = None
d.identify(deserialized_prints, cancellable=cancellable, callback=identify_cancelled_cb)
# Wait until the device is actually waiting for a finger before cancelling.
print("Waiting for device to reach wait-for-finger stage before cancelling...")
while not (d.get_finger_status() & FPrint.FingerStatusFlags.NEEDED):
ctx.iteration(True)
cancellable.cancel()
while not identify_cancelled:
ctx.iteration(True)
print(f"--- CANCELLATION TEST DONE, result: {cancel_result} ---")
del deserialized_prints
# Close and reopen device to ensure clean state after cancellation
print("--- REOPENING DEVICE ---")
d.close_sync()
d.open_sync()
print("--- DEVICE REOPENED ---")
# Delete
print("--- DELETING ---")
p_to_delete = next((sp for sp in stored if sp.equal(p)), None)
if p_to_delete:
d.delete_print_sync(p_to_delete)
print("--- DELETE DONE ---")
del p_to_delete
del p
# List
print("--- LISTING ---")
stored = d.list_prints_sync()
prints3 = len(stored)
print(f"--- LIST DONE: Found {prints3} prints after deleting---")
assert (prints2 - prints3) == 1
del stored
d.close_sync()
del d
del c
-275
View File
@@ -1,275 +0,0 @@
P: /devices/pci0000:00/0000:00:14.0/usb3/3-4
N: bus/usb/003/095=12010002FF0000407A1CAE0542990102030109022700010100A0320904000003FFFFFF00070581024000000705020240000007058303400001
E: BUSNUM=003
E: CURRENT_TAGS=:snap_cups_ippeveprinter:snap_cups_cupsd:
E: DEVNAME=/dev/bus/usb/003/095
E: DEVNUM=095
E: DEVTYPE=usb_device
E: DRIVER=usb
E: ID_BUS=usb
E: ID_MODEL=ETU905Axx-E
E: ID_MODEL_ENC=ETU905Axx-E
E: ID_MODEL_ID=05ae
E: ID_PATH=pci-0000:00:14.0-usb-0:4
E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_4
E: ID_PATH_WITH_USB_REVISION=pci-0000:00:14.0-usbv2-0:4
E: ID_REVISION=9942
E: ID_SERIAL=EGIS_ETU905Axx-E_0A8615PNA357
E: ID_SERIAL_SHORT=0A8615PNA357
E: ID_USB_INTERFACES=:ffffff:
E: ID_USB_MODEL=ETU905Axx-E
E: ID_USB_MODEL_ENC=ETU905Axx-E
E: ID_USB_MODEL_ID=05ae
E: ID_USB_REVISION=9942
E: ID_USB_SERIAL=EGIS_ETU905Axx-E_0A8615PNA357
E: ID_USB_SERIAL_SHORT=0A8615PNA357
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=350
E: PRODUCT=1c7a/5ae/9942
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=9942\n
A: bmAttributes=a0\n
A: busnum=3\n
A: configuration=
H: descriptors=12010002FF0000407A1CAE0542990102030109022700010100A0320904000003FFFFFF00070581024000000705020240000007058303400001
A: dev=189:350\n
A: devnum=95\n
A: devpath=4\n
L: driver=../../../../../bus/usb/drivers/usb
L: firmware_node=../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:11/device:12/device:16
A: idProduct=05ae\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=left\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-port4
A: power/active_duration=6368964\n
A: power/async=enabled\n
A: power/autosuspend=2\n
A: power/autosuspend_delay_ms=2000\n
A: power/connected_duration=6368964\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=6368399\n
A: power/runtime_enabled=forbidden\n
A: power/runtime_status=active\n
A: power/runtime_suspended_time=0\n
A: power/runtime_usage=2\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=ETU905Axx-E\n
A: quirks=0x0\n
A: removable=removable\n
A: rx_lanes=1\n
A: serial=0A8615PNA357\n
A: speed=12\n
A: tx_lanes=1\n
A: urbnum=3666\n
A: version= 2.00\n
P: /devices/pci0000:00/0000:00:14.0/usb3
N: bus/usb/003/001=12010002090001406B1D020017060302010109021900010100E0000904000001090000000705810304000C
E: BUSNUM=003
E: CURRENT_TAGS=:snap_cups_cupsd:seat: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=0617
E: ID_SERIAL=Linux_6.17.0-35-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=0617
E: ID_USB_SERIAL=Linux_6.17.0-35-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.17.0-35-generic_xhci-hcd
E: ID_USB_VENDOR_ENC=Linux\x206.17.0-35-generic\x20xhci-hcd
E: ID_USB_VENDOR_ID=1d6b
E: ID_VENDOR=Linux_6.17.0-35-generic_xhci-hcd
E: ID_VENDOR_ENC=Linux\x206.17.0-35-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/617
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=0617\n
A: bmAttributes=e0\n
A: busnum=3\n
A: configuration=
H: descriptors=12010002090001406B1D020017060302010109021900010100E0000904000001090000000705810304000C
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:11/device:12
A: idProduct=0002\n
A: idVendor=1d6b\n
A: interface_authorized_default=1\n
A: ltm_capable=no\n
A: manufacturer=Linux 6.17.0-35-generic xhci-hcd\n
A: maxchild=12\n
A: power/active_duration=107301257\n
A: power/async=enabled\n
A: power/autosuspend=0\n
A: power/autosuspend_delay_ms=0\n
A: power/connected_duration=114254295\n
A: power/control=auto\n
A: power/level=auto\n
A: power/runtime_active_kids=2\n
A: power/runtime_active_time=107307803\n
A: power/runtime_enabled=enabled\n
A: power/runtime_status=active\n
A: power/runtime_suspended_time=6940575\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=4179\n
A: version= 2.00\n
P: /devices/pci0000:00/0000:00:14.0
E: DRIVER=xhci_hcd
E: ID_MODEL_FROM_DATABASE=Meteor Lake-P USB 3.2 Gen 2x1 xHCI Host Controller
E: ID_PATH=pci-0000:00:14.0
E: ID_PATH_TAG=pci-0000_00_14_0
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:v00008086d00007E7Dsv00001028sd00000CB2bc0Csc03i30
E: PCI_CLASS=C0330
E: PCI_ID=8086:7E7D
E: PCI_SLOT_NAME=0000:00:14.0
E: PCI_SUBSYS_ID=1028:0CB2
E: SUBSYSTEM=pci
A: ari_enabled=0\n
A: broken_parity_status=0\n
A: class=0x0c0330\n
H: config=86807D7E060490022030030C1000800004002A195000000000000000000000000000000000000000000000002810B20C000000007000000000000000FF010000FD0134C08FC2FF8300000000000000003F6DDE0F000000000000000000000000326000000000000000000000000000000180C3C10800000000000000000000000590B7001804E0FE000000000000000009B014F01000400100000000C10A080000080E00001800008F50020000030000090000018680C00009001014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F200012000000
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: dbc_poll_interval_ms=64\n
A: device=0x7e7d\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:11
L: iommu=../../virtual/iommu/dmar1
L: iommu_group=../../../kernel/iommu_groups/11
A: irq=133\n
A: local_cpulist=0-13\n
A: local_cpus=3fff\n
A: modalias=pci:v00008086d00007E7Dsv00001028sd00000CB2bc0Csc03i30\n
A: msi_bus=1\n
A: msi_irqs/133=msi\n
A: msi_irqs/134=msi\n
A: msi_irqs/135=msi\n
A: msi_irqs/136=msi\n
A: msi_irqs/137=msi\n
A: msi_irqs/138=msi\n
A: msi_irqs/139=msi\n
A: msi_irqs/140=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 256 port bw ctx arrays 0 0 256 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 8 23 2112 23\nxHCI ring segments 35 73 4096 73\nbuffer-2048 0 0 2048 0\nbuffer-512 0 8 512 1\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=107308329\n
A: power/runtime_enabled=enabled\n
A: power/runtime_status=active\n
A: power/runtime_suspended_time=6940388\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=2\n
A: power/wakeup_count=0\n
A: power/wakeup_expire_count=2\n
A: power/wakeup_last_time_ms=103077428\n
A: power/wakeup_max_time_ms=105\n
A: power/wakeup_total_time_ms=207\n
A: power_state=D0\n
A: resource=0x00000050192a0000 0x00000050192affff 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=0x20\n
A: subsystem_device=0x0cb2\n
A: subsystem_vendor=0x1028\n
A: vendor=0x8086\n
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/python3
import traceback
import sys

Some files were not shown because too many files have changed in this diff Show More