Compare commits

..

303 Commits

Author SHA1 Message Date
Benjamin Berg
5cb91a4189 Release 1.90.5 2020-12-01 10:14:26 +01:00
Benjamin Berg
0bb132b167 NEWS: Fix release date of 1.90.4 2020-12-01 10:14:02 +01:00
Benjamin Berg
ce39f27b5e vfs301_proto: Remove usless break after return
Closes: #341
2020-11-30 20:22:18 +00:00
Benjamin Berg
7d0956513b upekts: Remove duplicated err handling path
Closes: #342
2020-11-30 20:22:17 +00:00
Marco Trevisan (Treviño)
2b7cfa751a list-udev-rules: Remove Wrongly added well-known USB controller vid/pid combo
This was causing USB controllers to use power-saving mode, breaking usb
devices usage.

Related to: https://gitlab.freedesktop.org/libfprint/libfprint/-/issues/327
2020-11-30 20:03:32 +01:00
Vincent Huang
0eee6a56dd synaptics: add support to sensor PID 0xC9 2020-11-30 13:53:07 +08:00
Benjamin Berg
8962e14fde Release 1.90.4 2020-11-27 13:55:49 +01:00
tt83
e246e00ba3 elan: added 0c4d device to the array of supported devices 2020-11-26 12:39:19 +01:00
Marco Trevisan (Treviño)
fa3bdb874d udev-rules: Regenerate from wiki to include VFS495 2020-11-25 18:28:41 +01:00
Benjamin Berg
2caeb8cbb3 udev-rules: Add unsupported devices from wiki page to rules
This ensures we are putting all these devices (which are unusable) into
power save mode.
2020-11-25 14:56:06 +00:00
Marco Trevisan (Treviño)
dda3587b76 identify: Use stored print to show identify information
The stored print may contain more metadata, so let's use that for
device-stored print, if available.
2020-11-23 17:00:01 +00:00
Marco Trevisan (Treviño)
fb5854213a examples: Add Identify example
It was the only main action left in the examples, we use the gallery
from the device if available, otherwise the local one.
2020-11-23 17:00:01 +00:00
Benjamin Berg
21ee241f0c virtual-image: Add command to trigger device removal
This is primarily useful for fprintd testing.
2020-11-23 17:47:03 +01:00
Benjamin Berg
280f916ace image-device: Fix incorrect g_free of error
GError needs to be free'ed using g_error_free, fix this.
2020-11-23 17:47:03 +01:00
Benjamin Berg
1b5dd0057f tests: Add device removal test
This tests all relevant scenarios of device removal, i.e.:
 * device is not open
 * device is currently closing
 * device is open and idle
 * device is currently opening
 * device is open and active

The test ensures that in all scenarios the following holds true:
 * device "removed" signal is only emitted after the action completes
 * context "device-removed" signal is only emitted after the device has
   been closed

Note that the "opening" case is special. Here we confirm that a success
from "open" will not be overriden by a FP_DEVICE_ERROR_REMOVED error, in
order to correctly signal that the internal device state is open and it
needs to be closed.
2020-11-23 17:47:03 +01:00
Benjamin Berg
8a6f1932f8 virtual-image: Add a notify::removed handler
In general, we rely on the underlying transport layer to throw errors
which will abort the current operation. This does not work for the
virtual image device though, but we need it there for testing purposes.

Add a notify::removed handler that makes things work as expected. Let it
throw a protocol error which should not be visible to the outside.
2020-11-23 17:47:03 +01:00
Benjamin Berg
0051ff6352 device: Treat devices as closed even after a close failure
We require the close call, but as the underlying transport layer is
gone, it will generally just return an error.

In principle, it makes sense to think of close as a function that always
succeeds (i.e. it makes no sense to try again). Should the device be in
a bad state, then a subsequent open() will simply fail.
2020-11-23 17:47:03 +01:00
Benjamin Berg
b6dd522459 Rework device removal to have a nice API
This enhances the device removal to create a well defined behaviour.

Primarily, it means that:
 * "device-removed" will only be called for closed devices
 * "removed" will be called only when no operation is active

Note that all actions will fail with FP_DEVICE_ERROR_REMOVED, *except*
for open which will only return this error if it failed.

Resolves: #330
2020-11-23 17:47:03 +01:00
Benjamin Berg
656bf3d175 elan: Add a few comments that stopping/callibrating helps
Apparently stopping/callibrating helps with the reliability of these
sensors. Add a few comments so that this information is known.

See https://gitlab.freedesktop.org/libfprint/libfprint/-/issues/216#note_619467
2020-11-18 15:18:11 +01:00
Benjamin Berg
fe498c56c7 virtual-image: Fix race condition closing new connection
When a new connection came in we would close the old connection. This in
turn would trigger a receive error causing the *new* connection to be
closed from the error handler.

Fix this by simply cancelling any pending transfers when a new
connection comes in. Also change the error handling code to catch issues
like partial writes correctly.

This fixes an issue for the fprintd test where some tests were flaky.
2020-11-10 15:59:53 +01:00
Marco Trevisan (Treviño)
251ccef9ba fpi-device: Add debug information about the enrolled finger 2020-11-08 22:19:23 +01:00
Marco Trevisan (Treviño)
3b993fabb6 verify/enroll: Save and use locally saved prints also for devices with storage
Some devices have storage but that's is limited enough not to be able
to store all the metadata, but just a fingerprint id.

In such case we also need to use the local storage to be able to verify.
Fprintd does this already, but we don't do it in the libfprint examples.

So, in in case we enroll, always save the print information to the disk,
while in case we verify we try to load the print from disk and we use
that in case its private data matches the one provided by the device.
2020-11-08 22:19:23 +01:00
Marco Trevisan (Treviño)
0c56e0de6d synaptics: Report finger status to libfprint
The inactivation in case would be set back by libfprint
2020-11-07 13:23:30 +00:00
Marco Trevisan (Treviño)
893ff9c033 fp-image-device: Report finger status changes to the device
While the image device has its own finger status tracking, we use a simpler
version as public data information, so let's just report the finger-on/off
and when a finger is expected to the parent class.

Verify that this happens as expected using the virtual-image class
2020-11-07 13:23:30 +00:00
Marco Trevisan (Treviño)
3c382cac7f fp-device: Reset the finger status on complete
Devices should handle the finger status internally, but if they don't do it
we need to handle it on actions completion
2020-11-07 13:23:30 +00:00
Marco Trevisan (Treviño)
42e4506b1b fp-device: Add finger-status property
It can be used by drivers to report the state of the finger on sensor
2020-11-07 13:23:30 +00:00
Benjamin Berg
ae3baadcf9 tests: Add a new test for vfs301
See: #320
2020-11-05 15:55:09 +01:00
Benjamin Berg
4f29a32da8 tests: Store temporary directory on failure
It is not very useful to just delete the data again after a failure, as
it might be useful for debugging. Just store it into an "errors"
subdirectory of the PWD in the hope that this is a sane location.

Note that it'll error out if the directory already exists, but that
should be acceptable in all cases. i.e. it won't exist in the CI and
developers can just wipe the directory.
2020-11-05 15:52:54 +01:00
Benjamin Berg
e5fa54e8e7 vfs301: Start capture only on state change to AWAIT_FINGER_ON
Start the capture when the state changes to AWAIT_FINGER_ON instead of
assuming that the device should always be active.

Closes: #320
2020-11-04 14:13:00 +01:00
Benjamin Berg
d3076039d0 vfs301: Fix device pointer handling in callback
When porting the driver to the new libfprint 1.90.0 a mistake was made
where the device was not passed through user_data anymore but it was
still read from there. Stop using user_data in the callback to fix this.

See: #320
2020-11-04 14:13:00 +01:00
Benjamin Berg
a748f4d30f elan: Simplify elan driver internal state machine
The elan driver always "deactivates" the device after a cpature run. We
can simplify the while internal state into a single "active" state and
rely on the image device driving this state machine correctly.

i.e.:
 * We start a callibrate/capture/deactivate when we go into the
   AWAIT_FINGER_ON state
 * We only store the fact that we are active and want to deactivate
 * We rely on the image device never going into the AWAIT_FINGER_ON
   state without first waiting for the finger to be off (which implies
   deactivation).
2020-11-04 14:13:00 +01:00
Benjamin Berg
3ee5536a13 image-device: Redefine internal states and valid transitions
This adds a number of new internal states to better capture what is
going on. Also added are checks that all transitions we make are in the
set of expected and valid transitions.

Only three drivers use the state_change notification. These drivers are
updated accordingly.
2020-11-04 14:13:00 +01:00
Benjamin Berg
f56aacc7ef image-device: Remove cancelling boolean from private data
The boolean is just used to emit a warning for unexpected state
transitions. It is sufficient to pass it to the deactivate function
directly for that purpose.
2020-11-04 14:13:00 +01:00
Benjamin Berg
96fa0a96eb uru4000: Fix missing reference to image transfer
We might redo image transfers, but we only ever had one reference that
was implicitly removed after the transfer completed. Add a new reference
each time it is submitted and only free the last reference in the stop
handler.
2020-10-19 17:41:26 +02:00
Benjamin Berg
1754bd0204 synaptics: Always submit interrupt transfer without a timeout
For some reason we re-submitted the interrupt transfer with a timeout of
1s while the original submission was without a timeout. This looks like
typo, remove the timeout.
2020-10-19 17:14:24 +02:00
Benjamin Berg
90a1abf2f8 synaptics: Fix missing reference to interrupt transfer
When resubmitting the interrupt transfer we need to add a new reference
as the submit function will steal it again.
2020-10-19 17:13:58 +02:00
Benjamin Berg
59824d2122 synaptics: Remove useless code
There is never a fall through if error is not NULL.
2020-10-19 16:59:07 +02:00
Vincent Huang
31319d9c6f synaptics: add support to pid 0xF9, 0xFC, 0xC2 2020-10-15 12:25:34 +08:00
Vincent Huang
e27b65c930 synaptics: add identify function 2020-10-14 18:48:09 +08:00
Benjamin Berg
b03f9a502a tests: Remove old README-umockdev file
It has been superseeded by README.md.
2020-10-14 12:39:10 +02:00
Benjamin Berg
44ef20d5ac aesx660: Use convenience API to allocate USB receive buffer
This also fixes a memory leak of the data as the g_free was missing and
none of the callback handlers free the data either.
2020-10-07 13:38:59 +02:00
Benjamin Berg
4719b30f16 aes1610: Use convenience API to allocate USB receive buffer 2020-10-07 13:38:59 +02:00
Benjamin Berg
7eb361087a drivers: Add access annotations to USB helpers
Mostly for completeness sake, doing this did not find any errors (but
might catch some issues with fixed buffer lengths).
2020-10-07 13:38:59 +02:00
Benjamin Berg
33d50e4e30 usb: Annotate access to USB buffers
Annotate the USB transfer creation functions with the correct buffer
access attribute. Note that we only annotate them as read_only as the
functions may be used for sending and receiving.

Hopefully this will catch buffer overflows in drivers in the future.
2020-10-07 13:22:18 +02:00
Benjamin Berg
994690cfa3 virtual-image: Only read socket when active
Doing this avoids race conditions in testing code where the code might
try to submit errors/images before the device has been activated. This
would then (sometimes) trigger assertions in the image driver code.
2020-10-07 09:44:49 +00:00
Benjamin Berg
52b2d10887 image-device: Delay task completion until device is deactivated
The earlier image device code tried to hide deactivation from the
surrounding library. However, this does not make any sense anymore with
the early reporting feature present.

This changes the operation to complete only once the device is
deactivated. Also changed that in most cases (except for cancellation)
we wait for the finger to be removed before deactivating the device.
2020-10-07 09:44:49 +00:00
Benjamin Berg
81e0f4dfe5 aes3k: Re-send some commands for each capture
The driver seems to have relied on the device to be fully
deactivated and activated again for each capture. This is not the case
during enroll, and as such in can cause issues.

Try fixing this by splitting out the last few commands send to the
device and assuming that this starts the capture.

Fixes: #306
2020-10-07 11:34:12 +02:00
Benjamin Berg
c7cab77fc1 usb-transfer: Work around libgusb cancellation issue
We have plenty of code paths where a transfer may be cancelled before it
is submitted. Unfortunately, libgusb up to and including version 0.3.6
are not handling that case correctly (due to libusb ignoring
cancellation on transfers that are not yet submitted).

Work around this, but do so in a somewhat lazy fashion that is not
entirely race free.

Closes: #306
2020-10-01 00:19:35 +02:00
Benjamin Berg
a63dcc96d5 virtual_image: Open a window for race conditions during open/close
Delay the open/close callbacks by 100ms so that we have a window of
opportunity for race conditions. This is needed to test certain
conditiosn in fprintd.
2020-09-29 12:02:19 +00:00
Benjamin Berg
fab349f356 Remove trailing \n where they are not necessary
Adding a trailing \n to g_message, g_debug, g_warning and g_error is not
neccessary, as a newline will be added automatically by the logging
infrastructure.
2020-09-29 10:42:03 +02:00
Benjamin Berg
62edf93958 tests: Add AES3500 test case
Thanks for Federico Cupellini for providing test samples.
2020-09-29 10:35:48 +02:00
Benjamin Berg
8c4ff253cb aes3k: Fix transfer error path and cancellable lifetime
The cancellable needs to be free'ed at deactivation. Also free it if we
run into a fatal error, which then in turn indicates that the device is
deactivated already.
2020-09-29 10:35:25 +02:00
Benjamin Berg
3ce6a15547 image-device: Don't deactivate if deactivation was already started
The test only prevent deactivation after it completed. Also guard
against trying to deactivate a second time.
2020-09-28 19:12:28 +02:00
Benjamin Berg
174aa2c091 Release 1.90.3 2020-09-14 14:23:45 +02:00
Benjamin Berg
9efe25b91c ci: Disable flatpak building for forks
Also move to use a single rules set for flatpak rather than only/except
rules.
2020-09-14 14:17:25 +02:00
Benjamin Berg
bcce8876e2 aes3k: Fix cancellation logic of aes3k driver
The change_state function is called synchronously from the
image_captured callback. This means that deactivation of the device
happens during the img_cb function, causing the USB transfer to be
re-registered even though the device is already deactivating.

There are various ways to fix this, but it makes sense to directly bind
the cancellation to the deactivation. So create a cancellable that we
cancel at deactivation time, and make sure we always deactivate by going
through cancellation.

closes: #306
2020-09-14 11:23:38 +00:00
boger.wang
3962372f47 tests: add identify test for driver goodixmoc
add identify ioctl file, modify custom.py add identify test
2020-09-14 09:55:55 +00:00
boger.wang
f67f61c638 goodixmoc: Add identify function
this device support verify and identify both, actually call the same
interface.
2020-09-14 09:55:55 +00:00
boger.wang
d5f7f4dfaa goodixmoc: Prevent incorrect firmware type running
only firmware type:APP can function well, if device flash in a factory
or test firmware, report a error tips user update firmware by fwupd
2020-09-14 09:55:55 +00:00
Benjamin Berg
ce6961d165 image-device: Fix cancellation documentation
Image devices are simply deactivated suddenly. As such, the cancellation
logic of FpDevice is not really useful there, but the documentation was
apparently accidentally copied unmodified.
2020-09-07 12:12:34 +02:00
Benjamin Berg
30e1a68344 ci: Build flatpak using GNOME runner and template 2020-09-03 09:42:28 +02:00
Benjamin Berg
5b087ed848 demo: Switch to use GNOME 3.36 runtime 2020-09-03 09:41:11 +02:00
Benjamin Berg
5d0481b031 context: Lower severity of warning if USB fails to initialise
This is unlikely to happen in a real world scenario and currently breaks
running the CI test (not the umockdev based ones) while building the
flatpak. Lower the severity to avoid aborting because there is a
warning.
2020-09-03 09:41:11 +02:00
Benjamin Berg
596d22a449 context: Fix invalid accesses to GUsbContext if USB is missing
When USB cannot be initialised (inside the flatpak build environment),
then we would have invalid accesses to the NULL pointer. Fix those.
2020-09-03 09:41:11 +02:00
boger.wang
c85f385191 tests: add test for goodixmoc driver 2020-09-01 16:39:06 +08:00
boger.wang
eb2aaaaa20 drivers: add goodix moc sensor driver support 2020-09-01 16:39:06 +08:00
boger.wang
e3c009c5b3 fp-device: add new type FpDeviceError FP_DEVICE_ERROR_DATA_DUPLICATE 2020-09-01 16:39:06 +08:00
Vincent Huang
a4f7293f32 synaptics: retry get version command once when receiving non-success
status
2020-08-21 17:42:00 +08:00
Vincent Huang
8b64312f4b synaptics: support sensors pid 0xe9 & 0xdf 2020-07-31 09:47:35 +08:00
Marco Trevisan (Treviño)
b7e27bfdc6 demo: Handle the non-imaging mode also if we get a non-supported error
Image devices may return a FP_DEVICE_ERROR_NOT_SUPPORTED error once capture
is already started, in such case handle the error going in non imaging mode
2020-06-17 14:16:34 +02:00
Marco Trevisan (Treviño)
37b19674f1 verify: Use fast matching callback also for non-storage devices 2020-06-17 14:14:50 +02:00
Marco Trevisan (Treviño)
a5f4ad507a img-capture: Exit with error if the device doesn't support capture 2020-06-17 14:12:43 +02:00
Marco Trevisan (Treviño)
1f96077e36 examples: Don't try to save an image from a print without it
A device may produce both prints with image data and not, so only do it
in the case the prints contains image data
2020-06-17 14:00:19 +02:00
Marco Trevisan (Treviño)
ed26976ac5 fpi-usb-transfer: Use the same values of libusb for transfer types 2020-06-16 22:50:47 +02:00
Marco Trevisan (Treviño)
e4d292b595 fp-image-device: Properly include fp-device.h 2020-06-16 22:49:38 +02:00
Marco Trevisan (Treviño)
c6b8430c72 fpi-print: Improve debug on print score 2020-06-16 22:49:18 +02:00
Marco Trevisan (Treviño)
cbf1dcca29 fpi-image-device: Improve debugging messages 2020-06-16 22:48:51 +02:00
Marco Trevisan (Treviño)
7f7d099ba0 fpi-device: Don't double check for cancellable value
g_cancellable_is_cancelled already handles properly null values
2020-06-16 22:43:18 +02:00
Marco Trevisan (Treviño)
b6f965c1d9 fpi-device: Use the current action string instead of value 2020-06-16 22:42:34 +02:00
Vasily Khoruzhick
fd2875aa3e examples: add img-capture example
This one can be useful in scripts, e.g. for building fingerprint
dataset.
2020-06-13 09:20:28 -07:00
Benjamin Berg
4b2816db64 Update for 1.90.2 2020-06-08 11:40:02 +02:00
Benjamin Berg
4af3e59174 uru4000: Always detect whether encryption is in use
This is based on the patch and observation from Bastien that some
URU4000B devices do not use encryption by default (it is a configuration
stored within the firmware). As such, it makes sense to always detect
whether encryption is in use by inspecting the image.

The encryption option would disable flipping of the image for the
URU400B device. Retain this behaviour for backward compatibility.
2020-06-05 15:53:53 +00:00
Benjamin Berg
24b1faffde upeksonly: Add a comment that multiple URBs are needed 2020-06-05 17:44:36 +02:00
Benjamin Berg
49983c8ee7 upeksonly: Fix memory leak of register data helper struct 2020-06-05 17:44:36 +02:00
Vasily Khoruzhick
d276c3489e upeksonly: Fix register write value
The value was set after the transfer was submitting, causing the value
to always be zero.
2020-06-05 17:44:36 +02:00
Benjamin Berg
3f51e6dcb6 upeksonly: Pass required user data to write_regs_cb
The user data for write_regs_cb needs to be set to wrdata. This was
simply missing, add the appropriate argument.
2020-06-05 17:44:36 +02:00
Benjamin Berg
b4dbbd667a upeksonly: Avoid reading beyond received packet boundary
The code would just read 4096 bytes from the packet, without checking
the size and neither setting short_is_error. It is not clear whether
packets from the device are always 4096 bytes or not. But the code
assume we always get a full line, so enforce that and use the actual
packet size otherwise.
2020-06-05 17:44:36 +02:00
Benjamin Berg
7d9245505f upeksonly: Remove callback support after killing transfers
This seems to have been unused even before the v2 port.
2020-06-05 17:44:36 +02:00
Benjamin Berg
570daf2321 upeksonly: Remove ABORT_SSM constant
This was not used. The old driver used this if creating a USB transfer
failed, however, we delay any such failures (which cannot really happen)
into the callback today, where the error is handled differently.
2020-06-05 15:40:17 +00:00
Benjamin Berg
60d0f84294 upeksonly: Fix creation of image transfers
The GPtrArray needs to be created at some point. Also, reference
counting was wrong as submitting the transfer sinks the ref, but we rely
on it surviving.

Note that we really should change this to only have one in-flight
transfer and starting a new one after it finishes.

Co-authored-by: Vasily Khoruzhick <anarsoul@gmail.com>
2020-06-05 15:40:17 +00:00
Benjamin Berg
6633025437 vfs301: Allow freeing of data by copying it
When sending static data, it would not be copied. The function that
sends it assumed that it should be free'ed though.

Fix this by simply always making a copy.
2020-06-05 15:17:42 +00:00
Benjamin Berg
40ed353666 elan: Only queue state changes once
The driver would warn about the fact that a state change is queued, but
still queue it a second time. This would result in deactivation to run
twice.

See: #216
2020-06-05 15:13:18 +00:00
Benjamin Berg
32bdd8d5c4 image: Fix reporting of retry on activation timeout
The image driver may still be deactivating when a new activation request
comes in. This is because of a hack to do early reporting, which is
technically not needed anymore.

Fix the immediate issue by properly reporting the retry case. The proper
fix is to only finish the previous operation after the device has been
deactivated.
2020-06-05 15:07:05 +00:00
Benjamin Berg
ec4fc9aec5 ci: Put coverage regexp into CI description
One can set it in the project, but that doesn't get copied to forks. And
that means the coverage information isn't printed in MRs sometimes.

Just add it into .gitlab-ci.yml so that it always works.
2020-06-05 15:03:38 +00:00
Benjamin Berg
390611d5c9 tests: Improve the instructions to create new umockdev captures
Thanks to Evgeny for much of the work and suggestions from Boger Wang.

Co-authored-by: Evgeny Gagauz <evgenij.gagauz@gmail.com>
2020-06-05 15:03:38 +00:00
Benjamin Berg
685052c605 tests: Add test for vfs0050 driver
This test is based on data captured by Evgeny.

Co-authored-by: Evgeny Gagauz <evgenij.gagauz@gmail.com>
2020-06-05 15:03:38 +00:00
Benjamin Berg
4b83f8bfd9 vfs0050: Accept zero bytes read instead of timeout for emulation
This allows us to replace non-emulateable timeout conditions into zero
byte replies in the recording.
2020-06-05 15:03:38 +00:00
Benjamin Berg
b137807420 upekts: Fix error reporting during verify
We delayed the wrong error types for the early reporting mechanism.
2020-06-05 14:48:57 +00:00
Benjamin Berg
0936fc3597 upekts: Fix error handling in verify_stop_deinit_cb
The error memory management was incorrect possibly causing double free's
and critical warnings.
2020-06-05 14:48:57 +00:00
Benjamin Berg
b7f436e8de upekts: Fix reading of fingerprint data for verification
the code tried to fetch the data from the device rather the print.
Obviously, this could never work.
2020-06-05 14:48:57 +00:00
Benjamin Berg
4f0b0fa526 tests: Ensure FpDevice checks enrolled prints for completeness
Enrolled prints need to have their type set. FpDevice should ensure that
is the case when a driver returns a print back.
2020-06-05 14:48:57 +00:00
Benjamin Berg
f0abefa9fa device: Ensure enrolled print as an appropriate type set
The driver might forget to set the type of the print. Catch that error a
bit earlier rather than failing when the API user tries to load it from
disk again.
2020-06-05 14:48:57 +00:00
Benjamin Berg
7f58556011 tests: An enrolled print needs to have a type set 2020-06-05 14:48:57 +00:00
Benjamin Berg
cecb01bcb9 upekts: Set the print type during enroll
The type of the print (RAW or NBIS) needs to be filled in by the driver.
For most drivers the image devices does this (NBIS), but the
corresponding call was missing in the upekts driver, rendering the
enrolled print unusable.
2020-06-05 14:48:57 +00:00
Benjamin Berg
b95402bc72 upekts: Fix memory leak and remove unneeded copy
__handle_incoming_msg would copy the payload of the message into a newly
created buffer just to destroy it again immediately after calling the
callback. Just reference the correct address inside the original package
instead.

Also, in one case the extra buffer was leaked afterwards.
2020-06-05 14:48:57 +00:00
Benjamin Berg
484743f652 upekts: Assert correct packet length in __handle_incoming_msg
The surrounding code already checks this and reads the correct amount.
Add an assert to ensure we really never do an out of bounds read.
2020-06-05 14:48:57 +00:00
Benjamin Berg
a5cfc1644f upekts: Fix 9 byte buffer overflow while reading extended data
The driver would correctly calculate the amount of extra space needed to
receive the whole packet. It would also request the correct number of
bytes for this transfer.

However, the reallocated buffer to hold this data was directly derived
from the expected payload size and did not include the overhead.

Make the code more explicit and get rid of the confusing
MAX_DATA_IN_READ_BUF define that hides details on buffer allocation
calculation from the code.
2020-06-05 14:48:57 +00:00
Benjamin Berg
b3565b83e1 upekts: Only release USB interface on exit
The device is already beeing de-initialised from the verify/enroll
commands. Trying it again will result in a timeout error as it is not
responding properly at that point.
2020-06-05 14:48:57 +00:00
Benjamin Berg
8f46de0a60 upekts: Fix reference counting in do_enroll_stop
The print passed to do_enroll_stop does not need to be ref'ed in the
function.
2020-06-05 14:48:57 +00:00
Benjamin Berg
b3c5fe4b82 upekts: Fix ownership transfer to fpi_device_enroll_complete
fpi_device_enroll_complete takes the ownership of the pointers. As such,
they need to be stolen from EnrollStopData.
2020-06-05 14:48:57 +00:00
Vasily Khoruzhick
4cf5f92a52 lib: re-add partial image flag
And activate perimeter points removal if this flag is set

This flag should be set for aes1610, aesx660, aes2501, aes2550
and upektc_img since these sensors may produce incomplete image.

Fixes: #142
2020-06-04 09:34:31 -07:00
Vasily Khoruzhick
297236b51a nbis: re-add step to remove perimeter points
This step helps to drop false minutiae for short sensors and
it was accidentally dropped during NBIS update. Re-add this step
and add a patch to update script to ensure that it's not dropped
during next update.

Fixes: 9fb789dc78 ("nbis: Update to NBIS 5.0.0")
2020-06-03 20:44:41 -07:00
Benjamin Berg
8626c64831 ci: Output diff of uncrustify check
Not having the diff is a bit painfull when the local version of
uncrustify differs from the one on the CI runner. So uncrustify in-place
and output the diff.
2020-06-02 11:56:19 +00:00
Benjamin Berg
e4f9935706 Uncrustify with newer version 2020-06-02 11:56:19 +00:00
Benjamin Berg
8ba29606bb Add MAINTAINERS file 2020-05-22 15:00:11 +02:00
Evgeny Gagauz
1b74813adf vfs0050: Stop capture after a timeout happens
If a transfer errors out then actual_length is negative. The only error
that is not caught is a timeout error, which should also result in the
SSM to move to the next state.
2020-05-13 09:51:23 +00:00
Benjamin Berg
07ff03970f libfprint: Fix a few issues with the documentation
This must have been broken all along. Get it into a better shape. Looks
like mostly cases of bad copy/paste.
2020-05-11 20:51:29 +02:00
Benjamin Berg
25ab4849a4 doc: Move sections and fix title for internal FpImageDevice
Otherwise the public API page (which is empty) is overwritten.
2020-05-11 20:51:29 +02:00
Benjamin Berg
840bcc77a5 ci: Export HTML documentation as artifacts
We build the HTML documentation. For feature branches, it is convenient
to be able to view the documentation easily. Expose them as artifacts
and add a link to the browser underneath the pipeline in the MR.

Unfortunately, it does not seem to be possible to link directly to the
HTML.
2020-05-11 20:51:29 +02:00
Benjamin Berg
a464f602ca nbis: Apply patch to fix scan-build warnings
Apply the newly added patch to the checkout.
2020-05-07 14:22:02 +00:00
Benjamin Berg
ad17011e68 nbis: Add patch to fix warnings reported by scan-build
This patch basically only adds annotations. None of the errors can be
hit in practice.
2020-05-07 14:22:02 +00:00
Benjamin Berg
744a71ce08 vfs301: Assert hex string has 2 or more characters
Otherwise static analysis thinks we may end up allocating a 0 byte
output buffer.
2020-05-07 14:22:02 +00:00
Benjamin Berg
422fc5facf tests: Add unused annotation
The variables exist for memory management but are unused otherwise. Tag
them as such.
2020-05-07 14:22:02 +00:00
Benjamin Berg
6d542edf8a test: Remove unused initialisers
Make the static analysis happy.
2020-05-07 14:22:02 +00:00
Benjamin Berg
8d4d56b1f1 tests: Annotate a few variables as unused
These solely exist for memory management, but the static analysis
complains.
2020-05-07 14:22:02 +00:00
Benjamin Berg
6e30a1ee45 print: Fix "possible leak" warning by moving initialization
For some reason static checkers report that the auto-free is not run if
the goto exists the loop. It seems to me like that should work fine, but
we can simply make the analysers happy by moving it.
2020-05-07 14:22:02 +00:00
Benjamin Berg
87c3b9c5ba upekts: Fix possible NULL pointer access
Reported by scan-build.
2020-05-07 14:22:02 +00:00
Benjamin Berg
0a08a6a7c0 ci: Run clang scan-build test as part of CI 2020-05-07 14:22:02 +00:00
Benjamin Berg
9db89e00d0 elan: Downgrade g_message to debug severity
See: #211
2020-05-07 13:55:35 +00:00
Benjamin Berg
3ad65b9589 vfs5011: Prevent too small images
We need more than 1 line for assembling, but in general, we should
have a reasonable amount of lines. So use the width in the hope we'll
get an image that is about square at least.

Closes: #135
2020-05-07 13:55:35 +00:00
Benjamin Berg
48aa6d0252 upekts: Fix regression during initialisation
The driver has two helper functions to format a command. In one case,
the function was accidentally changed during the port to the new driver
APIs.

See https://bugzilla.redhat.com/show_bug.cgi?id=1832229
2020-05-07 09:55:13 +02:00
Michal Prívozník
eefc954f91 context: Fix order of PID/VID in a debug message
When no driver is found for an USB device a debug message is
printed. However, it has PID and VID in wrong order - usually it
is Vendor ID which goes first. This is how 'lsusb' prints it.
Matching the order helps debugging.

Signed-off-by: Michal Privoznik <michal@privoznik.com>
2020-04-27 16:16:43 +02:00
Michal Prívozník
5bcf9ac008 print: Include "fpi-compact.h"
In the fpi_print_fill_from_user_id() GDate is defined using
g_autoptr(). However, this requires new enough GLib. For older
versions there's a definition provided locally in fpi-compact.h.
Include the file to fix build with older version of GLib.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2020-04-27 16:16:39 +02:00
Benjamin Berg
d2402309ee examples: Cancel verify operation on Ctrl+C
This makes it easier to do basic testing of cancellation paths in the
drivers.
2020-04-24 18:40:48 +00:00
Benjamin Berg
a651b65401 example: Cancel enroll operation on Ctrl+C
This makes it easier to do basic testing of cancellation paths in the
drivers.
2020-04-24 18:40:48 +00:00
Marco Trevisan (Treviño)
bc3f622b2a context: Factorize duplicated code 2020-04-24 20:21:01 +02:00
Marco Trevisan (Treviño)
5de49b33e6 image-device: Terminate the current action with error on print failure
If we fail when setting the scanned image to a print, we'll have a fatal
error, in such case we can terminate the current action and deactivate the
device.
2020-04-24 20:09:56 +02:00
Marco Trevisan (Treviño)
81e198c034 image: Return an error if we got an empty or NULL minutiae array
In some cases nbim's get_minutiae returns no minutiae without providing an
error code, and if this happens we would crash in the task callback function
as we would try to deference the data->minutiae pointer.

Related to: #251
2020-04-24 20:09:56 +02:00
Marco Trevisan (Treviño)
c0895a858d etes603: Return TOO_SHORT retry error for small images
If the image height is less than the sensor horizontal resolution, then
return a retry error rather than trying to submit the image for further
processing.

Related to: #251
2020-04-24 20:03:51 +02:00
Benjamin Berg
5d5995f201 image: Check for task success rather than just cancellation
The minutiae detection might fail and we must not copy any data at that
point. So check g_task_had_error to ensure that we only do so when the
task was successful.

Fixes: #251
2020-04-24 20:03:37 +02:00
Benjamin Berg
f71045b743 synaptics: Use the FpPrint ID generation functionality
As the functionality is now part of the internal FpPrint API, it makes
sene to use it.
2020-04-20 16:43:52 +02:00
Benjamin Berg
0274d0783b print: Add helpers to generate a unique print ID containing metadata
It makes sense to include this functionality in the core library as more
than one driver will be using it soon.
2020-04-20 16:43:52 +02:00
Bastien Nocera
5c5a4f6907 upekts: Fix memory leak
Don't allocate a new finger print structure,
the fpi_device_get_enroll_data() just below will overwrite it.
2020-04-15 15:55:39 +02:00
Bastien Nocera
5b6f5c9aad synaptics: Don't pass always-NULL value
No need to pass "error" to this report function, it will always be NULL.
2020-04-15 15:55:39 +02:00
Marco Trevisan (Treviño)
41e05b1133 test-fpi-device: Don't compare error pointers that have been cleared
During verify/identify complete we replace the error pointer that the driver
returned with another error we created, after clearing that one.

However, when we initialize a new error the compiler may reuse the same
allocation of the cleared one, and this might lead to a test failure.

So, don't be so fragile and ignore the pointer check
2020-04-15 14:19:53 +02:00
Bastien Nocera
579e01359b fp-print: Fix sign-compare warnings
libfprint/fp-print.c: In function ‘fp_print_equal’:
libfprint/fp-print.c:596:21: warning: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’} [-Wsign-compare]
  596 |       for (i = 0; i < self->prints->len; i++)
      |                     ^
libfprint/fp-print.c: In function ‘fp_print_serialize’:
libfprint/fp-print.c:667:21: warning: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’} [-Wsign-compare]
  667 |       for (i = 0; i < print->prints->len; i++)
      |                     ^
libfprint/fp-print.c: In function ‘fp_print_deserialize’:
libfprint/fp-print.c:823:21: warning: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘gsize’ {aka ‘long unsigned int’} [-Wsign-compare]
  823 |       for (i = 0; i < g_variant_n_children (prints); i++)
      |                     ^
2020-04-15 12:48:05 +02:00
Bastien Nocera
cc887c1a37 libfprint: Fix typos 2020-04-14 13:51:02 +02:00
Bastien Nocera
fdd2d6abf8 fprint-list: Fix typos in Copyright statements 2020-04-14 13:51:02 +02:00
Bastien Nocera
6bf29108a1 examples: Fix typo 2020-04-14 13:51:02 +02:00
Bastien Nocera
d0751ae06b tests/elan: Fix typos 2020-04-14 13:51:02 +02:00
Bastien Nocera
a218a5efdd virtual-image: Fix typo 2020-04-14 13:51:01 +02:00
Bastien Nocera
c6ae8e58a4 elan: Fix typo 2020-04-14 13:50:49 +02:00
Bastien Nocera
87c7894c28 synaptics: Fix typos 2020-04-14 13:47:48 +02:00
Marco Trevisan (Treviño)
e7ff4f705c tests: Import FPrint only during execution not when parsing
The unittest_parser script would try to import FPrint gi module, but it
would fail as per the fact that none is installed yet, so make sure that
we don't load any FPrint module until we try to actually run the tests
2020-03-27 21:19:09 +00:00
Marco Trevisan (Treviño)
c26588942a ci: Print coverage data once available so that gitlab can parse it 2020-03-27 00:00:07 +01:00
Laurent Bigonville
3d68cddfe7 Properly set the dependencies in the pkg-config file
The public API uses gio and gobject header, ensure that these are in the
list of Required pkg-config modules, otherwise they are added to
Required.private which is not OK.
2020-03-20 11:13:06 +00:00
Benjamin Berg
96fba323b9 meson: Fix linking issue of libfprint with newer gcc
It appears the order of linking is relevant in this case, change it to
fix some linking issues.

It may be that there are better solutions to this problem.
2020-03-20 12:05:14 +01:00
Benjamin Berg
bd4f118b5e ci: Update CI after the fdo template changes
The CI definition needs to be updated to work with the new fdo
templates.
2020-03-20 12:05:14 +01:00
Benjamin Berg
9d4b5ad682 Revert "ci: Fix CI syntax error"
This reverts commit bd500b2235.

The fix from the commit was not correct.

See https://gitlab.freedesktop.org/libfprint/libfprint/-/merge_requests/124
2020-03-20 12:05:08 +01:00
Benjamin Berg
ca788b6de2 Revert "ci: Fix unknown keys in CI"
This reverts commit ebe5cb58ba.

The fix from the commit was not correct.

See https://gitlab.freedesktop.org/libfprint/libfprint/-/merge_requests/124
2020-03-20 12:04:58 +01:00
Bastien Nocera
90ccf9a0af vfs0050: Fix incorrect destructor for GError 2020-03-13 10:23:52 +01:00
Bastien Nocera
2581f1aa32 aes1610: Fix memory leak
Fix small memory leak when reading some data that's not going to be
processed.
2020-03-13 10:23:52 +01:00
Bastien Nocera
ebe5cb58ba ci: Fix unknown keys in CI
Fix:
root config contains unknown keys: container_fedora_build
2020-03-13 10:14:59 +01:00
Bastien Nocera
bd500b2235 ci: Fix CI syntax error
Fix CI syntax error:
container_fedora_build: unknown keys in `extends` (.fedora@container-build)
Caused by changes in the wayland CI templates:
4a73f030d0
2020-03-13 10:08:58 +01:00
Bastien Nocera
8fa50d667c ci: s/flatpack/flatpak/
“Flatpack” is for IKEA furniture.
2020-03-13 10:08:58 +01:00
Marco Trevisan (Treviño)
2ae8b74e60 meson: Add fpi-compat.h to the private headers 2020-02-27 06:03:21 +01:00
Benjamin Berg
f4ec816a6b tests: Cast to correct pointer type rather than gpointer
This is just a small cleanup
2020-02-19 16:42:30 +01:00
Benjamin Berg
9e2a7235e3 tests: Fix reading of gboolean into pointer
Otherwise the payload will be lost later on when using GPOINTER_TO_INT
to cast it back.

See: #236
2020-02-19 16:42:30 +01:00
Benjamin Berg
66c9e4a829 Update for 1.90.1 release 2020-02-10 12:20:25 +01:00
Marco Trevisan (Treviño)
0bb8ad1313 tests: Make meson be aware of the single python unit tests
Scan for the unit tests in virtual-image suite and handle them individually
2020-02-10 11:41:40 +01:00
Marco Trevisan (Treviño)
6eb06697e9 tests/virtual-image: Use introspection names for errors 2020-02-10 11:41:40 +01:00
Marco Trevisan (Treviño)
355cae1bbd fp-device: Return error if trying to list a storage-less device
Devices with no storage don't allow listing prints, and if we try to do
that, we'd end up in trying to call a NULL function pointer, causing a crash

So always check if the device has storage before calling the list vfunc, and
if we fail, return an error.

Include an unit-test to verify this situation
2020-02-10 11:41:40 +01:00
Marco Trevisan (Treviño)
15a90eb451 meson: Use gnu99 as default C standard 2020-02-10 11:41:40 +01:00
Marco Trevisan (Treviño)
82ba69b1df meson: Depends on gusb 0.2.0, but only enable tests on 0.3.0 2020-02-10 11:41:40 +01:00
Marco Trevisan (Treviño)
ccd42bdece gitignore: Remove autotools ignores, add _build 2020-02-10 11:41:40 +01:00
Benjamin Berg
e19a1a6550 meson: Create include directories in build tree
Without this we get warnings like the following:

  cc1: warning: .../_build/libfprint/nbis/libfprint-include: No such file or directory [-Wmissing-include-dirs]
2020-02-10 11:41:40 +01:00
Benjamin Berg
5ac770c614 tests: Return skip error if import fails
Rather than backtracing, just print the exception and return a skip
error if the import fails.
2020-02-10 11:41:40 +01:00
Marco Trevisan (Treviño)
5faf8498d9 compat: Use new GDate autoptr and define if needed 2020-02-10 11:41:40 +01:00
Marco Trevisan (Treviño)
cfbd5d27b7 comapt: Add FpDeviceClass compatibility autoptr and use it 2020-02-10 11:41:37 +01:00
Benjamin Berg
169ca1ba77 compat: Add compatibility defines for older GLib
We are already using a number of defines and autoptrs from newer GLib
releases. Add the appropriate compatibility defines rather than removing
the corresponding code.

Closes: #222
2020-02-10 11:40:40 +01:00
Marco Trevisan (Treviño)
bb08d2e3c2 ci: Use extends to define extra libfprint variables
This allows to merge the values when included instead of replacing the whole
variables stanza.
2020-02-05 20:00:07 +01:00
Marco Trevisan (Treviño)
ca5143ffa5 ci: Exclude flatpak job from the schedules 2020-02-05 19:37:42 +01:00
Marco Trevisan (Treviño)
7eb10178b8 ci: Use a template to define libfprint dependencies
This allows to share the configuration with fprintd
2020-02-05 18:04:00 +01:00
Marco Trevisan (Treviño)
2c9e252ca4 meson: Use versioned name for autosuspend udev rules
Given distros may have the previous value around, let's rename this too
2020-01-23 17:25:38 +01:00
Marco Trevisan (Treviño)
23fab3a20a Change SONAME and all the library paths to libfprint-2
To avoid conflicts with previous libfprint version and make sure that the
target version is the correct one (plus to allow parallel install in some
distros), let's use a versioned naming for the library keeping the abi
version in sync.

Fixes #223
2020-01-23 17:09:58 +01:00
Marco Trevisan (Treviño)
24e9363a46 doc/meson: Use ignore_headers instead of scan_args 2020-01-23 17:00:08 +01:00
Marco Trevisan (Treviño)
a12d316aa4 meson: Use project name for log domain 2020-01-23 17:00:04 +01:00
Benjamin Berg
88461d53ec upekts: Fix use-after-free in an error condition
The callback function would continue processing even after having failed
the SSM already. This causes further invalid operations on the SSM.

This error was found using a coverity scan.
2020-01-21 14:19:33 +00:00
Marco Trevisan (Treviño)
3b47113122 synaptics: Immediately complete verification if finger removed
When quickly scanning a finger with the synaptic driver, it may wait forever
for finger removal even if this has already happened.

In fact we don't take care of the finger status when reporting the
verification.

To avoid this, add a function that delays the completion of the verification
until the finger removal if the finger is on sensor, otherwise it just
performs it.

Fixes #228
2020-01-20 18:24:16 +00:00
Marco Trevisan (Treviño)
7a7bec5a80 synaptics: Report a verify complete error on unexpected result
Also remove the unneeded verify report with match failure
2020-01-20 18:24:16 +00:00
Marco Trevisan (Treviño)
8be861b876 synaptics: Remove unneeded complete error/data parameters
Remove the never-used cmd_complete_data value and the repetition of
cmd_complete_error.

In fact now as per the fpi_device_verify_report() usage, we already pass
the match information to the device, such as the error and it will be
they will be used on completion if needed.

This allows to simplify cmd_ssm_done() as well.
2020-01-20 18:24:16 +00:00
Marco Trevisan (Treviño)
8893840ffa synaptics: Always report verify state early on non-match
In some cases we want to complete the verification after that the finger has
been removed, but we still need to promptly report the match state otherwise
fpi-device will complain about, and will eventually cause a match error
instead that reporting a non-match:

  synaptics: Finger is now on the sensor
  synaptics: Received message with 0 sequence number 0x91, ignoring!
  synaptics: interrupt transfer done
  synaptics: delaying match failure until after finger removal!
  synaptics: interrupt transfer done
  device: Driver reported successful verify complete but did not report the
          result earlier. Reporting error instead
  libfprint: Failed to verify print: An unspecified error occured!

Fixes #227
2020-01-20 18:24:16 +00:00
Marco Trevisan (Treviño)
4d6a7ec09d synaptics: Really check if a print is device database
Fix a typo causing the not-in-database print error to be fired, actually
checking the response result.
2020-01-20 18:24:16 +00:00
Benjamin Berg
b9e546f05b tests: Add missing NULL terminator to g_object_new
The g_object_new call had a NULL argument for a property. This meant
that the compiler could not warn about the lack of NULL termination for
the argument list.

Add the missing NULL termination.
2020-01-20 13:34:31 +01:00
Marco Trevisan (Treviño)
05df5e2822 test-fpi-device: Verify driver enroll errors 2020-01-17 19:44:52 +01:00
Marco Trevisan (Treviño)
58a9214610 test-fpi-device: Add tests for verify/identify warnings 2020-01-17 19:44:52 +01:00
Marco Trevisan (Treviño)
cdcc476325 examples/verify: Prompt match/no-match report in callback
Promptly show the match/no-match result in the match callback instead of
waiting the verification process to be finished.

Also exit in case of an hard error, while permit to try again in case of a
retry error.
2020-01-17 19:44:52 +01:00
Marco Trevisan (Treviño)
a87e9c546f test-fpi-device: Verify device action error operations 2020-01-17 19:44:52 +01:00
Marco Trevisan (Treviño)
ad514c3775 test-fpi-device: Fix file description 2020-01-17 19:44:52 +01:00
Marco Trevisan (Treviño)
3c5b7f8ea6 test-fpi-device: Add more probe tests with errors 2020-01-17 19:44:52 +01:00
Marco Trevisan (Treviño)
b09df0e40a test-fpi-device: Add tests for error message creation 2020-01-17 19:44:50 +01:00
Marco Trevisan (Treviño)
027ac8d843 fpi-device: Only mark a device as closed if the operation succeeded
We may fail during the close phase, in such case the device should not be
marked as closed.
2020-01-17 19:03:33 +01:00
Marco Trevisan (Treviño)
b3a4c2cf9a fpi-device: Improve logging and testing on report/complete 2020-01-17 19:03:33 +01:00
Marco Trevisan (Treviño)
9f3272f296 test-fpi-device: Use smart pointer to handle CB data 2020-01-17 18:43:01 +01:00
Marco Trevisan (Treviño)
456522397a verify: Add match callback report 2020-01-16 20:24:42 +01:00
Marco Trevisan (Treviño)
0889ec20a8 fp-device: Remove confusing success parameter on FpMatchCb
This was added as alias to the error check, but given we're passing to the
callback both the error and the match itself, we can just avoid adding an
extra parameter that could be confusing (as may imply that the matching
happened).

Also clarify the documentation and ensure that the match value is properly
set in tests.
2020-01-16 20:24:42 +01:00
Marco Trevisan (Treviño)
30c783cbeb examples: add FP_COMPONENT definitions 2020-01-16 14:37:07 +01:00
Marco Trevisan (Treviño)
078cea1709 verify: Add a verify_quit convenience function
It ends the loop if the device is not open, otherwise closes it and ends
the loop once done.
2020-01-16 14:36:41 +01:00
Marco Trevisan (Treviño)
bc8a5859e3 verify: Always close an open device before quitting the loop 2020-01-16 14:19:08 +01:00
Benjamin Berg
05bc2e1c80 image-device: Avoid invalid state transition on cancellation
Fixes: #226
2020-01-15 14:57:51 +01:00
Benjamin Berg
29a13a9b4a tests: Add error reporting tests based on virtual driver
We were not testing the image device error reporting functions yet
inside libfprint (fprintd already had such tests). Add tests to make
sure we catch errors earlier.
2020-01-13 17:58:41 +01:00
Benjamin Berg
54286c7603 image-device: Handle retry error propagation correctly
With the early reporting mechanism the retry error propagation needs to
be adjusted. Do the appropriate changes.
2020-01-13 17:58:41 +01:00
Benjamin Berg
db14995c31 image-device: Set cancelling when errors are reported
Allow the AWAIT_FINGER_ON to deactivation state transition after errors
are reported.
2020-01-13 17:58:41 +01:00
Benjamin Berg
7aaeec3d6a tests: Check that missing identify/verify result returns error 2020-01-13 14:37:39 +01:00
Benjamin Berg
0b8e2d6074 tests: Add tests for the verify/identify retry cases 2020-01-13 14:37:39 +01:00
Benjamin Berg
0c582230f3 tests: Add tests for the early reporting mechanism
This adds tests to check whether the verify/identify early reporting
mechanism is behaving correctly.
2020-01-13 14:37:39 +01:00
Benjamin Berg
829fb9f873 device: Add early match reporting to sync API
This makes the sync/async APIs more similar again. It also simplifies
testing as otherwise we would need the async methods for some tests.
2020-01-13 14:37:39 +01:00
Benjamin Berg
4d5c34e11a Introduce an early reporting mechanism for verify and match
It is a good idea to report match results early, to e.g. log in a user
immediately even if more device interaction is needed. Add new _full
variants for the verify/identify functions, with a corresponding
callback. Also move driver result reporting into new
fpi_device_{identify,verify}_report functions and remove the reporting
from the fpi_device_{identify,verify}_complete calls.

Basic updates to code is done in places. Only the upekts driver is
actually modified from a behaviour point of view. The image driver code
should be restructured quite a bit to split the reporting and only
report completion after device deactivation. This should simplifiy the
code quite a bit again.
2020-01-13 14:37:39 +01:00
Benjamin Berg
8292c449f7 device: Better define ownership passing for results
Some things were odd with regard to the ownership of passed objects. Try
to make things sane overall, in particular with the possible floating
FpPrint reference.
2020-01-13 13:45:05 +01:00
Benjamin Berg
3f3d4559b4 upekts: Remove unused argument from deinitsm_new 2020-01-09 10:45:54 +01:00
Benjamin Berg
fcdf1a1ff1 device: Fix potential memory leak of progress_cb user data
The progress report user data free func was not assigned and therefore
never called. Add the missing assign, potentially fixing memory leaks
(mostly relevant for bindings).
2020-01-09 10:45:54 +01:00
Benjamin Berg
ba07c74006 tests: Always add dummy skipping tests
Just hiding tests that cannot be run does not seem like the best idea.
So add dummy tests that are skipped, to denote that we could test more
than we actually do (even if it is just for drivers that are not
enabled).
2020-01-08 18:40:41 +01:00
Benjamin Berg
6716359fe8 tests: Add more notes about umockdev recording creation
umockdev recordings are usually not usable as is. Add some notes to the
README to summarise what kind of changes may be required.
2020-01-08 18:14:06 +01:00
Benjamin Berg
c27a4faeca elan: Add umockdev based test
Unfortunately, the timeout handling cannot be simulated properly. This
also adds a workaround in the driver to not consider it a protocol error
if this happens.
2020-01-08 18:14:06 +01:00
Benjamin Berg
8992e559f8 image-device: Fix enroll continuation after retry error
Continuing an enroll was broken in case of a retry error. Explicitly add
code to wait for the finger to go OFF after a retry error, and ensure
that the enroll will continue once that has happened.
2020-01-08 18:14:06 +01:00
Benjamin Berg
6c6df626c8 image-device: Fix reading default values from the class
We cannot copy information from the class in the _init routine, instead,
this needs to be done in _constructed. Move the relevant code into a new
_constructed function to fix importing the bz3_threshold override from
drivers.

Fixes: #206
2020-01-08 18:14:06 +01:00
Benjamin Berg
87dee93633 examples: Do not re-prompt the finger when repeating verification
Let's assume the user will want to re-scan the same finger rather than
being prompted again to change it.
2020-01-08 18:14:06 +01:00
Benjamin Berg
516c1593bb examples: Continue verification when return is pressed
The message says [Y/n], but will not do Y by default. Fix this.
2020-01-08 18:14:06 +01:00
Benjamin Berg
dcc7e6de90 examples: Save image even on match failure
Move the image saving out, so that it is always done when an image is
available. This allows viewing the image that corresponds to a match
failure.
2020-01-08 18:14:06 +01:00
Benjamin Berg
3c6ba0b678 storage: Do not free image data owned by FpPrint
The data returned by fp_print_get_image is owned by the FpPrint and
should not be free'ed.
2020-01-08 18:14:06 +01:00
Benjamin Berg
4db1b84c7a examples: Do not free data returned by g_variant_get_fixed_array
This data is owned by the variant, so do not free it.
2020-01-08 18:14:06 +01:00
Benjamin Berg
36108f9f82 examples: Fix possible use-after-free in storage code
The variant may need the buffer, so we should only free the buffer
together with the variant.
2020-01-08 18:14:06 +01:00
Benjamin Berg
19f239ce61 tests: Add some frame assembly unit tests 2020-01-08 18:14:06 +01:00
Benjamin Berg
f91e5310bb tests: Set MESON_SOURCE_ROOT to source root not build root 2020-01-02 13:46:46 +01:00
Benjamin Berg
0d604fa34e fpi-assembling: Fix offsets to be relative to the previous frame
The offset stored for a frame was not always relative to the previous
frame. This was the case for reverse movement, but for forwrad movement
the offset was the one to the next frame.

Make the offset handling consistent and alwasy store the offset to the
previous frame. Also update the frame assembling code to add the offset
before blitting the frame (i.e. making it relative to the previous frame
there too).

Note that fpi_assemble_lines already made the assumption that this was
the case as it forced the offset for the first frame to be zero. As
such, the code was inconsistent.

This could affect the AES drivers slightly as they use hardware reported
values which might not adhere to these assumptions.
2020-01-02 13:46:46 +01:00
Benjamin Berg
d9bcf9b9cc fpi-assembling: Accept error of zero
Rather than discarding a zero error, check that the constraints are
sane. This way a perfect match is possible.
2020-01-02 13:46:46 +01:00
Marco Trevisan (Treviño)
b8e558452a fp-print: Add FP_FINGER_IS_VALID
This is coming directly from fprintd, but being something generic is better
to have it insinde libfprint itself.
2019-12-19 14:20:00 +01:00
Marco Trevisan (Treviño)
c9e1a7f283 examples: Iterate through fingers via first/last refs 2019-12-18 12:23:14 +00:00
Marco Trevisan (Treviño)
c5aedc9970 fp-print: Add aliases for First and Last finger in our order
We might want to iterate through the supported fingers values, to do that we
were hardcoding FP_FINGER_LEFT_THUMB and FP_FINGER_RIGHT_LITTLE as first and
last fingers.

Not that we'd ever get more fingers (unless some weird radiation would do
the job), but it's logically nicer to read than random hardcoded values.
2019-12-18 12:23:14 +00:00
Benjamin Berg
5b17eda011 Prefix internal properties/signals with fpi- and annotate them
We prefixed them with fp- which is not as obvious as fpi-. Also,
explicitly mark them as private and to be skipped in the GObject
Introspection annotatinos.

Warning: FPrint: (Signal)fp-image-device-state-changed: argument object: Unresolved type: 'FpiImageDeviceState'
2019-12-18 12:25:06 +01:00
Marco Trevisan (Treviño)
022b4a75b1 meson: Bump dependency on 0.49.0
We're using some new features, and we may use more in future so better to
bump the version to the minimum required than look back given we're still
unstable.

Fixes #204
2019-12-17 20:44:37 +01:00
Marco Trevisan (Treviño)
bfc75de778 libfprint: Make sure we install fp-enums.h in the right folder
Since we were not explictly setting the install_dir, it was endind up in
$PREFIX/include by default, while we use our project name as subfolder.
2019-12-17 15:42:56 +01:00
Marco Trevisan (Treviño)
f3f768e738 meson: Fix syntax for fpi_enums generation call 2019-12-17 15:42:32 +01:00
Marco Trevisan (Treviño)
dbb26c5ade meson: Define enum dependency and ensure we generate them before using
Avoid setting the headers as sources everywhere, but instead use a dependency
to manage the headers creation in time
2019-12-17 14:38:20 +01:00
Marco Trevisan (Treviño)
0566f82219 tests: Add a reference to the enrolled print before returning it 2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
c8e1269f61 cleanup: Use FPI prefix for all the internal enum types 2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
2158c5e2d1 cleanup: Use #pragma once everywhere
Remove legacy header guards, and use compiler newer features.
2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
10945f8546 cleanup: Remove fp_internal.h and update drivers_api.h
Remove the uneeded internal API, as we can now include each header directly
if needed, while move the assembling stuff to the drivers API.
2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
806ad10673 meson: Add fp-image-device to public headers 2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
4562f9dae3 meson: Use soversion everywhere
Don't repeat the 2 version number hard-coding it, so we can easily track
updates
2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
c57defda92 meson: Use more meson's project_name()
Not that libfprint is long to write, but in case we'll ever change the
basename, we do it once.
2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
c806993cb9 meson: Don't install fpi-enums 2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
95cb62fd3b meson: No need to redefine default pkgconfig install dir
This value would be the default anyways
2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
d255a91e97 meson: List deps in multiple lines, to have better diffs on changes 2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
9ebb3fd231 meson: Parse all private headers
So we don't have to list each one in case we add new enums.
Also, as per previous commit we can reorder them alphabetically.
2019-12-17 14:38:19 +01:00
Marco Trevisan (Treviño)
68b5c5d98f fpi-ssm, fpi-usb-transfer: Use fwd-declarations to avoid headers dependencies
Don't make headers inclusions dependent on each others, given that FpiSsm
depends on FpiUsbTransfer and vice-versa, so fix the dependency cycle by
using forwarded declarations.
2019-12-17 14:38:19 +01:00
Benjamin Berg
2af0531994 tests: Fix stack corruption in FpiSsm test
Using a function with a void return value for a g_timeout_add is not a
good idea after all.
2019-12-17 14:32:59 +01:00
Benjamin Berg
bfd68bbc01 meson: Add missing dependency on fp-enum.h for private library
The private library needs to indirectly include fp-enum.h. This
dependency was not listed anyway, resulting in a race condition during
the build process.
2019-12-16 11:42:51 +01:00
Benjamin Berg
9c806e60f4 elan: Do not leak converted frames
The elan driver converts frames into a different format. These frames
are only needed to assemable the image and should be free'ed afterwards.

Fixes: #213
2019-12-16 10:20:49 +00:00
Benjamin Berg
113bef8f3f examples: Fix double device closing in manage-prints
The manage-prints command would close the device twice when the user
hits an invalid number or 'n' and no prints need to be deleted.

Simply remove the erroneous call to fix the issue.
2019-12-16 10:20:49 +00:00
Benjamin Berg
1d1c39c234 tests: Ensure objects are free'ed at the end of tests
The objects may not be garbage collected otherwise.
2019-12-16 10:20:49 +00:00
Benjamin Berg
4948a85e97 synaptics: Use local variable rather than re-fetching usb device 2019-12-16 10:20:49 +00:00
Marco Trevisan (Treviño)
7e2db8e988 driver_ids.h: Remove the legacy ID file
This is not used anymore by drivers as they all use GType's, so remove it
and any (dead) reference to it.
2019-12-14 17:20:47 +01:00
Marco Trevisan (Treviño)
24d388f923 meson: Split single-line dependencies to reduce the diff on changes
Make it more readable and in case we add something else, it's easier to track
2019-12-14 17:20:47 +01:00
Marco Trevisan (Treviño)
af42b3e468 ci: Increase the timeout multiplier for tests
Change the FEDORA_TAG to the current date in order to rebuild the image
2019-12-14 17:20:47 +01:00
Marco Trevisan (Treviño)
edb09463f4 ci: Save coverage reports when running tests 2019-12-14 17:20:47 +01:00
Marco Trevisan (Treviño)
42b1deaeea tests: Add unit tests for fpi-ssm
Verify that the state machine actions are done as we expected, being the
main tool for drivers, better to check that is done as we expect.
2019-12-14 17:20:47 +01:00
Marco Trevisan (Treviño)
fe828d0bb2 fpi-ssm: Clear delayed actions for parent and child on subssm start
While timeout was already cleared for parent, we didn't properly delete the
cancellable.

Although we'd warn anyways when starting the SSM, is still better to clear
any delayed action also for the sub-SSM
2019-12-14 17:20:47 +01:00
Marco Trevisan (Treviño)
cf5473a55c fpi-ssm: Make clear that the completed callback owns the error 2019-12-14 17:20:47 +01:00
Marco Trevisan (Treviño)
0471edbf10 fpi-ssm: Add debug message when a delayed state change is cancelled 2019-12-14 17:20:46 +01:00
Marco Trevisan (Treviño)
299a797423 fpi-ssm: Bug on wrong state passed to jump_to_state_delayed
While remove the checks that are already part of the common function
fpi_ssm_set_delayed_action_timeout().
2019-12-14 17:20:46 +01:00
Marco Trevisan (Treviño)
c594863639 fpi-ssm: Define autoptr cleanup function 2019-12-14 17:20:46 +01:00
Marco Trevisan (Treviño)
3bb1840750 fpi-ssm: Use same argument names of header file
So we can mute a Gtk-doc parser warning
2019-12-14 17:20:46 +01:00
Marco Trevisan (Treviño)
f31b8483d4 tests: Add fpi device tests
Verify drivers operations simulating a fake driver to check that the lib
interaction is correct.
2019-12-14 17:20:46 +01:00
Marco Trevisan (Treviño)
324258bc8c tests/meson: Support unit-tests non depending on virtual driver
Since tests depending on the fake device don't depend on virtual-image
driver anymore, let's change the way we organize the things, by putting
everything in the test lib, but enabling unit-tests depending on what they
depend on.
2019-12-14 17:20:46 +01:00
Marco Trevisan (Treviño)
f578ebe82d test-device-fake: Add fake test driver to verify fpi functions 2019-12-14 17:20:46 +01:00
Marco Trevisan (Treviño)
98fa6efce3 fpi-device: Clarify ownership of parameters for progress call 2019-12-14 17:20:46 +01:00
Marco Trevisan (Treviño)
c7b7f78273 fp-device: Call identify device class method on identification
Identify on device was broken because we were calling verify device method
on devices instead of the right one.

Thank you unit tests! :)
2019-12-14 17:20:46 +01:00
Marco Trevisan (Treviño)
b4c3756ab0 tests: Add fp-device basic unit tests
Use the virtual image device as base for now, while the new setup allows
to create easily fake device drivers without including the driver in
libfprint itself and test all the fpi_device functionalities.
2019-12-14 17:20:45 +01:00
Marco Trevisan (Treviño)
da46f53e82 tests: Add basic unit tests for fp-context
Link the tests with the private library using an utils library that will
be useful to share other tests functions
2019-12-14 17:20:45 +01:00
Marco Trevisan (Treviño)
eeddd8c7bc fp-context, tools: Use auto-ptr to handle GTypeClass ownership
This also fixes a small leak we might have if reffing a type that was not a
virtual one.
2019-12-14 17:20:45 +01:00
Marco Trevisan (Treviño)
43a8c909bf fp-context: Use an env to define a whitelist of drivers to enable
This avoids that we pick unwanted drivers when running the tests in a
machine that has some supported device attached.
2019-12-14 17:20:45 +01:00
Marco Trevisan (Treviño)
09576e5209 fpi-context: Make fpi_get_driver_types to return an array
No need to create one all the times and the fill it with what we need.
2019-12-14 16:57:11 +01:00
Marco Trevisan (Treviño)
8184e33dd6 meson: Fix syntax in the auto-generated fpi-drivers file
Better to be nice everywhere :)
2019-12-14 16:56:56 +01:00
Marco Trevisan (Treviño)
6f2b1f97da fprint: Move drivers to private internal library
This allows us to finally remove fpi_get_driver_types from the exported list
of symbols.
2019-12-12 19:03:57 +01:00
Marco Trevisan (Treviño)
c3bf6fe863 meson: Rely on libfprint dependency to get root_include
No need to redefine it in all our tools
2019-12-12 18:55:08 +01:00
Marco Trevisan (Treviño)
5bed81025e meson: Use files to track the map file presence 2019-12-11 20:07:13 +01:00
Marco Trevisan (Treviño)
d7100e41df fp-image, fp-print: Move private methods to own code units 2019-12-11 20:07:13 +01:00
Marco Trevisan (Treviño)
ae7021e529 fp-image-device: Move fpi code into its own unit that can be compiled a part
In order to be able to test the private device code (used by drivers) we
need to have that split a part in a different .c file so that we can compile
it alone and link with it both the shared library and the test executables.

Redefine fp_image_device_get_instance_private for private usage, not to move
the private struct as part of FpDevice.
2019-12-11 20:07:12 +01:00
Marco Trevisan (Treviño)
d9de941a47 fp-device: Move fpi code into its own unit that can be compiled a part
In order to be able to test the private device code (used by drivers) we
need to have that split a part in a different .c file so that we can compile
it alone and link with it both the shared library and the test executables.

Redefine fp_device_get_instance_private for private usage, not to move
the private struct as part of FpDevice.
2019-12-11 20:07:12 +01:00
Marco Trevisan (Treviño)
7114d97f25 libfprint: Introduce libfprint_private static library
Split the library into a private part with all the symbols that we can use
for unit-test all the fpi functions.
2019-12-10 20:19:55 +01:00
Marco Trevisan (Treviño)
92a5278a74 fp-device: Add a "open" property and method to check its state 2019-12-10 20:19:55 +01:00
Marco Trevisan (Treviño)
d01bb41b7c tests/meson: Set the typelib env var only if we have introspection 2019-12-10 19:20:21 +01:00
Marco Trevisan (Treviño)
ad88a5a78f docs: Don't ignore the deprecated API_EXPORTED definition 2019-12-10 19:20:21 +01:00
Marco Trevisan (Treviño)
ff0107fc0a fp-device: Use different pointers for device handlers
A Fp-device use an union to track the handle to the lower-level device, and
the value depends on the object type.

So in case of using a virtual device, the priv->usb_device location matches
the priv->virtual_env string location, and thus we'd end up unreffing a
string location as it was a GObject, while we'd leak the string.

To avoid such errors, instead of just checking the device type when we
finalize the device, let's just use different pointers to avoid other
possible clashes.
2019-12-10 19:20:21 +01:00
Vincent Huang
107fdfde32 synaptics: Fix problem after match is failed
This fixes the the problem that the sensor becomes unresponsive after
pressing the wrong fingerprint. We fix the problem by making sure that
the non-match report is delayed until the finger is removed. With this
we cannot run into the situation that the next match request fails
immediately as the finger is still present.

Fixes: #208
2019-12-10 09:38:54 +01:00
Marco Trevisan (Treviño)
bb0ef04b85 ci: Partially hardcode the fedora image path 2019-12-05 17:31:46 +01:00
Benjamin Berg
0a475196e0 ci: Use CI generated build image 2019-12-05 14:38:46 +01:00
Benjamin Berg
1dee7985b9 ci: Remove Dockerfile as it is replaced by new logic 2019-12-05 14:38:46 +01:00
Benjamin Berg
a1a3933191 ci: Add fedora image builder target for schedule 2019-12-05 14:38:46 +01:00
Benjamin Berg
9c8360ad67 ci: Do not run usual targets from a scheduled job
This is in preparation to building docker images automatically.
2019-12-05 14:14:13 +01:00
Benjamin Berg
0c7d2d8ecd ci: Define a variable for the fedora docker image 2019-12-05 13:56:12 +01:00
Benjamin Berg
6209b22e3b print: Fix match error propagation
The FPI_MATCH_ERROR constant was set to 0, however it is propagated to
the task completion using g_task_propagate_int. As g_task_propagate_int
will always return -1 on error, we either need to add an explicit -1
check or we just need to match the semantics.

Change the constant to -1, also rearange FP_MATCH_SUCCESS so that it
does not end up being 0.
2019-12-05 11:46:11 +00:00
149 changed files with 16476 additions and 3500 deletions

23
.gitignore vendored
View File

@@ -1,24 +1,3 @@
ltmain.sh
missing
stamp-h1
libtool
*.la
*.lo
*.o *.o
*.swp *.swp
Makefile _build
Makefile.in
config.h*
aclocal.m4
autom4te.cache
config.guess
config.log
config.status
config.sub
configure
depcomp
install-sh
.deps
.libs
compile
ChangeLog

View File

@@ -1,13 +1,25 @@
image: registry.freedesktop.org/libfprint/libfprint/master:v1 include:
- local: '.gitlab-ci/libfprint-templates.yaml'
- project: 'wayland/ci-templates'
ref: master
file: '/templates/fedora.yml'
- remote: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/master/flatpak/flatpak_ci_initiative.yml'
variables:
extends: .libfprint_common_variables
FDO_DISTRIBUTION_TAG: latest
FDO_DISTRIBUTION_VERSION: rawhide
FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG"
BUNDLE: "org.freedesktop.libfprint.Demo.flatpak"
LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546"
stages: stages:
- check-source - check-source
- build - build
- test - test
- flatpack - flatpak
variables: image: "$FEDORA_IMAGE"
BUNDLE: "org.freedesktop.libfprint.Demo.flatpak"
LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546"
.build_one_driver_template: &build_one_driver .build_one_driver_template: &build_one_driver
script: script:
@@ -29,75 +41,104 @@ variables:
build: build:
stage: build stage: build
except:
variables:
- $CI_PIPELINE_SOURCE == "schedule"
variables: variables:
driver: virtual_image driver: virtual_image
<<: *build_one_driver <<: *build_one_driver
<<: *build <<: *build
# <<: *check_abi # <<: *check_abi
artifacts:
expose_as: "HTML Documentation"
paths:
- _build/doc/html/
expire_in: 1 week
test: test:
stage: test stage: test
except:
variables:
- $CI_PIPELINE_SOURCE == "schedule"
script: script:
- meson --werror -Ddrivers=all . _build - meson --werror -Ddrivers=all -Db_coverage=true . _build
- ninja -C _build - ninja -C _build
- meson test -C _build --verbose --no-stdsplit - meson test -C _build --verbose --no-stdsplit --timeout-multiplier 3
- ninja -C _build coverage
- cat _build/meson-logs/coverage.txt
artifacts:
paths:
- _build/meson-logs
expire_in: 1 week
coverage: '/^TOTAL.*\s+(\d+\%)$/'
test_valgrind: test_valgrind:
stage: test stage: test
except:
variables:
- $CI_PIPELINE_SOURCE == "schedule"
script: script:
- meson -Ddrivers=all . _build - meson -Ddrivers=all . _build
- ninja -C _build - ninja -C _build
- meson test -C _build --verbose --no-stdsplit --setup=valgrind - meson test -C _build --verbose --no-stdsplit --setup=valgrind
test_indent: test_scan_build:
stage: check-source stage: test
except:
variables:
- $CI_PIPELINE_SOURCE == "schedule"
allow_failure: true
script: script:
- scripts/uncrustify.sh --check - meson -Ddrivers=all . _build
# This is ugly, the wrapper disables the malloc checker
.flatpak_script_template: &flatpak_script - SCANBUILD=$CI_PROJECT_DIR/.gitlab-ci/scan-build ninja -C _build scan-build
script: # Check that the directory is empty
- flatpak-builder --stop-at=${FLATPAK_MODULE} app ${MANIFEST_PATH} - "! ls -A _build/meson-logs/scanbuild | grep -q ."
# Make sure to keep this in sync with the Flatpak manifest, all arguments
# are passed except the config-args because we build it ourselves
- flatpak build app meson --prefix=/app --libdir=lib ${MESON_ARGS} _build
- flatpak build app ninja -C _build install
- flatpak build app rm -rf /app/include/ /app/lib/pkgconfig/
- flatpak-builder --finish-only --repo=repo app ${MANIFEST_PATH}
# Generate a Flatpak bundle
- flatpak build-bundle repo ${BUNDLE} --runtime-repo=${RUNTIME_REPO} ${DBUS_ID}
.flatpak_artifacts_template: &flatpak_artifacts
artifacts: artifacts:
paths: paths:
- ${BUNDLE} - _build/meson-logs
when: always expire_in: 1 week
expire_in: 30 days
.flatpak_template: &flatpak test_indent:
<<: *flatpak_script stage: check-source
<<: *flatpak_artifacts except:
variables:
- $CI_PIPELINE_SOURCE == "schedule"
script:
- scripts/uncrustify.sh
- git diff
- "! git status -s | grep -q ."
.flatpak_master_template: &flatpak_master flatpak:
image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:3.32 stage: flatpak
stage: flatpack extends: .flatpak
image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:3.36
variables: variables:
MANIFEST_PATH: "demo/org.freedesktop.libfprint.Demo.json" MANIFEST_PATH: "demo/org.freedesktop.libfprint.Demo.json"
# From demo/org.freedesktop.libfprint.Demo.json
MESON_ARGS: "-Dudev_rules=false -Dx11-examples=false -Dgtk-examples=true"
FLATPAK_MODULE: "libfprint" FLATPAK_MODULE: "libfprint"
DBUS_ID: "org.freedesktop.libfprint.Demo" APP_ID: "org.freedesktop.libfprint.Demo"
<<: *flatpak rules:
- if: '$CI_PROJECT_PATH != "libfprint/libfprint"'
flatpak-auto master: when: never
<<: *flatpak_master - if: '$CI_PIPELINE_SOURCE == "schedule"'
when: never
- if: '$CI_COMMIT_BRANCH == "master"'
when: always when: always
only: - if: '$CI_COMMIT_TAG'
- tags when: always
- master # For any other (commit), allow manual run.
# This excludes MRs which would create a duplicate pipeline
flatpak-manual master: - if: '$CI_COMMIT_BRANCH'
<<: *flatpak_master
when: manual when: manual
except: allow_failure: true
- tags
- master # CONTAINERS creation stage
container_fedora_build:
extends: .fdo.container-build@fedora
only:
variables:
- $CI_PIPELINE_SOURCE == "schedule" && $CRON_TASK == "BUILD_CI_IMAGES"
variables:
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
# a list of packages to install
FDO_DISTRIBUTION_PACKAGES: $LIBFPRINT_DEPENDENCIES

View File

@@ -1,36 +0,0 @@
# Rebuild and push with
#
# cd .gitlab-ci/
# docker build --no-cache -t registry.freedesktop.org/libfprint/libfprint/master:v1 .
# docker push registry.freedesktop.org/libfprint/libfprint/master:v1
#
FROM fedora:rawhide
RUN dnf -y update && dnf -y upgrade && \
dnf -y install \
doxygen \
flatpak-builder \
gcc \
gcc-c++ \
git \
glib2-devel \
glibc-devel \
gobject-introspection-devel \
gtk-doc \
gtk3-devel \
libabigail \
libgusb-devel \
libX11-devel \
libXv-devel \
meson \
nss-devel \
pixman-devel \
python3-cairo \
python3-gobject \
systemd \
umockdev \
uncrustify \
valgrind \
&& \
dnf clean all

View File

@@ -0,0 +1,27 @@
.libfprint_common_variables:
LIBFPRINT_DEPENDENCIES:
doxygen
flatpak-builder
gcc
gcc-c++
gcovr
git
glib2-devel
glibc-devel
gobject-introspection-devel
gtk-doc
gtk3-devel
libabigail
libgusb-devel
libX11-devel
libXv-devel
meson
nss-devel
pixman-devel
python3-cairo
python3-gobject
systemd
umockdev
uncrustify
valgrind
clang-analyzer

4
.gitlab-ci/scan-build Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
# This wrapper just disables the malloc checker
exec /usr/bin/scan-build -disable-checker unix.Malloc "$@"

13
MAINTAINERS Normal file
View File

@@ -0,0 +1,13 @@
Current maintainers of libfprint are:
* Benjamin Berg <bberg@redhat.com>
* Marco Trevisan (Treviño) <mail@3v1n0.net>
Many drivers are not actively maintained and may not be fully functional.
We are happy to receive contributions, but the support we can give is
limitted unfortunately. For many drivers we may not even have test devices.
Maintained drivers are:
* synaptics:
Contributed and maintained by Synaptics Inc.
Contact: Vincent Huang <vincent.huang@tw.synaptics.com>

95
NEWS
View File

@@ -1,6 +1,101 @@
This file lists notable changes in each release. For the full history of all This file lists notable changes in each release. For the full history of all
changes, see ChangeLog. changes, see ChangeLog.
2020-12-01: v1.90.5 release
The 1.90.4 release caused a major regression, as it included a USB hub in
UDEV the autosupend rule list.
Highlights:
* Remove USB hub from udev autosupend rules
* synaptics: Add PID 0x00c9 which is used in some HP laptops
2020-11-27: v1.90.4 release
This release contains a number of important bugfixes. On the feature side,
the USB hotplug support was improved. A lot of drivers received fixes and
improvements.
Highlights:
* Work around GUsb cancellation issue
* Redefine internal image device state machine for more robustness
* Add public finger-status reporting to FpDevice
* Rework device removal API to be convenient (#330)
* Enable powersave for unsupported USB devices
* Improvements to examples
* synaptics: Support identify operation
* synaptics: Fix possible crash when the interrupt transfer is resubmitted
* synaptics: Add support for PIDs 0x00f9, 0x00fc and 0x00c2
* elan: Add PID 0x0c4d to supported device list
* aes3k: Fix driver and add CI test (#306)
* uru4000: Fix reference counting of image transfer
* vfs301: Fix driver and add CI test (#320)
2020-06-08: v1.90.3 release
This release mostly contains support for a number of new match-on-chip
devices. Most notable is the addition of the new goodixmoc driver.
Currently the driver has the small caveat that we have no strategy to
garbage collect old prints yet (a simple strategy could be implemented
in fprintd).
Highlights:
* New goodixmoc driver supporting Goodix USB devices:
27C6:5840
27C6:6496
27C6:60A2
* Newly added support for Synaptics device:
06CB:00E9
06CB:00DF
* Fixed an issue with Synaptics devices sometimes not working at boot
* Fix issue with aes3k driver (#306)
2020-06-08: v1.90.2 release
This release contains a large amount of bug and regression fixes. These
are not listed explicitly, but affect the majority of drivers.
Highlights:
* A patch for nbis required for some sensors was accidentally dropped in
an earlier release. Users of these sensors/drivers (aes1610, aes2501,
aes2550, aes1660, aes2660, elan, upektc_img) need to re-enroll (#142).
2019-11-20: v1.90.1 release
This release fixes a lot of the regressions introduced in 1.90.0. Please note
that both the driver and external APIs have changed, as both the verify and
the identify functions now have early reporting mechanisms.
The soname for the library, as well as a number of file locations have also
changed. While this allows installation in parallel with the 1.0 version of
libfprint, we recommend installing only one, and migrating from version 1.0 to
version 2.0 alongside its main consumer (fprintd).
Only major changes are listed below. A lot of other cleanup work and small
fixes have also been merged.
* Library:
- Add support to run tests in gdb/valgrind
- Allow testing on all architectures
- Avoid image device AWAIT_FINGER_ON to deactivate state transitions
- Fix verify/identify error propagation to library user
- Correctly read image device information from class data
- Continue enroll after an image driver reported a retry error
- Change external API to allow reporting match results early
- A lot of new unit tests and integration tests have been added
* Drivers API
- Support variadic arguments in error functions
- Various re-definitions of ownership handling
- Add convenience API to change state after a timeout
- Add unit tests for all the drivers API
* Drivers:
- elan: Ensure correct deactivation of device
- uru4000: Fix IRQ handler registration and internal state handling
- uru4000: Fix control transfer request type
- synaptics: Ensure errors are only reported after finger removal
2019-11-20: v1.90.0 release 2019-11-20: v1.90.0 release
This release updates the core of the library to use GLib routines and Gio This release updates the core of the library to use GLib routines and Gio

View File

@@ -241,6 +241,8 @@ dev_capture_start_cb (FpDevice *dev,
if (error->domain == FP_DEVICE_RETRY || if (error->domain == FP_DEVICE_RETRY ||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
libfprint_demo_set_mode (win, RETRY_MODE); libfprint_demo_set_mode (win, RETRY_MODE);
else if (g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED))
libfprint_demo_set_mode (win, NOIMAGING_MODE);
else else
libfprint_demo_set_mode (win, ERROR_MODE); libfprint_demo_set_mode (win, ERROR_MODE);
return; return;

View File

@@ -9,9 +9,9 @@ datadir = join_paths(prefix, get_option('datadir'))
executable('gtk-libfprint-test', executable('gtk-libfprint-test',
[ 'gtk-libfprint-test.c', gtk_test_resources ], [ 'gtk-libfprint-test.c', gtk_test_resources ],
dependencies: [ libfprint_dep, gtk_dep ], dependencies: [
include_directories: [ gtk_dep,
root_inc, libfprint_dep,
], ],
c_args: '-DPACKAGE_VERSION="' + meson.project_version() + '"', c_args: '-DPACKAGE_VERSION="' + meson.project_version() + '"',
install: true, install: true,

View File

@@ -1,7 +1,7 @@
{ {
"app-id": "org.freedesktop.libfprint.Demo", "app-id": "org.freedesktop.libfprint.Demo",
"runtime": "org.gnome.Platform", "runtime": "org.gnome.Platform",
"runtime-version": "3.32", "runtime-version": "3.36",
"sdk": "org.gnome.Sdk", "sdk": "org.gnome.Sdk",
"command": "gtk-libfprint-test", "command": "gtk-libfprint-test",
"finish-args": [ "finish-args": [

View File

@@ -26,6 +26,7 @@ FpDeviceError
fp_device_retry_quark fp_device_retry_quark
fp_device_error_quark fp_device_error_quark
FpEnrollProgress FpEnrollProgress
FpMatchCb
fp_device_get_driver fp_device_get_driver
fp_device_get_device_id fp_device_get_device_id
fp_device_get_name fp_device_get_name
@@ -129,7 +130,7 @@ fpi_get_driver_types
<FILE>fpi-device</FILE> <FILE>fpi-device</FILE>
FpDeviceClass FpDeviceClass
FpTimeoutFunc FpTimeoutFunc
FpDeviceAction FpiDeviceAction
FpIdEntry FpIdEntry
fpi_device_get_usb_device fpi_device_get_usb_device
fpi_device_get_virtual_env fpi_device_get_virtual_env
@@ -159,6 +160,8 @@ fpi_device_identify_complete
fpi_device_capture_complete fpi_device_capture_complete
fpi_device_delete_complete fpi_device_delete_complete
fpi_device_enroll_progress fpi_device_enroll_progress
fpi_device_verify_report
fpi_device_identify_report
</SECTION> </SECTION>
<SECTION> <SECTION>
@@ -172,8 +175,8 @@ fpi_image_resize
<SECTION> <SECTION>
<FILE>fpi-image-device</FILE> <FILE>fpi-image-device</FILE>
<TITLE>FpImageDevice</TITLE> <TITLE>Internal FpImageDevice</TITLE>
FpImageDeviceState FpiImageDeviceState
FpImageDeviceClass FpImageDeviceClass
fpi_image_device_session_error fpi_image_device_session_error
fpi_image_device_open_complete fpi_image_device_open_complete
@@ -197,13 +200,15 @@ BUG
<SECTION> <SECTION>
<FILE>fpi-print</FILE> <FILE>fpi-print</FILE>
FpPrintType FpiPrintType
FpiMatchResult FpiMatchResult
fpi_print_add_print fpi_print_add_print
fpi_print_set_type fpi_print_set_type
fpi_print_set_device_stored fpi_print_set_device_stored
fpi_print_add_from_image fpi_print_add_from_image
fpi_print_bz3_match fpi_print_bz3_match
fpi_print_generate_user_id
fpi_print_fill_from_user_id
</SECTION> </SECTION>
<SECTION> <SECTION>

View File

@@ -90,7 +90,7 @@ fp_image_get_width
<TITLE>Base class for image devices</TITLE> <TITLE>Base class for image devices</TITLE>
FpImageDevice FpImageDevice
FpImageDeviceClass FpImageDeviceClass
FpImageDeviceState FpiImageDeviceState
</SECTION> </SECTION>
<SECTION> <SECTION>
@@ -114,5 +114,3 @@ FpUsbTransferCallback
FP_USB_ENDPOINT_IN FP_USB_ENDPOINT_IN
FP_USB_ENDPOINT_OUT FP_USB_ENDPOINT_OUT
</SECTION> </SECTION>

View File

@@ -4,7 +4,6 @@ private_headers = [
'config.h', 'config.h',
'nbis-helpers.h', 'nbis-helpers.h',
'fprint.h', 'fprint.h',
'fp_internal.h',
# Subdirectories to ignore # Subdirectories to ignore
'drivers', 'drivers',
@@ -24,17 +23,13 @@ glib_prefix = dependency('glib-2.0').get_pkgconfig_variable('prefix')
glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html') glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html')
docpath = join_paths(get_option('datadir'), 'gtk-doc', 'html') docpath = join_paths(get_option('datadir'), 'gtk-doc', 'html')
gnome.gtkdoc('libfprint', gnome.gtkdoc(versioned_libname,
main_xml: 'libfprint-docs.xml', main_xml: 'libfprint-docs.xml',
src_dir: join_paths(meson.source_root(), 'libfprint'), src_dir: join_paths(meson.source_root(), 'libfprint'),
dependencies: libfprint_dep, dependencies: libfprint_dep,
content_files: content_files, content_files: content_files,
expand_content_files: expand_content_files, expand_content_files: expand_content_files,
scan_args: [ ignore_headers: private_headers,
#'--rebuild-sections',
'--ignore-decorators=API_EXPORTED',
'--ignore-headers=' + ' '.join(private_headers),
],
fixxref_args: [ fixxref_args: [
'--html-dir=@0@'.format(docpath), '--html-dir=@0@'.format(docpath),
'--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')), '--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')),

View File

@@ -1,8 +1,8 @@
ent_conf = configuration_data() ent_conf = configuration_data()
ent_conf.set('PACKAGE', 'libfprint') ent_conf.set('PACKAGE', versioned_libname)
ent_conf.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/libfprint/libfprint/issues') ent_conf.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/libfprint/libfprint/issues')
ent_conf.set('PACKAGE_NAME', 'libfprint') ent_conf.set('PACKAGE_NAME', versioned_libname)
ent_conf.set('PACKAGE_STRING', 'libfprint') ent_conf.set('PACKAGE_STRING', versioned_libname)
ent_conf.set('PACKAGE_TARNAME', 'libfprint-' + meson.project_version()) ent_conf.set('PACKAGE_TARNAME', 'libfprint-' + meson.project_version())
ent_conf.set('PACKAGE_URL', 'https://fprint.freedesktop.org/') ent_conf.set('PACKAGE_URL', 'https://fprint.freedesktop.org/')
ent_conf.set('PACKAGE_VERSION', meson.project_version()) ent_conf.set('PACKAGE_VERSION', meson.project_version())

View File

@@ -1,6 +1,6 @@
/* /*
* Example fingerprint enrollment program * Example fingerprint enrollment program
* Enrolls your choosen finger and saves the print to disk * Enrolls your chosen finger and saves the print to disk
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com> * Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
* *
@@ -19,8 +19,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#define FP_COMPONENT "example-enroll"
#include <stdio.h> #include <stdio.h>
#include <libfprint/fprint.h> #include <libfprint/fprint.h>
#include <glib-unix.h>
#include "storage.h" #include "storage.h"
#include "utilities.h" #include "utilities.h"
@@ -28,6 +31,8 @@
typedef struct _EnrollData typedef struct _EnrollData
{ {
GMainLoop *loop; GMainLoop *loop;
GCancellable *cancellable;
unsigned int sigint_handler;
FpFinger finger; FpFinger finger;
int ret_value; int ret_value;
} EnrollData; } EnrollData;
@@ -35,6 +40,8 @@ typedef struct _EnrollData
static void static void
enroll_data_free (EnrollData *enroll_data) enroll_data_free (EnrollData *enroll_data)
{ {
g_clear_handle_id (&enroll_data->sigint_handler, g_source_remove);
g_clear_object (&enroll_data->cancellable);
g_main_loop_unref (enroll_data->loop); g_main_loop_unref (enroll_data->loop);
g_free (enroll_data); g_free (enroll_data);
} }
@@ -50,7 +57,7 @@ on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data)
fp_device_close_finish (dev, res, &error); fp_device_close_finish (dev, res, &error);
if (error) if (error)
g_warning ("Failed closing device %s\n", error->message); g_warning ("Failed closing device %s", error->message);
g_main_loop_quit (enroll_data->loop); g_main_loop_quit (enroll_data->loop);
} }
@@ -69,9 +76,14 @@ on_enroll_completed (FpDevice *dev, GAsyncResult *res, void *user_data)
{ {
enroll_data->ret_value = EXIT_SUCCESS; enroll_data->ret_value = EXIT_SUCCESS;
if (!fp_device_has_storage (dev)) if (fp_device_has_storage (dev))
{ g_debug ("Device has storage, saving a print reference locally");
g_debug ("Device has not storage, saving locally"); else
g_debug ("Device has not storage, saving print only locally");
/* Even if the device has storage, it may not be able to save all the
* metadata that the print contains, so we can always save a local copy
* containing the handle to the device print */
int r = print_data_save (print, enroll_data->finger); int r = print_data_save (print, enroll_data->finger);
if (r < 0) if (r < 0)
{ {
@@ -79,10 +91,9 @@ on_enroll_completed (FpDevice *dev, GAsyncResult *res, void *user_data)
enroll_data->ret_value = EXIT_FAILURE; enroll_data->ret_value = EXIT_FAILURE;
} }
} }
}
else else
{ {
g_warning ("Enroll failed with error %s\n", error->message); g_warning ("Enroll failed with error %s", error->message);
} }
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed,
@@ -105,7 +116,7 @@ on_enroll_progress (FpDevice *device,
return; return;
} }
if (fp_device_supports_capture (device) && if (print && fp_print_get_image (print) &&
print_image_save (print, "enrolled.pgm")) print_image_save (print, "enrolled.pgm"))
printf ("Wrote scanned image to enrolled.pgm\n"); printf ("Wrote scanned image to enrolled.pgm\n");
@@ -135,11 +146,22 @@ on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
printf ("Scan your finger now.\n"); printf ("Scan your finger now.\n");
print_template = print_create_template (dev, enroll_data->finger); print_template = print_create_template (dev, enroll_data->finger);
fp_device_enroll (dev, print_template, NULL, on_enroll_progress, NULL, fp_device_enroll (dev, print_template, enroll_data->cancellable,
NULL, (GAsyncReadyCallback) on_enroll_completed, on_enroll_progress, NULL, NULL,
(GAsyncReadyCallback) on_enroll_completed,
enroll_data); enroll_data);
} }
static gboolean
sigint_cb (void *user_data)
{
EnrollData *enroll_data = user_data;
g_cancellable_cancel (enroll_data->cancellable);
return G_SOURCE_CONTINUE;
}
int int
main (void) main (void)
{ {
@@ -186,8 +208,15 @@ main (void)
enroll_data->finger = finger; enroll_data->finger = finger;
enroll_data->ret_value = EXIT_FAILURE; enroll_data->ret_value = EXIT_FAILURE;
enroll_data->loop = g_main_loop_new (NULL, FALSE); enroll_data->loop = g_main_loop_new (NULL, FALSE);
enroll_data->cancellable = g_cancellable_new ();
enroll_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH,
SIGINT,
sigint_cb,
enroll_data,
NULL);
fp_device_open (dev, NULL, (GAsyncReadyCallback) on_device_opened, fp_device_open (dev, enroll_data->cancellable,
(GAsyncReadyCallback) on_device_opened,
enroll_data); enroll_data);
g_main_loop_run (enroll_data->loop); g_main_loop_run (enroll_data->loop);

319
examples/identify.c Normal file
View File

@@ -0,0 +1,319 @@
/*
* Example fingerprint verification program, which verifies the
* finger which has been previously enrolled to disk.
* Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "example-identify"
#include <stdio.h>
#include <libfprint/fprint.h>
#include <glib-unix.h>
#include "storage.h"
#include "utilities.h"
typedef struct _IdentifyData
{
GMainLoop *loop;
GCancellable *cancellable;
unsigned int sigint_handler;
int ret_value;
} IdentifyData;
static void
identify_data_free (IdentifyData *identify_data)
{
g_clear_handle_id (&identify_data->sigint_handler, g_source_remove);
g_clear_object (&identify_data->cancellable);
g_main_loop_unref (identify_data->loop);
g_free (identify_data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (IdentifyData, identify_data_free)
static void
on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data)
{
IdentifyData *identify_data = user_data;
g_autoptr(GError) error = NULL;
fp_device_close_finish (dev, res, &error);
if (error)
g_warning ("Failed closing device %s", error->message);
g_main_loop_quit (identify_data->loop);
}
static void
identify_quit (FpDevice *dev,
IdentifyData *identify_data)
{
if (!fp_device_is_open (dev))
{
g_main_loop_quit (identify_data->loop);
return;
}
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, identify_data);
}
static void start_identification (FpDevice *dev,
IdentifyData *identify_data);
static void
on_identify_completed (FpDevice *dev, GAsyncResult *res, void *user_data)
{
IdentifyData *identify_data = user_data;
g_autoptr(FpPrint) print = NULL;
g_autoptr(FpPrint) match = NULL;
g_autoptr(GError) error = NULL;
char buffer[20];
if (!fp_device_identify_finish (dev, res, &match, &print, &error))
{
g_warning ("Failed to identify print: %s", error->message);
identify_data->ret_value = EXIT_FAILURE;
if (error->domain != FP_DEVICE_RETRY)
{
identify_quit (dev, identify_data);
return;
}
}
g_print ("Identify again? [Y/n]? ");
if (fgets (buffer, sizeof (buffer), stdin) &&
(buffer[0] == 'Y' || buffer[0] == 'y' || buffer[0] == '\n'))
{
start_identification (dev, identify_data);
return;
}
identify_quit (dev, identify_data);
}
static FpPrint *
get_stored_print (FpDevice *dev, FpPrint *print)
{
g_autoptr(GPtrArray) gallery = gallery_data_load (dev);
guint index;
if (g_ptr_array_find_with_equal_func (gallery, print,
(GEqualFunc) fp_print_equal,
&index))
return g_object_ref (g_ptr_array_index (gallery, index));
return NULL;
}
static void
on_identify_cb (FpDevice *dev, FpPrint *match, FpPrint *print,
gpointer user_data, GError *error)
{
IdentifyData *identify_data = user_data;
if (error)
{
g_warning ("Identify report: No finger matched, retry error reported: %s",
error->message);
return;
}
if (print && fp_print_get_image (print) &&
print_image_save (print, "identify.pgm"))
g_print ("Print image saved as identify.pgm\n");
if (match)
{
g_autoptr(FpPrint) matched_print = g_object_ref (match);
char date_str[128] = {};
identify_data->ret_value = EXIT_SUCCESS;
if (fp_print_get_device_stored (match))
{
FpPrint *stored_print = get_stored_print (dev, match);
if (stored_print)
matched_print = g_steal_pointer (&stored_print);
}
if (fp_print_get_enroll_date (matched_print))
g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0",
fp_print_get_enroll_date (matched_print));
else
strcpy (date_str, "<unknown>");
g_debug ("Identify report: device %s matched finger %s successfully "
"with print '%s', enrolled on date %s by user %s",
fp_device_get_name (dev),
finger_to_string (fp_print_get_finger (matched_print)),
fp_print_get_description (matched_print), date_str,
fp_print_get_username (matched_print));
g_print ("IDENTIFIED!\n");
}
else
{
g_debug ("Identification report: No finger matched");
g_print ("NOT IDENTIFIED!\n");
}
}
static void
on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data)
{
IdentifyData *identify_data = user_data;
g_autoptr(GPtrArray) gallery = NULL;
g_autoptr(GError) error = NULL;
gallery = fp_device_list_prints_finish (dev, res, &error);
if (!error)
{
if (!gallery->len)
{
g_warning ("No prints saved on device");
identify_quit (dev, identify_data);
return;
}
g_debug ("Identifying with %u prints in gallery", gallery->len);
fp_device_identify (dev, gallery, identify_data->cancellable,
on_identify_cb, identify_data, NULL,
(GAsyncReadyCallback) on_identify_completed,
identify_data);
}
else
{
g_warning ("Loading prints failed with error %s", error->message);
identify_quit (dev, identify_data);
}
}
static void
start_identification (FpDevice *dev, IdentifyData *identify_data)
{
if (fp_device_has_storage (dev))
{
g_print ("Creating finger template, using device storage...\n");
fp_device_list_prints (dev, NULL,
(GAsyncReadyCallback) on_list_completed,
identify_data);
}
else
{
g_autoptr(GPtrArray) gallery = gallery_data_load (dev);
if (!gallery)
{
identify_quit (dev, identify_data);
return;
}
g_print ("Gallery loaded. Time to identify!\n");
fp_device_identify (dev, gallery, identify_data->cancellable,
on_identify_cb, identify_data, NULL,
(GAsyncReadyCallback) on_identify_completed,
identify_data);
}
}
static void
on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
{
IdentifyData *identify_data = user_data;
g_autoptr(GError) error = NULL;
if (!fp_device_open_finish (dev, res, &error))
{
g_warning ("Failed to open device: %s", error->message);
identify_quit (dev, identify_data);
return;
}
g_print ("Opened device. ");
start_identification (dev, identify_data);
}
static gboolean
sigint_cb (void *user_data)
{
IdentifyData *identify_data = user_data;
g_cancellable_cancel (identify_data->cancellable);
return G_SOURCE_CONTINUE;
}
int
main (void)
{
g_autoptr(FpContext) ctx = NULL;
g_autoptr(IdentifyData) identify_data = NULL;
GPtrArray *devices;
FpDevice *dev;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
ctx = fp_context_new ();
devices = fp_context_get_devices (ctx);
if (!devices)
{
g_warning ("Impossible to get devices");
return EXIT_FAILURE;
}
dev = discover_device (devices);
if (!dev)
{
g_warning ("No devices detected.");
return EXIT_FAILURE;
}
if (!fp_device_supports_identify (dev))
{
g_warning ("Device %s does not support identification.",
fp_device_get_name (dev));
return EXIT_FAILURE;
}
identify_data = g_new0 (IdentifyData, 1);
identify_data->ret_value = EXIT_FAILURE;
identify_data->loop = g_main_loop_new (NULL, FALSE);
identify_data->cancellable = g_cancellable_new ();
identify_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH,
SIGINT,
sigint_cb,
identify_data,
NULL);
fp_device_open (dev, identify_data->cancellable,
(GAsyncReadyCallback) on_device_opened,
identify_data);
g_main_loop_run (identify_data->loop);
return identify_data->ret_value;
}

192
examples/img-capture.c Normal file
View File

@@ -0,0 +1,192 @@
/*
* Example fingerprint verification program, which verifies the
* finger which has been previously enrolled to disk.
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
* Copyright (C) 2020 Vasily Khoruzhick <anarsoul@gmail.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 "example-capture"
#include <stdio.h>
#include <libfprint/fprint.h>
#include <glib-unix.h>
#include "storage.h"
#include "utilities.h"
typedef struct CaptureData
{
GMainLoop *loop;
GCancellable *cancellable;
unsigned int sigint_handler;
int ret_value;
const char *filename;
} CaptureData;
static void
capture_data_free (CaptureData *capture_data)
{
g_clear_handle_id (&capture_data->sigint_handler, g_source_remove);
g_clear_object (&capture_data->cancellable);
g_main_loop_unref (capture_data->loop);
g_free (capture_data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (CaptureData, capture_data_free)
static void
on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data)
{
CaptureData *capture_data = user_data;
g_autoptr(GError) error = NULL;
fp_device_close_finish (dev, res, &error);
if (error)
g_warning ("Failed closing device %s", error->message);
g_main_loop_quit (capture_data->loop);
}
static void
capture_quit (FpDevice *dev,
CaptureData *capture_data)
{
if (!fp_device_is_open (dev))
{
g_main_loop_quit (capture_data->loop);
return;
}
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, capture_data);
}
static void
dev_capture_cb (FpDevice *dev,
GAsyncResult *res,
void *user_data)
{
g_autoptr(GError) error = NULL;
CaptureData *capture_data = user_data;
FpImage *image = NULL;
g_clear_object (&capture_data->cancellable);
image = fp_device_capture_finish (dev, res, &error);
if (!image)
{
g_warning ("Error capturing data: %s", error->message);
capture_quit (dev, capture_data);
return;
}
save_image_to_pgm (image, capture_data->filename);
capture_quit (dev, capture_data);
}
static void
start_capture (FpDevice *dev, CaptureData *capture_data)
{
fp_device_capture (dev, TRUE, capture_data->cancellable, (GAsyncReadyCallback) dev_capture_cb, capture_data);
}
static void
on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
{
CaptureData *capture_data = user_data;
g_autoptr(GError) error = NULL;
if (!fp_device_open_finish (dev, res, &error))
{
g_warning ("Failed to open device: %s", error->message);
capture_quit (dev, capture_data);
return;
}
g_print ("Opened device. ");
start_capture (dev, capture_data);
}
static gboolean
sigint_cb (void *user_data)
{
CaptureData *capture_data = user_data;
g_cancellable_cancel (capture_data->cancellable);
return G_SOURCE_CONTINUE;
}
int
main (int argc, const char *argv[])
{
g_autoptr(FpContext) ctx = NULL;
g_autoptr(CaptureData) capture_data = NULL;
GPtrArray *devices;
FpDevice *dev;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
ctx = fp_context_new ();
devices = fp_context_get_devices (ctx);
if (!devices)
{
g_warning ("Impossible to get devices");
return EXIT_FAILURE;
}
dev = discover_device (devices);
if (!dev)
{
g_warning ("No devices detected.");
return EXIT_FAILURE;
}
if (!fp_device_supports_capture (dev))
{
g_warning ("Device %s doesn't support capture",
fp_device_get_name (dev));
return EXIT_FAILURE;
}
capture_data = g_new0 (CaptureData, 1);
capture_data->ret_value = EXIT_FAILURE;
capture_data->loop = g_main_loop_new (NULL, FALSE);
capture_data->cancellable = g_cancellable_new ();
capture_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH,
SIGINT,
sigint_cb,
capture_data,
NULL);
if (argc == 2)
capture_data->filename = argv[1];
else
capture_data->filename = "finger.pgm";
fp_device_open (dev, capture_data->cancellable,
(GAsyncReadyCallback) on_device_opened,
capture_data);
g_main_loop_run (capture_data->loop);
return capture_data->ret_value;
}

View File

@@ -18,6 +18,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#define FP_COMPONENT "example-mange-prints"
#include <stdio.h> #include <stdio.h>
#include <libfprint/fprint.h> #include <libfprint/fprint.h>
@@ -52,7 +54,7 @@ on_device_closed (FpDevice *dev,
fp_device_close_finish (dev, res, &error); fp_device_close_finish (dev, res, &error);
if (error) if (error)
g_warning ("Failed closing device %s\n", error->message); g_warning ("Failed closing device %s", error->message);
g_main_loop_quit (list_data->loop); g_main_loop_quit (list_data->loop);
} }
@@ -84,7 +86,7 @@ delete_next_print (FpDevice *dev,
g_assert_nonnull (list_data->to_delete); g_assert_nonnull (list_data->to_delete);
print = list_data->to_delete->data; print = list_data->to_delete->data;
g_debug ("Deleting print %s\n", fp_print_get_description (print)); g_debug ("Deleting print %s", fp_print_get_description (print));
fp_device_delete_print (dev, print, NULL, fp_device_delete_print (dev, print, NULL,
(GAsyncReadyCallback) on_print_deleted, list_data); (GAsyncReadyCallback) on_print_deleted, list_data);
} }
@@ -197,9 +199,6 @@ on_list_completed (FpDevice *dev,
list_data->ret_value = EXIT_SUCCESS; list_data->ret_value = EXIT_SUCCESS;
else else
g_warning ("Invalid finger selected"); g_warning ("Invalid finger selected");
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed,
list_data);
} }
} }

View File

@@ -1,17 +1,23 @@
examples = [ 'enroll', 'verify', 'manage-prints' ] examples = [
'enroll',
'identify',
'img-capture',
'manage-prints',
'verify',
]
foreach example: examples foreach example: examples
executable(example, executable(example,
[ example + '.c', 'storage.c', 'utilities.c' ], [ example + '.c', 'storage.c', 'utilities.c' ],
dependencies: [ libfprint_dep, glib_dep ], dependencies: [
include_directories: [ libfprint_dep,
root_inc, glib_dep,
]) ],
)
endforeach endforeach
executable('cpp-test', executable('cpp-test',
'cpp-test.cpp', 'cpp-test.cpp',
dependencies: libfprint_dep, dependencies: libfprint_dep,
include_directories: [ )
root_inc,
])

View File

@@ -2,7 +2,7 @@
* Trivial storage driver for example programs * Trivial storage driver for example programs
* *
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com> * Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com> * Copyright (C) 2019-2020 Marco Trevisan <marco.trevisan@canonical.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@@ -19,7 +19,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#define FP_COMPONENT "example-storage"
#include <libfprint/fprint.h> #include <libfprint/fprint.h>
#include <libfprint/fpi-compat.h>
#include "storage.h" #include "storage.h"
#include <errno.h> #include <errno.h>
@@ -57,7 +60,7 @@ load_data (void)
{ {
GVariantDict *res; GVariantDict *res;
GVariant *var; GVariant *var;
g_autofree gchar *contents = NULL; gchar *contents = NULL;
gsize length = 0; gsize length = 0;
if (!g_file_get_contents (STORAGE_FILE, &contents, &length, NULL)) if (!g_file_get_contents (STORAGE_FILE, &contents, &length, NULL))
@@ -66,7 +69,12 @@ load_data (void)
return g_variant_dict_new (NULL); return g_variant_dict_new (NULL);
} }
var = g_variant_new_from_data (G_VARIANT_TYPE_VARDICT, contents, length, FALSE, NULL, NULL); var = g_variant_new_from_data (G_VARIANT_TYPE_VARDICT,
contents,
length,
FALSE,
g_free,
contents);
res = g_variant_dict_new (var); res = g_variant_dict_new (var);
g_variant_unref (var); g_variant_unref (var);
@@ -129,7 +137,7 @@ print_data_load (FpDevice *dev, FpFinger finger)
g_autoptr(GVariant) val = NULL; g_autoptr(GVariant) val = NULL;
g_autoptr(GVariantDict) dict = NULL; g_autoptr(GVariantDict) dict = NULL;
g_autofree guchar *stored_data = NULL; const guchar *stored_data = NULL;
gsize stored_len; gsize stored_len;
dict = load_data (); dict = load_data ();
@@ -140,7 +148,7 @@ print_data_load (FpDevice *dev, FpFinger finger)
FpPrint *print; FpPrint *print;
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
stored_data = (guchar *) g_variant_get_fixed_array (val, &stored_len, 1); stored_data = (const guchar *) g_variant_get_fixed_array (val, &stored_len, 1);
print = fp_print_deserialize (stored_data, stored_len, &error); print = fp_print_deserialize (stored_data, stored_len, &error);
if (error) if (error)
@@ -152,12 +160,58 @@ print_data_load (FpDevice *dev, FpFinger finger)
return NULL; return NULL;
} }
GPtrArray *
gallery_data_load (FpDevice *dev)
{
g_autoptr(GVariantDict) dict = NULL;
g_autoptr(GVariant) dict_variant = NULL;
g_autofree char *dev_prefix = NULL;
GPtrArray *gallery;
const char *driver;
const char *dev_id;
GVariantIter iter;
GVariant *value;
gchar *key;
gallery = g_ptr_array_new_with_free_func (g_object_unref);
dict = load_data ();
dict_variant = g_variant_dict_end (dict);
driver = fp_device_get_driver (dev);
dev_id = fp_device_get_device_id (dev);
dev_prefix = g_strdup_printf ("%s/%s/", driver, dev_id);
g_variant_iter_init (&iter, dict_variant);
while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
{
FpPrint *print;
const guchar *stored_data;
g_autoptr(GError) error = NULL;
gsize stored_len;
if (!g_str_has_prefix (key, dev_prefix))
continue;
stored_data = (const guchar *) g_variant_get_fixed_array (value, &stored_len, 1);
print = fp_print_deserialize (stored_data, stored_len, &error);
if (error)
{
g_warning ("Error deserializing data: %s", error->message);
continue;
}
g_ptr_array_add (gallery, print);
}
return gallery;
}
FpPrint * FpPrint *
print_create_template (FpDevice *dev, FpFinger finger) print_create_template (FpDevice *dev, FpFinger finger)
{ {
g_autoptr(GDateTime) datetime = NULL; g_autoptr(GDateTime) datetime = NULL;
g_autoptr(GDate) date = NULL;
FpPrint *template = NULL; FpPrint *template = NULL;
GDate *date = NULL;
gint year, month, day; gint year, month, day;
template = fp_print_new (dev); template = fp_print_new (dev);
@@ -167,13 +221,12 @@ print_create_template (FpDevice *dev, FpFinger finger)
g_date_time_get_ymd (datetime, &year, &month, &day); g_date_time_get_ymd (datetime, &year, &month, &day);
date = g_date_new_dmy (day, month, year); date = g_date_new_dmy (day, month, year);
fp_print_set_enroll_date (template, date); fp_print_set_enroll_date (template, date);
g_date_free (date);
return template; return template;
} }
static gboolean gboolean
save_image_to_pgm (FpImage *img, const char *path) save_image_to_pgm (FpImage *img, const char *path)
{ {
FILE *fd = fopen (path, "w"); FILE *fd = fopen (path, "w");
@@ -213,7 +266,7 @@ save_image_to_pgm (FpImage *img, const char *path)
gboolean gboolean
print_image_save (FpPrint *print, const char *path) print_image_save (FpPrint *print, const char *path)
{ {
g_autoptr(FpImage) img = NULL; FpImage *img = NULL;
g_return_val_if_fail (FP_IS_PRINT (print), FALSE); g_return_val_if_fail (FP_IS_PRINT (print), FALSE);
g_return_val_if_fail (path != NULL, FALSE); g_return_val_if_fail (path != NULL, FALSE);

View File

@@ -18,17 +18,16 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __STORAGE_H #pragma once
#define __STORAGE_H
int print_data_save (FpPrint *print, int print_data_save (FpPrint *print,
FpFinger finger); FpFinger finger);
FpPrint * print_data_load (FpDevice *dev, FpPrint * print_data_load (FpDevice *dev,
FpFinger finger); FpFinger finger);
GPtrArray * gallery_data_load (FpDevice *dev);
FpPrint * print_create_template (FpDevice *dev, FpPrint * print_create_template (FpDevice *dev,
FpFinger finger); FpFinger finger);
gboolean print_image_save (FpPrint *print, gboolean print_image_save (FpPrint *print,
const char *path); const char *path);
gboolean save_image_to_pgm (FpImage *img,
#endif /* __STORAGE_H */ const char *path);

View File

@@ -18,6 +18,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#define FP_COMPONENT "example-utilities"
#include <libfprint/fprint.h> #include <libfprint/fprint.h>
#include <stdio.h> #include <stdio.h>
@@ -107,29 +109,19 @@ finger_to_string (FpFinger finger)
FpFinger FpFinger
finger_chooser (void) finger_chooser (void)
{ {
int i; int i = FP_FINGER_UNKNOWN;
const FpFinger all_fingers[] = {
FP_FINGER_LEFT_THUMB,
FP_FINGER_LEFT_INDEX,
FP_FINGER_LEFT_MIDDLE,
FP_FINGER_LEFT_RING,
FP_FINGER_LEFT_LITTLE,
FP_FINGER_RIGHT_THUMB,
FP_FINGER_RIGHT_INDEX,
FP_FINGER_RIGHT_MIDDLE,
FP_FINGER_RIGHT_RING,
FP_FINGER_RIGHT_LITTLE,
};
for (i = all_fingers[0]; i <= G_N_ELEMENTS (all_fingers); ++i) for (i = FP_FINGER_FIRST; i <= FP_FINGER_LAST; ++i)
g_print (" [%d] %s\n", (i - all_fingers[0]), finger_to_string (i)); g_print (" [%d] %s\n", (i - FP_FINGER_FIRST), finger_to_string (i));
g_print ("> "); g_print ("> ");
if (!scanf ("%d%*c", &i)) if (!scanf ("%d%*c", &i))
return FP_FINGER_UNKNOWN; return FP_FINGER_UNKNOWN;
if (i < 0 || i >= G_N_ELEMENTS (all_fingers)) i += FP_FINGER_FIRST;
if (i < FP_FINGER_FIRST || i > FP_FINGER_LAST)
return FP_FINGER_UNKNOWN; return FP_FINGER_UNKNOWN;
return all_fingers[i]; return i;
} }

View File

@@ -18,11 +18,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __UTILITIES_H #pragma once
#define __UTILITIES_H
FpDevice * discover_device (GPtrArray *devices); FpDevice * discover_device (GPtrArray *devices);
FpFinger finger_chooser (void); FpFinger finger_chooser (void);
const char * finger_to_string (FpFinger finger); const char * finger_to_string (FpFinger finger);
#endif /* __UTILITIES_H */

View File

@@ -19,8 +19,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#define FP_COMPONENT "example-verify"
#include <stdio.h> #include <stdio.h>
#include <libfprint/fprint.h> #include <libfprint/fprint.h>
#include <glib-unix.h>
#include "storage.h" #include "storage.h"
#include "utilities.h" #include "utilities.h"
@@ -28,6 +31,8 @@
typedef struct _VerifyData typedef struct _VerifyData
{ {
GMainLoop *loop; GMainLoop *loop;
GCancellable *cancellable;
unsigned int sigint_handler;
FpFinger finger; FpFinger finger;
int ret_value; int ret_value;
} VerifyData; } VerifyData;
@@ -35,6 +40,8 @@ typedef struct _VerifyData
static void static void
verify_data_free (VerifyData *verify_data) verify_data_free (VerifyData *verify_data)
{ {
g_clear_handle_id (&verify_data->sigint_handler, g_source_remove);
g_clear_object (&verify_data->cancellable);
g_main_loop_unref (verify_data->loop); g_main_loop_unref (verify_data->loop);
g_free (verify_data); g_free (verify_data);
} }
@@ -50,11 +57,24 @@ on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data)
fp_device_close_finish (dev, res, &error); fp_device_close_finish (dev, res, &error);
if (error) if (error)
g_warning ("Failed closing device %s\n", error->message); g_warning ("Failed closing device %s", error->message);
g_main_loop_quit (verify_data->loop); g_main_loop_quit (verify_data->loop);
} }
static void
verify_quit (FpDevice *dev,
VerifyData *verify_data)
{
if (!fp_device_is_open (dev))
{
g_main_loop_quit (verify_data->loop);
return;
}
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, verify_data);
}
static void start_verification (FpDevice *dev, static void start_verification (FpDevice *dev,
VerifyData *verify_data); VerifyData *verify_data);
@@ -71,35 +91,85 @@ on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data)
if (!fp_device_verify_finish (dev, res, &match, &print, &error)) if (!fp_device_verify_finish (dev, res, &match, &print, &error))
{ {
g_warning ("Failed to verify print: %s", error->message); g_warning ("Failed to verify print: %s", error->message);
g_main_loop_quit (verify_data->loop); verify_data->ret_value = EXIT_FAILURE;
if (error->domain != FP_DEVICE_RETRY)
{
verify_quit (dev, verify_data);
return; return;
} }
if (match)
{
g_print ("MATCH!\n");
if (fp_device_supports_capture (dev) &&
print_image_save (print, "verify.pgm"))
g_print ("Print image saved as verify.pgm");
verify_data->ret_value = EXIT_SUCCESS;
}
else
{
g_print ("NO MATCH!\n");
verify_data->ret_value = EXIT_FAILURE;
} }
g_print ("Verify again? [Y/n]? "); g_print ("Verify again? [Y/n]? ");
if (fgets (buffer, sizeof (buffer), stdin) && if (fgets (buffer, sizeof (buffer), stdin) &&
(buffer[0] == 'Y' || buffer[0] == 'y')) (buffer[0] == 'Y' || buffer[0] == 'y' || buffer[0] == '\n'))
{ {
start_verification (dev, verify_data); start_verification (dev, verify_data);
return; return;
} }
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, verify_quit (dev, verify_data);
verify_data); }
static void
on_match_cb (FpDevice *dev, FpPrint *match, FpPrint *print,
gpointer user_data, GError *error)
{
VerifyData *verify_data = user_data;
if (error)
{
g_warning ("Match report: Finger not matched, retry error reported: %s",
error->message);
return;
}
if (print && fp_print_get_image (print) &&
print_image_save (print, "verify.pgm"))
g_print ("Print image saved as verify.pgm\n");
if (match)
{
char date_str[128];
verify_data->ret_value = EXIT_SUCCESS;
g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0",
fp_print_get_enroll_date (match));
g_debug ("Match report: device %s matched finger %s successifully "
"with print %s, enrolled on date %s by user %s",
fp_device_get_name (dev),
finger_to_string (fp_print_get_finger (match)),
fp_print_get_description (match), date_str,
fp_print_get_username (match));
g_print ("MATCH!\n");
}
else
{
g_debug ("Match report: Finger not matched");
g_print ("NO MATCH!\n");
}
}
static FpPrint *
get_stored_print (FpDevice *dev, VerifyData *verify_data)
{
FpPrint *verify_print;
g_print ("Loading previously enrolled %s finger data...\n",
finger_to_string (verify_data->finger));
verify_print = print_data_load (dev, verify_data->finger);
if (!verify_print)
{
g_warning ("Failed to load fingerprint data");
g_warning ("Did you remember to enroll your %s finger first?",
finger_to_string (verify_data->finger));
}
return verify_print;
} }
static void static void
@@ -115,15 +185,27 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data)
if (!error) if (!error)
{ {
FpPrint *verify_print = NULL; FpPrint *verify_print = NULL;
g_autoptr(FpPrint) stored_print = NULL;
guint i; guint i;
if (!prints->len) if (!prints->len)
{
g_warning ("No prints saved on device"); g_warning ("No prints saved on device");
verify_quit (dev, verify_data);
return;
}
stored_print = get_stored_print (dev, verify_data);
for (i = 0; i < prints->len; ++i) for (i = 0; i < prints->len; ++i)
{ {
FpPrint *print = prints->pdata[i]; FpPrint *print = prints->pdata[i];
if (stored_print && fp_print_equal (stored_print, print))
/* If the private print data matches, let's use the stored print
* as it contains more metadata to show */
print = stored_print;
if (fp_print_get_finger (print) == verify_data->finger && if (fp_print_get_finger (print) == verify_data->finger &&
g_strcmp0 (fp_print_get_username (print), g_get_user_name ()) == 0) g_strcmp0 (fp_print_get_username (print), g_get_user_name ()) == 0)
{ {
@@ -141,9 +223,7 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data)
if (!verify_print) if (!verify_print)
{ {
g_warning ("Did you remember to enroll your %s finger first?", verify_quit (dev, verify_data);
finger_to_string (verify_data->finger));
g_main_loop_quit (verify_data->loop);
return; return;
} }
@@ -151,28 +231,32 @@ on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data)
fp_print_get_description (verify_print)); fp_print_get_description (verify_print));
g_print ("Print loaded. Time to verify!\n"); g_print ("Print loaded. Time to verify!\n");
fp_device_verify (dev, verify_print, NULL, fp_device_verify (dev, verify_print, verify_data->cancellable,
on_match_cb, verify_data, NULL,
(GAsyncReadyCallback) on_verify_completed, (GAsyncReadyCallback) on_verify_completed,
verify_data); verify_data);
} }
else else
{ {
g_warning ("Loading prints failed with error %s", error->message); g_warning ("Loading prints failed with error %s", error->message);
g_main_loop_quit (verify_data->loop); verify_quit (dev, verify_data);
} }
} }
static void static void
start_verification (FpDevice *dev, VerifyData *verify_data) start_verification (FpDevice *dev, VerifyData *verify_data)
{
if (verify_data->finger == FP_FINGER_UNKNOWN)
{ {
g_print ("Choose the finger to verify:\n"); g_print ("Choose the finger to verify:\n");
verify_data->finger = finger_chooser (); verify_data->finger = finger_chooser ();
}
if (verify_data->finger == FP_FINGER_UNKNOWN) if (verify_data->finger == FP_FINGER_UNKNOWN)
{ {
g_warning ("Unknown finger selected"); g_warning ("Unknown finger selected");
verify_data->ret_value = EXIT_FAILURE; verify_data->ret_value = EXIT_FAILURE;
g_main_loop_quit (verify_data->loop); verify_quit (dev, verify_data);
return; return;
} }
@@ -185,23 +269,17 @@ start_verification (FpDevice *dev, VerifyData *verify_data)
} }
else else
{ {
g_print ("Loading previously enrolled %s finger data...\n", g_autoptr(FpPrint) verify_print = get_stored_print (dev, verify_data);
finger_to_string (verify_data->finger));
g_autoptr(FpPrint) verify_print = NULL;
verify_print = print_data_load (dev, verify_data->finger);
if (!verify_print) if (!verify_print)
{ {
g_warning ("Failed to load fingerprint data"); verify_quit (dev, verify_data);
g_warning ("Did you remember to enroll your %s finger first?",
finger_to_string (verify_data->finger));
g_main_loop_quit (verify_data->loop);
return; return;
} }
g_print ("Print loaded. Time to verify!\n"); g_print ("Print loaded. Time to verify!\n");
fp_device_verify (dev, verify_print, NULL, fp_device_verify (dev, verify_print, verify_data->cancellable,
on_match_cb, verify_data, NULL,
(GAsyncReadyCallback) on_verify_completed, (GAsyncReadyCallback) on_verify_completed,
verify_data); verify_data);
} }
@@ -217,7 +295,7 @@ on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
if (!fp_device_open_finish (dev, res, &error)) if (!fp_device_open_finish (dev, res, &error))
{ {
g_warning ("Failed to open device: %s", error->message); g_warning ("Failed to open device: %s", error->message);
g_main_loop_quit (verify_data->loop); verify_quit (dev, verify_data);
return; return;
} }
@@ -226,6 +304,16 @@ on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
start_verification (dev, verify_data); start_verification (dev, verify_data);
} }
static gboolean
sigint_cb (void *user_data)
{
VerifyData *verify_data = user_data;
g_cancellable_cancel (verify_data->cancellable);
return G_SOURCE_CONTINUE;
}
int int
main (void) main (void)
{ {
@@ -256,8 +344,14 @@ main (void)
verify_data = g_new0 (VerifyData, 1); verify_data = g_new0 (VerifyData, 1);
verify_data->ret_value = EXIT_FAILURE; verify_data->ret_value = EXIT_FAILURE;
verify_data->loop = g_main_loop_new (NULL, FALSE); verify_data->loop = g_main_loop_new (NULL, FALSE);
verify_data->cancellable = g_cancellable_new ();
fp_device_open (dev, NULL, (GAsyncReadyCallback) on_device_opened, verify_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH,
SIGINT,
sigint_cb,
verify_data,
NULL);
fp_device_open (dev, verify_data->cancellable,
(GAsyncReadyCallback) on_device_opened,
verify_data); verify_data);
g_main_loop_run (verify_data->loop); g_main_loop_run (verify_data->loop);

View File

@@ -135,10 +135,8 @@ generic_read_ignore_data (FpiSsm *ssm, FpDevice *dev,
size_t bytes) size_t bytes)
{ {
FpiUsbTransfer *transfer = fpi_usb_transfer_new (dev); FpiUsbTransfer *transfer = fpi_usb_transfer_new (dev);
unsigned char *data;
data = g_malloc (bytes); fpi_usb_transfer_fill_bulk (transfer, EP_IN, bytes);
fpi_usb_transfer_fill_bulk_full (transfer, EP_IN, data, bytes, NULL);
transfer->ssm = ssm; transfer->ssm = ssm;
transfer->short_is_error = TRUE; transfer->short_is_error = TRUE;
fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL,
@@ -614,6 +612,7 @@ capture_read_strip_cb (FpiUsbTransfer *transfer, FpDevice *device,
self->strips = g_slist_reverse (self->strips); self->strips = g_slist_reverse (self->strips);
fpi_do_movement_estimation (&assembling_ctx, self->strips); fpi_do_movement_estimation (&assembling_ctx, self->strips);
img = fpi_assemble_frames (&assembling_ctx, self->strips); img = fpi_assemble_frames (&assembling_ctx, self->strips);
img->flags |= FPI_IMAGE_PARTIAL;
g_slist_free_full (self->strips, g_free); g_slist_free_full (self->strips, g_free);
self->strips = NULL; self->strips = NULL;

View File

@@ -18,8 +18,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __AES1660_H #pragma once
#define __AES1660_H
#define AES1660_FRAME_SIZE 0x244 #define AES1660_FRAME_SIZE 0x244
@@ -1986,5 +1985,3 @@ static const unsigned char aes1660_start_imaging_cmd[] = {
0x55, 0x07, 0x00, 0x80, 0x42, 0x00, 0x7f, 0x00, 0x00, 0x14, 0x55, 0x07, 0x00, 0x80, 0x42, 0x00, 0x7f, 0x00, 0x00, 0x14,
0x49, 0x03, 0x00, 0x20, 0x00, 0xc8 0x49, 0x03, 0x00, 0x20, 0x00, 0xc8
}; };
#endif

View File

@@ -458,6 +458,7 @@ capture_read_strip_cb (FpiUsbTransfer *transfer, FpDevice *_dev,
fpi_do_movement_estimation (&assembling_ctx, self->strips); fpi_do_movement_estimation (&assembling_ctx, self->strips);
img = fpi_assemble_frames (&assembling_ctx, img = fpi_assemble_frames (&assembling_ctx,
self->strips); self->strips);
img->flags |= FPI_IMAGE_PARTIAL;
g_slist_free_full (self->strips, g_free); g_slist_free_full (self->strips, g_free);
self->strips = NULL; self->strips = NULL;
self->strips_len = 0; self->strips_len = 0;

View File

@@ -19,8 +19,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __AES2501_H #pragma once
#define __AES2501_H
enum aes2501_regs { enum aes2501_regs {
AES2501_REG_CTRL1 = 0x80, AES2501_REG_CTRL1 = 0x80,
@@ -172,5 +171,3 @@ enum aes2501_sensor_gain2 {
#define AES2501_SUM_HIGH_THRESH 1000 #define AES2501_SUM_HIGH_THRESH 1000
#define AES2501_SUM_LOW_THRESH 700 #define AES2501_SUM_LOW_THRESH 700
#endif /* __AES2501_H */

View File

@@ -197,12 +197,12 @@ process_strip_data (FpiSsm *ssm, FpImageDevice *dev,
if (data[0] != AES2550_EDATA_MAGIC) if (data[0] != AES2550_EDATA_MAGIC)
{ {
fp_dbg ("Bogus magic: %.2x\n", (int) (data[0])); fp_dbg ("Bogus magic: %.2x", (int) (data[0]));
return FALSE; return FALSE;
} }
len = data[1] * 256 + data[2]; len = data[1] * 256 + data[2];
if (len != (AES2550_STRIP_SIZE - 3)) if (len != (AES2550_STRIP_SIZE - 3))
fp_dbg ("Bogus frame len: %.4x\n", len); fp_dbg ("Bogus frame len: %.4x", len);
stripe = g_malloc0 (FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof (struct fpi_frame)); /* 4 bits per pixel */ stripe = g_malloc0 (FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof (struct fpi_frame)); /* 4 bits per pixel */
stripe->delta_x = (int8_t) data[6]; stripe->delta_x = (int8_t) data[6];
stripe->delta_y = -(int8_t) data[7]; stripe->delta_y = -(int8_t) data[7];
@@ -230,6 +230,7 @@ capture_set_idle_reqs_cb (FpiUsbTransfer *transfer,
self->strips = g_slist_reverse (self->strips); self->strips = g_slist_reverse (self->strips);
img = fpi_assemble_frames (&assembling_ctx, self->strips); img = fpi_assemble_frames (&assembling_ctx, self->strips);
img->flags |= FPI_IMAGE_PARTIAL;
g_slist_free_full (self->strips, g_free); g_slist_free_full (self->strips, g_free);
self->strips = NULL; self->strips = NULL;
self->strips_len = 0; self->strips_len = 0;

View File

@@ -17,8 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __AES2550_H #pragma once
#define __AES2550_H
/* Registers bits */ /* Registers bits */
@@ -110,5 +109,3 @@ enum aes2550_cmds {
#define AES2550_HEARTBEAT_MAGIC 0xdb #define AES2550_HEARTBEAT_MAGIC 0xdb
#define AES2550_EP_IN_BUF_SIZE 8192 #define AES2550_EP_IN_BUF_SIZE 8192
#endif

View File

@@ -17,8 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __AES2660_H #pragma once
#define __AES2660_H
#define AES2660_FRAME_SIZE 0x354 #define AES2660_FRAME_SIZE 0x354
@@ -1960,5 +1959,3 @@ static const unsigned char aes2660_start_imaging_cmd[] = {
0x55, 0x07, 0x00, 0x80, 0x42, 0x00, 0xbf, 0x00, 0x00, 0x18, 0x55, 0x07, 0x00, 0x80, 0x42, 0x00, 0xbf, 0x00, 0x00, 0x18,
0x49, 0x03, 0x00, 0x20, 0x08, 0xc8 0x49, 0x03, 0x00, 0x20, 0x08, 0xc8
}; };
#endif

View File

@@ -106,7 +106,9 @@ static struct aes_regwrite init_reqs[] = {
{ 0x9e, 0x53 }, /* clear challenge word bits */ { 0x9e, 0x53 }, /* clear challenge word bits */
{ 0x9f, 0x6b }, /* set some challenge word bits */ { 0x9f, 0x6b }, /* set some challenge word bits */
{ 0, 0 }, { 0, 0 },
};
static struct aes_regwrite capture_reqs[] = {
{ 0x80, 0x00 }, { 0x80, 0x00 },
{ 0x81, 0x00 }, { 0x81, 0x00 },
{ 0, 0 }, { 0, 0 },
@@ -155,4 +157,6 @@ fpi_device_aes3500_class_init (FpiDeviceAes3500Class *klass)
aes_class->enlarge_factor = ENLARGE_FACTOR; aes_class->enlarge_factor = ENLARGE_FACTOR;
aes_class->init_reqs = init_reqs; aes_class->init_reqs = init_reqs;
aes_class->init_reqs_len = G_N_ELEMENTS (init_reqs); aes_class->init_reqs_len = G_N_ELEMENTS (init_reqs);
aes_class->capture_reqs = capture_reqs;
aes_class->capture_reqs_len = G_N_ELEMENTS (capture_reqs);
} }

View File

@@ -42,8 +42,10 @@
typedef struct typedef struct
{ {
FpiUsbTransfer *img_trf; /* This is used both as a flag that we are in a capture operation
gboolean deactivating; * and for cancellation.
*/
GCancellable *img_capture_cancel;
} FpiDeviceAes3kPrivate; } FpiDeviceAes3kPrivate;
#define CTRL_TIMEOUT 1000 #define CTRL_TIMEOUT 1000
@@ -84,7 +86,8 @@ img_cb (FpiUsbTransfer *transfer, FpDevice *device,
FpImage *img; FpImage *img;
int i; int i;
priv->img_trf = NULL; /* Image capture operation is finished (error/completed) */
g_clear_object (&priv->img_capture_cancel);
if (error) if (error)
{ {
@@ -92,14 +95,14 @@ img_cb (FpiUsbTransfer *transfer, FpDevice *device,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_CANCELLED)) G_IO_ERROR_CANCELLED))
{ {
/* Deactivation was completed. */ /* Cancellation implies we are deactivating. */
g_error_free (error); g_error_free (error);
if (priv->deactivating)
fpi_image_device_deactivate_complete (dev, NULL); fpi_image_device_deactivate_complete (dev, NULL);
return; return;
} }
fpi_image_device_session_error (dev, error); fpi_image_device_session_error (dev, error);
return;
} }
fpi_image_device_report_finger_status (dev, TRUE); fpi_image_device_report_finger_status (dev, TRUE);
@@ -123,43 +126,69 @@ img_cb (FpiUsbTransfer *transfer, FpDevice *device,
fpi_image_device_image_captured (dev, img); fpi_image_device_image_captured (dev, img);
/* FIXME: rather than assuming finger has gone, we should poll regs until /* FIXME: rather than assuming finger has gone, we should poll regs until
* it really has, then restart the capture */ * it really has. */
fpi_image_device_report_finger_status (dev, FALSE); fpi_image_device_report_finger_status (dev, FALSE);
do_capture (dev); /* Note: The transfer is re-started when we switch to the AWAIT_FINGER_ON state. */
} }
static void static void
do_capture (FpImageDevice *dev) do_capture (FpImageDevice *dev)
{ {
g_autoptr(FpiUsbTransfer) img_trf = NULL;
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev); FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self); FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
FpiDeviceAes3kClass *cls = FPI_DEVICE_AES3K_GET_CLASS (self); FpiDeviceAes3kClass *cls = FPI_DEVICE_AES3K_GET_CLASS (self);
priv->img_trf = fpi_usb_transfer_new (FP_DEVICE (dev)); img_trf = fpi_usb_transfer_new (FP_DEVICE (dev));
fpi_usb_transfer_fill_bulk (priv->img_trf, EP_IN, cls->data_buflen); fpi_usb_transfer_fill_bulk (img_trf, EP_IN, cls->data_buflen);
priv->img_trf->short_is_error = TRUE; img_trf->short_is_error = TRUE;
fpi_usb_transfer_submit (priv->img_trf, 0, fpi_usb_transfer_submit (g_steal_pointer (&img_trf), 0,
fpi_device_get_cancellable (FP_DEVICE (dev)), priv->img_capture_cancel,
img_cb, NULL); img_cb, NULL);
} }
static void
capture_reqs_cb (FpImageDevice *dev, GError *result, void *user_data)
{
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
if (result)
{
g_clear_object (&priv->img_capture_cancel);
fpi_image_device_session_error (dev, result);
return;
}
/* FIXME: we never cancel a pending capture. So we are likely leaving the
* hardware in a bad state should we abort the capture operation and the
* user does not touch the device.
* But, we don't know how we might cancel, so just leave it as is. */
do_capture (dev);
}
static void
do_capture_start (FpImageDevice *dev)
{
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
FpiDeviceAes3kClass *cls = FPI_DEVICE_AES3K_GET_CLASS (self);
aes_write_regv (dev, cls->capture_reqs, cls->capture_reqs_len, capture_reqs_cb, NULL);
}
static void static void
init_reqs_cb (FpImageDevice *dev, GError *result, void *user_data) init_reqs_cb (FpImageDevice *dev, GError *result, void *user_data)
{ {
fpi_image_device_activate_complete (dev, result); fpi_image_device_activate_complete (dev, result);
if (!result)
do_capture (dev);
} }
static void static void
aes3k_dev_activate (FpImageDevice *dev) aes3k_dev_activate (FpImageDevice *dev)
{ {
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev); FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
FpiDeviceAes3kClass *cls = FPI_DEVICE_AES3K_GET_CLASS (self); FpiDeviceAes3kClass *cls = FPI_DEVICE_AES3K_GET_CLASS (self);
priv->deactivating = FALSE;
aes_write_regv (dev, cls->init_reqs, cls->init_reqs_len, init_reqs_cb, NULL); aes_write_regv (dev, cls->init_reqs, cls->init_reqs_len, init_reqs_cb, NULL);
} }
@@ -169,12 +198,28 @@ aes3k_dev_deactivate (FpImageDevice *dev)
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev); FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self); FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
priv->deactivating = TRUE; /* If a capture is running, then deactivation finishes from the cancellation handler */
if (priv->img_trf) if (priv->img_capture_cancel)
return; g_cancellable_cancel (priv->img_capture_cancel);
else
fpi_image_device_deactivate_complete (dev, NULL); fpi_image_device_deactivate_complete (dev, NULL);
} }
static void
aes3k_dev_change_state (FpImageDevice *dev, FpiImageDeviceState state)
{
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
{
g_assert (!priv->img_capture_cancel);
priv->img_capture_cancel = g_cancellable_new ();
do_capture_start (dev);
}
}
static void static void
fpi_device_aes3k_init (FpiDeviceAes3k *self) fpi_device_aes3k_init (FpiDeviceAes3k *self)
{ {
@@ -217,6 +262,7 @@ fpi_device_aes3k_class_init (FpiDeviceAes3kClass *klass)
img_class->img_open = aes3k_dev_init; img_class->img_open = aes3k_dev_init;
img_class->img_close = aes3k_dev_deinit; img_class->img_close = aes3k_dev_deinit;
img_class->activate = aes3k_dev_activate; img_class->activate = aes3k_dev_activate;
img_class->change_state = aes3k_dev_change_state;
img_class->deactivate = aes3k_dev_deactivate; img_class->deactivate = aes3k_dev_deactivate;
/* Extremely low due to low image quality. */ /* Extremely low due to low image quality. */

View File

@@ -57,4 +57,6 @@ struct _FpiDeviceAes3kClass
gsize data_buflen; /* buffer length of usb bulk transfer */ gsize data_buflen; /* buffer length of usb bulk transfer */
struct aes_regwrite *init_reqs; /* initial values sent to device */ struct aes_regwrite *init_reqs; /* initial values sent to device */
gsize init_reqs_len; gsize init_reqs_len;
struct aes_regwrite *capture_reqs; /* capture values sent to device */
gsize capture_reqs_len;
}; };

View File

@@ -103,7 +103,9 @@ static struct aes_regwrite init_reqs[] = {
{ 0x9e, 0x53 }, /* clear challenge word bits */ { 0x9e, 0x53 }, /* clear challenge word bits */
{ 0x9f, 0x6b }, /* set some challenge word bits */ { 0x9f, 0x6b }, /* set some challenge word bits */
{ 0, 0 }, { 0, 0 },
};
static struct aes_regwrite capture_reqs[] = {
{ 0x80, 0x00 }, { 0x80, 0x00 },
{ 0x81, 0x00 }, { 0x81, 0x00 },
{ 0, 0 }, { 0, 0 },
@@ -152,4 +154,6 @@ fpi_device_aes4000_class_init (FpiDeviceAes4000Class *klass)
aes_class->enlarge_factor = ENLARGE_FACTOR; aes_class->enlarge_factor = ENLARGE_FACTOR;
aes_class->init_reqs = init_reqs; aes_class->init_reqs = init_reqs;
aes_class->init_reqs_len = G_N_ELEMENTS (init_reqs); aes_class->init_reqs_len = G_N_ELEMENTS (init_reqs);
aes_class->capture_reqs = capture_reqs;
aes_class->capture_reqs_len = G_N_ELEMENTS (capture_reqs);
} }

View File

@@ -19,13 +19,11 @@
#define FP_COMPONENT "aeslib" #define FP_COMPONENT "aeslib"
#include "fp_internal.h" #include "drivers_api.h"
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include "fpi-usb-transfer.h"
#include "fpi-assembling.h"
#include "aeslib.h" #include "aeslib.h"
#define MAX_REGWRITES_PER_REQUEST 16 #define MAX_REGWRITES_PER_REQUEST 16

View File

@@ -17,8 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __AESLIB_H__ #pragma once
#define __AESLIB_H__
#include <fprint.h> #include <fprint.h>
@@ -45,5 +44,3 @@ unsigned char aes_get_pixel (struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame, struct fpi_frame *frame,
unsigned int x, unsigned int x,
unsigned int y); unsigned int y);
#endif

View File

@@ -54,6 +54,7 @@ static void complete_deactivation (FpImageDevice *dev);
#define CALIBRATE_DATA_LEN 4 #define CALIBRATE_DATA_LEN 4
#define FINGER_DET_DATA_LEN 4 #define FINGER_DET_DATA_LEN 4
FP_GNUC_ACCESS (read_only, 3, 4)
static void static void
aesX660_send_cmd_timeout (FpiSsm *ssm, aesX660_send_cmd_timeout (FpiSsm *ssm,
FpDevice *_dev, FpDevice *_dev,
@@ -70,6 +71,7 @@ aesX660_send_cmd_timeout (FpiSsm *ssm,
fpi_usb_transfer_submit (transfer, timeout, NULL, callback, NULL); fpi_usb_transfer_submit (transfer, timeout, NULL, callback, NULL);
} }
FP_GNUC_ACCESS (read_only, 3, 4)
static void static void
aesX660_send_cmd (FpiSsm *ssm, aesX660_send_cmd (FpiSsm *ssm,
FpDevice *dev, FpDevice *dev,
@@ -89,13 +91,12 @@ aesX660_read_response (FpiSsm *ssm,
FpiUsbTransferCallback callback) FpiUsbTransferCallback callback)
{ {
FpiUsbTransfer *transfer = fpi_usb_transfer_new (_dev); FpiUsbTransfer *transfer = fpi_usb_transfer_new (_dev);
unsigned char *data;
GCancellable *cancel = NULL; GCancellable *cancel = NULL;
if (cancellable) if (cancellable)
cancel = fpi_device_get_cancellable (_dev); cancel = fpi_device_get_cancellable (_dev);
data = g_malloc (buf_len);
fpi_usb_transfer_fill_bulk_full (transfer, EP_IN, data, buf_len, NULL); fpi_usb_transfer_fill_bulk (transfer, EP_IN, buf_len);
transfer->ssm = ssm; transfer->ssm = ssm;
transfer->short_is_error = short_is_error; transfer->short_is_error = short_is_error;
fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, cancel, callback, NULL); fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, cancel, callback, NULL);
@@ -116,7 +117,7 @@ aesX660_read_calibrate_data_cb (FpiUsbTransfer *transfer,
/* Calibrate response was read correctly? */ /* Calibrate response was read correctly? */
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_CALIBRATE_RESPONSE) if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_CALIBRATE_RESPONSE)
{ {
fp_dbg ("Bogus calibrate response: %.2x\n", data[0]); fp_dbg ("Bogus calibrate response: %.2x", data[0]);
fpi_ssm_mark_failed (transfer->ssm, fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Bogus calibrate " "Bogus calibrate "
@@ -155,14 +156,14 @@ finger_det_read_fd_data_cb (FpiUsbTransfer *transfer,
if (error) if (error)
{ {
fp_dbg ("Failed to read FD data\n"); fp_dbg ("Failed to read FD data");
fpi_ssm_mark_failed (transfer->ssm, error); fpi_ssm_mark_failed (transfer->ssm, error);
return; return;
} }
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_FINGER_DET_RESPONSE) if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_FINGER_DET_RESPONSE)
{ {
fp_dbg ("Bogus FD response: %.2x\n", data[0]); fp_dbg ("Bogus FD response: %.2x", data[0]);
fpi_ssm_mark_failed (transfer->ssm, fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Bogus FD response %.2x", "Bogus FD response %.2x",
@@ -177,7 +178,7 @@ finger_det_read_fd_data_cb (FpiUsbTransfer *transfer,
} }
else else
{ {
fp_dbg ("Wait for finger returned %.2x as result\n", fp_dbg ("Wait for finger returned %.2x as result",
data[AESX660_FINGER_PRESENT_OFFSET]); data[AESX660_FINGER_PRESENT_OFFSET]);
fpi_ssm_jump_to_state (transfer->ssm, FINGER_DET_SEND_FD_CMD); fpi_ssm_jump_to_state (transfer->ssm, FINGER_DET_SEND_FD_CMD);
} }
@@ -331,6 +332,7 @@ capture_set_idle_cmd_cb (FpiUsbTransfer *transfer, FpDevice *device,
priv->strips = g_slist_reverse (priv->strips); priv->strips = g_slist_reverse (priv->strips);
img = fpi_assemble_frames (cls->assembling_ctx, priv->strips); img = fpi_assemble_frames (cls->assembling_ctx, priv->strips);
img->flags |= FPI_IMAGE_PARTIAL;
g_slist_foreach (priv->strips, (GFunc) g_free, NULL); g_slist_foreach (priv->strips, (GFunc) g_free, NULL);
g_slist_free (priv->strips); g_slist_free (priv->strips);
priv->strips = NULL; priv->strips = NULL;
@@ -404,7 +406,7 @@ capture_read_stripe_data_cb (FpiUsbTransfer *transfer,
g_byte_array_set_size (priv->stripe_packet, 0); g_byte_array_set_size (priv->stripe_packet, 0);
} }
fp_dbg ("finger %s\n", finger_missing ? "missing" : "present"); fp_dbg ("finger %s", finger_missing ? "missing" : "present");
if (finger_missing) if (finger_missing)
fpi_ssm_next_state (transfer->ssm); fpi_ssm_next_state (transfer->ssm);
@@ -439,7 +441,7 @@ capture_run_state (FpiSsm *ssm, FpDevice *_dev)
break; break;
case CAPTURE_SET_IDLE: case CAPTURE_SET_IDLE:
fp_dbg ("Got %lu frames\n", priv->strips_len); fp_dbg ("Got %lu frames", priv->strips_len);
aesX660_send_cmd (ssm, _dev, set_idle_cmd, sizeof (set_idle_cmd), aesX660_send_cmd (ssm, _dev, set_idle_cmd, sizeof (set_idle_cmd),
capture_set_idle_cmd_cb); capture_set_idle_cmd_cb);
break; break;
@@ -512,19 +514,19 @@ activate_read_id_cb (FpiUsbTransfer *transfer, FpDevice *device,
if (error) if (error)
{ {
fp_dbg ("read_id cmd failed\n"); fp_dbg ("read_id cmd failed");
fpi_ssm_mark_failed (transfer->ssm, error); fpi_ssm_mark_failed (transfer->ssm, error);
return; return;
} }
/* ID was read correctly */ /* ID was read correctly */
if (data[0] == 0x07) if (data[0] == 0x07)
{ {
fp_dbg ("Sensor device id: %.2x%2x, bcdDevice: %.2x.%.2x, init status: %.2x\n", fp_dbg ("Sensor device id: %.2x%2x, bcdDevice: %.2x.%.2x, init status: %.2x",
data[4], data[3], data[5], data[6], data[7]); data[4], data[3], data[5], data[6], data[7]);
} }
else else
{ {
fp_dbg ("Bogus read ID response: %.2x\n", data[AESX660_RESPONSE_TYPE_OFFSET]); fp_dbg ("Bogus read ID response: %.2x", data[AESX660_RESPONSE_TYPE_OFFSET]);
fpi_ssm_mark_failed (transfer->ssm, fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Bogus read ID response %.2x", "Bogus read ID response %.2x",
@@ -552,7 +554,7 @@ activate_read_id_cb (FpiUsbTransfer *transfer, FpDevice *device,
break; break;
default: default:
fp_dbg ("Failed to init device! init status: %.2x\n", data[7]); fp_dbg ("Failed to init device! init status: %.2x", data[7]);
fpi_ssm_mark_failed (transfer->ssm, fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Failed to init device %.2x", "Failed to init device %.2x",
@@ -569,11 +571,11 @@ activate_read_init_cb (FpiUsbTransfer *transfer, FpDevice *device,
FpiDeviceAesX660Private *priv = fpi_device_aes_x660_get_instance_private (self); FpiDeviceAesX660Private *priv = fpi_device_aes_x660_get_instance_private (self);
unsigned char *data = transfer->buffer; unsigned char *data = transfer->buffer;
fp_dbg ("read_init_cb\n"); fp_dbg ("read_init_cb");
if (error) if (error)
{ {
fp_dbg ("read_init transfer status: %s, actual_len: %d\n", error->message, fp_dbg ("read_init transfer status: %s, actual_len: %d", error->message,
(gint) transfer->actual_length); (gint) transfer->actual_length);
fpi_ssm_mark_failed (transfer->ssm, error); fpi_ssm_mark_failed (transfer->ssm, error);
return; return;
@@ -581,7 +583,7 @@ activate_read_init_cb (FpiUsbTransfer *transfer, FpDevice *device,
/* ID was read correctly */ /* ID was read correctly */
if (data[0] != 0x42 || data[3] != 0x01) if (data[0] != 0x42 || data[3] != 0x01)
{ {
fp_dbg ("Bogus read init response: %.2x %.2x\n", data[0], fp_dbg ("Bogus read init response: %.2x %.2x", data[0],
data[3]); data[3]);
fpi_ssm_mark_failed (transfer->ssm, fpi_ssm_mark_failed (transfer->ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
@@ -613,13 +615,13 @@ activate_run_state (FpiSsm *ssm, FpDevice *_dev)
{ {
case ACTIVATE_SET_IDLE: case ACTIVATE_SET_IDLE:
priv->init_seq_idx = 0; priv->init_seq_idx = 0;
fp_dbg ("Activate: set idle\n"); fp_dbg ("Activate: set idle");
aesX660_send_cmd (ssm, _dev, set_idle_cmd, sizeof (set_idle_cmd), aesX660_send_cmd (ssm, _dev, set_idle_cmd, sizeof (set_idle_cmd),
fpi_ssm_usb_transfer_cb); fpi_ssm_usb_transfer_cb);
break; break;
case ACTIVATE_SEND_READ_ID_CMD: case ACTIVATE_SEND_READ_ID_CMD:
fp_dbg ("Activate: read ID\n"); fp_dbg ("Activate: read ID");
aesX660_send_cmd (ssm, _dev, read_id_cmd, sizeof (read_id_cmd), aesX660_send_cmd (ssm, _dev, read_id_cmd, sizeof (read_id_cmd),
fpi_ssm_usb_transfer_cb); fpi_ssm_usb_transfer_cb);
break; break;
@@ -629,7 +631,7 @@ activate_run_state (FpiSsm *ssm, FpDevice *_dev)
break; break;
case ACTIVATE_SEND_INIT_CMD: case ACTIVATE_SEND_INIT_CMD:
fp_dbg ("Activate: send init seq #%d cmd #%d\n", fp_dbg ("Activate: send init seq #%d cmd #%d",
priv->init_seq_idx, priv->init_seq_idx,
priv->init_cmd_idx); priv->init_cmd_idx);
aesX660_send_cmd (ssm, _dev, aesX660_send_cmd (ssm, _dev,
@@ -639,7 +641,7 @@ activate_run_state (FpiSsm *ssm, FpDevice *_dev)
break; break;
case ACTIVATE_READ_INIT_RESPONSE: case ACTIVATE_READ_INIT_RESPONSE:
fp_dbg ("Activate: read init response\n"); fp_dbg ("Activate: read init response");
aesX660_read_response (ssm, _dev, TRUE, FALSE, INIT_LEN, activate_read_init_cb); aesX660_read_response (ssm, _dev, TRUE, FALSE, INIT_LEN, activate_read_init_cb);
break; break;

View File

@@ -73,9 +73,8 @@ struct _FpiDeviceElan
/* end commands */ /* end commands */
/* state */ /* state */
gboolean active;
gboolean deactivating; gboolean deactivating;
FpImageDeviceState dev_state;
FpImageDeviceState dev_state_next;
unsigned char *last_read; unsigned char *last_read;
unsigned char calib_atts_left; unsigned char calib_atts_left;
unsigned char calib_status; unsigned char calib_status;
@@ -87,8 +86,6 @@ struct _FpiDeviceElan
GSList *frames; GSList *frames;
/* end state */ /* end state */
}; };
G_DECLARE_FINAL_TYPE (FpiDeviceElan, fpi_device_elan, FPI, DEVICE_ELAN,
FpImageDevice);
G_DEFINE_TYPE (FpiDeviceElan, fpi_device_elan, FP_TYPE_IMAGE_DEVICE); G_DEFINE_TYPE (FpiDeviceElan, fpi_device_elan, FP_TYPE_IMAGE_DEVICE);
static int static int
@@ -207,6 +204,7 @@ elan_save_img_frame (FpiDeviceElan *elandev)
unsigned int frame_size = elandev->frame_width * elandev->frame_height; unsigned int frame_size = elandev->frame_width * elandev->frame_height;
unsigned short *frame = g_malloc (frame_size * sizeof (short)); unsigned short *frame = g_malloc (frame_size * sizeof (short));
elan_save_frame (elandev, frame); elan_save_frame (elandev, frame);
unsigned int sum = 0; unsigned int sum = 0;
@@ -244,6 +242,7 @@ elan_process_frame_linear (unsigned short *raw_frame,
G_DEBUG_HERE (); G_DEBUG_HERE ();
unsigned short min = 0xffff, max = 0; unsigned short min = 0xffff, max = 0;
for (int i = 0; i < frame_size; i++) for (int i = 0; i < frame_size; i++)
{ {
if (raw_frame[i] < min) if (raw_frame[i] < min)
@@ -255,6 +254,7 @@ elan_process_frame_linear (unsigned short *raw_frame,
g_assert (max != min); g_assert (max != min);
unsigned short px; unsigned short px;
for (int i = 0; i < frame_size; i++) for (int i = 0; i < frame_size; i++)
{ {
px = raw_frame[i]; px = raw_frame[i];
@@ -278,6 +278,7 @@ elan_process_frame_thirds (unsigned short *raw_frame,
unsigned short lvl0, lvl1, lvl2, lvl3; unsigned short lvl0, lvl1, lvl2, lvl3;
unsigned short *sorted = g_malloc (frame_size * sizeof (short)); unsigned short *sorted = g_malloc (frame_size * sizeof (short));
memcpy (sorted, raw_frame, frame_size * sizeof (short)); memcpy (sorted, raw_frame, frame_size * sizeof (short));
qsort (sorted, frame_size, sizeof (short), cmp_short); qsort (sorted, frame_size, sizeof (short), cmp_short);
lvl0 = sorted[0]; lvl0 = sorted[0];
@@ -287,6 +288,7 @@ elan_process_frame_thirds (unsigned short *raw_frame,
g_free (sorted); g_free (sorted);
unsigned short px; unsigned short px;
for (int i = 0; i < frame_size; i++) for (int i = 0; i < frame_size; i++)
{ {
px = raw_frame[i]; px = raw_frame[i];
@@ -320,6 +322,9 @@ elan_submit_image (FpImageDevice *dev)
g_slist_foreach (raw_frames, (GFunc) self->process_frame, &frames); g_slist_foreach (raw_frames, (GFunc) self->process_frame, &frames);
fpi_do_movement_estimation (&assembling_ctx, frames); fpi_do_movement_estimation (&assembling_ctx, frames);
img = fpi_assemble_frames (&assembling_ctx, frames); img = fpi_assemble_frames (&assembling_ctx, frames);
img->flags |= FPI_IMAGE_PARTIAL;
g_slist_free_full (frames, g_free);
fpi_image_device_image_captured (dev, img); fpi_image_device_image_captured (dev, img);
} }
@@ -479,7 +484,7 @@ stop_capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
/* The device is inactive at this point. */ /* The device is inactive at this point. */
self->dev_state = FP_IMAGE_DEVICE_STATE_INACTIVE; self->active = FALSE;
if (self->deactivating) if (self->deactivating)
{ {
@@ -497,16 +502,15 @@ stop_capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
} }
static void static void
elan_stop_capture (FpDevice *dev) elan_stop_capture (FpiDeviceElan *self)
{ {
FpiDeviceElan *self = FPI_DEVICE_ELAN (dev);
G_DEBUG_HERE (); G_DEBUG_HERE ();
elan_dev_reset_state (self); elan_dev_reset_state (self);
FpiSsm *ssm = FpiSsm *ssm =
fpi_ssm_new (dev, stop_capture_run_state, STOP_CAPTURE_NUM_STATES); fpi_ssm_new (FP_DEVICE (self), stop_capture_run_state, STOP_CAPTURE_NUM_STATES);
fpi_ssm_start (ssm, stop_capture_complete); fpi_ssm_start (ssm, stop_capture_complete);
} }
@@ -536,8 +540,6 @@ capture_run_state (FpiSsm *ssm, FpDevice *dev)
break; break;
case CAPTURE_READ_DATA: case CAPTURE_READ_DATA:
self->dev_state = FP_IMAGE_DEVICE_STATE_CAPTURE;
/* 0x55 - finger present /* 0x55 - finger present
* 0xff - device not calibrated (probably) */ * 0xff - device not calibrated (probably) */
if (self->last_read && self->last_read[0] == 0x55) if (self->last_read && self->last_read[0] == 0x55)
@@ -547,6 +549,10 @@ capture_run_state (FpiSsm *ssm, FpDevice *dev)
} }
else else
{ {
/* XXX: The timeout is emulated incorrectly, resulting in a zero byte read. */
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)); fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
} }
break; break;
@@ -579,8 +585,6 @@ capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
G_DEBUG_HERE (); G_DEBUG_HERE ();
/* XXX: cancellation was specially handled by doing nothing! */
/* either max frames captured or timed out waiting for the next frame */ /* either max frames captured or timed out waiting for the next frame */
if (!error || if (!error ||
(g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT) && (g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT) &&
@@ -603,18 +607,22 @@ capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
fpi_image_device_session_error (dev, error); fpi_image_device_session_error (dev, error);
} }
/* Note: We always stop capturing even if that may not be needed always.
* Doing this between captures appears to make it at least less likely for
* devices to end up in a bad state.
*/
elan_stop_capture (self);
} }
static void static void
elan_capture (FpDevice *dev) elan_capture (FpiDeviceElan *self)
{ {
FpiDeviceElan *self = FPI_DEVICE_ELAN (dev);
G_DEBUG_HERE (); G_DEBUG_HERE ();
elan_dev_reset_state (self); elan_dev_reset_state (self);
FpiSsm *ssm = FpiSsm *ssm =
fpi_ssm_new (dev, capture_run_state, CAPTURE_NUM_STATES); fpi_ssm_new (FP_DEVICE (self), capture_run_state, CAPTURE_NUM_STATES);
fpi_ssm_start (ssm, capture_complete); fpi_ssm_start (ssm, capture_complete);
} }
@@ -729,7 +737,7 @@ calibrate_run_state (FpiSsm *ssm, FpDevice *dev)
fp_dbg ("calibration failed"); fp_dbg ("calibration failed");
fpi_ssm_mark_failed (ssm, fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"Callibration failed!")); "Calibration failed!"));
} }
break; break;
@@ -765,34 +773,33 @@ calibrate_run_state (FpiSsm *ssm, FpDevice *dev)
static void static void
calibrate_complete (FpiSsm *ssm, FpDevice *dev, GError *error) calibrate_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
{ {
FpiDeviceElan *self = FPI_DEVICE_ELAN (dev);
G_DEBUG_HERE (); G_DEBUG_HERE ();
if (error) if (error)
{ {
self->dev_state = FP_IMAGE_DEVICE_STATE_INACTIVE;
fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error); fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error);
elan_stop_capture (FPI_DEVICE_ELAN (dev));
} }
else else
{ {
elan_capture (dev); elan_capture (FPI_DEVICE_ELAN (dev));
} }
} }
static void static void
elan_calibrate (FpDevice *dev) elan_calibrate (FpiDeviceElan *self)
{ {
FpiDeviceElan *self = FPI_DEVICE_ELAN (dev);
G_DEBUG_HERE (); G_DEBUG_HERE ();
elan_dev_reset_state (self); elan_dev_reset_state (self);
g_return_if_fail (!self->active);
self->active = TRUE;
self->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS; self->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS;
FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (dev), calibrate_run_state, FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (self), calibrate_run_state,
CALIBRATE_NUM_STATES); CALIBRATE_NUM_STATES);
fpi_ssm_start (ssm, calibrate_complete); fpi_ssm_start (ssm, calibrate_complete);
} }
@@ -888,6 +895,7 @@ elan_activate (FpImageDevice *dev)
FpiSsm *ssm = FpiSsm *ssm =
fpi_ssm_new (FP_DEVICE (dev), activate_run_state, fpi_ssm_new (FP_DEVICE (dev), activate_run_state,
ACTIVATE_NUM_STATES); ACTIVATE_NUM_STATES);
fpi_ssm_start (ssm, activate_complete); fpi_ssm_start (ssm, activate_complete);
} }
@@ -945,94 +953,18 @@ dev_activate (FpImageDevice *dev)
} }
static void static void
elan_change_state (FpImageDevice *idev) dev_change_state (FpImageDevice *dev, FpiImageDeviceState state)
{
FpDevice *dev = FP_DEVICE (idev);
FpiDeviceElan *self = FPI_DEVICE_ELAN (dev);
FpImageDeviceState next_state = self->dev_state_next;
if (self->dev_state == next_state)
{
fp_dbg ("already in %d", next_state);
return;
}
else
{
fp_dbg ("changing to %d", next_state);
}
switch (next_state)
{
case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
/* activation completed or another enroll stage started */
self->dev_state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON;
elan_calibrate (dev);
break;
case FP_IMAGE_DEVICE_STATE_CAPTURE:
/* not used */
break;
case FP_IMAGE_DEVICE_STATE_INACTIVE:
case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF:
elan_stop_capture (dev);
break;
}
}
static void
elan_change_state_async (FpDevice *dev,
void *data)
{
g_message ("state change dev: %p", dev);
elan_change_state (FP_IMAGE_DEVICE (dev));
}
static void
dev_change_state (FpImageDevice *dev, FpImageDeviceState state)
{ {
FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); FpiDeviceElan *self = FPI_DEVICE_ELAN (dev);
GSource *timeout;
G_DEBUG_HERE (); G_DEBUG_HERE ();
/* Inactive and await finger off are equivalent for the elan driver. */ /* Note: We always calibrate even if that may not be needed always.
if (state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) * Doing this for each capture appears to make it at least less likely for
state = FP_IMAGE_DEVICE_STATE_INACTIVE; * devices to end up in a bad state.
*/
if (self->dev_state_next == state) if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
fp_dbg ("change to state %d already queued", state); elan_calibrate (self);
switch (state)
{
case FP_IMAGE_DEVICE_STATE_INACTIVE:
case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: {
char *name;
/* schedule state change instead of calling it directly to allow all actions
* related to the previous state to complete */
self->dev_state_next = state;
timeout = fpi_device_add_timeout (FP_DEVICE (dev), 10,
elan_change_state_async,
NULL, NULL);
name = g_strdup_printf ("dev_change_state to %d", state);
g_source_set_name (timeout, name);
g_free (name);
break;
}
case FP_IMAGE_DEVICE_STATE_CAPTURE:
/* TODO MAYBE: split capture ssm into smaller ssms and use this state */
self->dev_state = state;
self->dev_state_next = state;
break;
default:
g_assert_not_reached ();
}
} }
static void static void
@@ -1042,19 +974,14 @@ dev_deactivate (FpImageDevice *dev)
G_DEBUG_HERE (); G_DEBUG_HERE ();
if (self->dev_state == FP_IMAGE_DEVICE_STATE_INACTIVE) if (!self->active)
{
/* The device is inactive already, complete the operation immediately. */ /* The device is inactive already, complete the operation immediately. */
fpi_image_device_deactivate_complete (dev, NULL); fpi_image_device_deactivate_complete (dev, NULL);
}
else else
{
/* The device is not yet inactive, flag that we are deactivating (and /* The device is not yet inactive, flag that we are deactivating (and
* need to signal back deactivation) and then ensure we will change * need to signal back deactivation).
* to the inactive state eventually. */ * Note that any running capture will be cancelled already if needed. */
self->deactivating = TRUE; self->deactivating = TRUE;
dev_change_state (dev, FP_IMAGE_DEVICE_STATE_INACTIVE);
}
} }
static void static void

View File

@@ -18,8 +18,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __ELAN_H #pragma once
#define __ELAN_H
#include <glib.h> #include <glib.h>
@@ -71,6 +70,9 @@
#define ELAN_CMD_TIMEOUT 10000 #define ELAN_CMD_TIMEOUT 10000
#define ELAN_FINGER_TIMEOUT 200 #define ELAN_FINGER_TIMEOUT 200
G_DECLARE_FINAL_TYPE (FpiDeviceElan, fpi_device_elan, FPI, DEVICE_ELAN,
FpImageDevice);
struct elan_cmd struct elan_cmd
{ {
unsigned char cmd[ELAN_CMD_LEN]; unsigned char cmd[ELAN_CMD_LEN];
@@ -212,6 +214,7 @@ static const FpIdEntry elan_id_table[] = {
{.vid = ELAN_VEND_ID, .pid = 0x0c32, .driver_data = ELAN_ALL_DEV}, {.vid = ELAN_VEND_ID, .pid = 0x0c32, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c33, .driver_data = ELAN_ALL_DEV}, {.vid = ELAN_VEND_ID, .pid = 0x0c33, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c42, .driver_data = ELAN_0C42}, {.vid = ELAN_VEND_ID, .pid = 0x0c42, .driver_data = ELAN_0C42},
{.vid = ELAN_VEND_ID, .pid = 0x0c4d, .driver_data = ELAN_ALL_DEV},
{.vid = 0, .pid = 0, .driver_data = 0}, {.vid = 0, .pid = 0, .driver_data = 0},
}; };
@@ -219,10 +222,8 @@ static void elan_cmd_done (FpiSsm *ssm);
static void elan_cmd_read (FpiSsm *ssm, static void elan_cmd_read (FpiSsm *ssm,
FpDevice *dev); FpDevice *dev);
static void elan_calibrate (FpDevice *dev); static void elan_calibrate (FpiDeviceElan *self);
static void elan_capture (FpDevice *dev); static void elan_capture (FpiDeviceElan *self);
static void dev_change_state (FpImageDevice *dev, static void dev_change_state (FpImageDevice *dev,
FpImageDeviceState state); FpiImageDeviceState state);
#endif

View File

@@ -36,7 +36,6 @@
#define FP_COMPONENT "etes603" #define FP_COMPONENT "etes603"
#include "drivers_api.h" #include "drivers_api.h"
#include "driver_ids.h"
/* libusb defines */ /* libusb defines */
#define EP_IN 0x81 #define EP_IN 0x81
@@ -860,13 +859,15 @@ m_capture_state (FpiSsm *ssm, FpDevice *dev)
} }
else else
{ {
FpImage *img;
unsigned int img_size;
/* Remove empty parts 2 times for the 2 frames */ /* Remove empty parts 2 times for the 2 frames */
process_removefpi_end (self); process_removefpi_end (self);
process_removefpi_end (self); process_removefpi_end (self);
img_size = self->fp_height * FE_WIDTH;
img = fp_image_new (FE_WIDTH, self->fp_height); if (self->fp_height >= FE_WIDTH)
{
FpImage *img = fp_image_new (FE_WIDTH, self->fp_height);
unsigned int img_size = self->fp_height * FE_WIDTH;
/* Images received are white on black, so invert it. */ /* Images received are white on black, so invert it. */
/* TODO detect sweep direction */ /* TODO detect sweep direction */
img->flags = FPI_IMAGE_COLORS_INVERTED | FPI_IMAGE_V_FLIPPED; img->flags = FPI_IMAGE_COLORS_INVERTED | FPI_IMAGE_V_FLIPPED;
@@ -875,6 +876,12 @@ m_capture_state (FpiSsm *ssm, FpDevice *dev)
fp_dbg ("Sending the raw fingerprint image (%dx%d)", fp_dbg ("Sending the raw fingerprint image (%dx%d)",
img->width, img->height); img->width, img->height);
fpi_image_device_image_captured (idev, img); fpi_image_device_image_captured (idev, img);
}
else
{
fpi_image_device_retry_scan (idev, FP_DEVICE_RETRY_TOO_SHORT);
}
fpi_image_device_report_finger_status (idev, FALSE); fpi_image_device_report_finger_status (idev, FALSE);
fpi_ssm_mark_completed (ssm); fpi_ssm_mark_completed (ssm);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
/*
* Goodix Moc driver for libfprint
* Copyright (C) 2019 Shenzhen Goodix Technology Co., Ltd.
*
* 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 (FpiDeviceGoodixMoc, fpi_device_goodixmoc, FPI, DEVICE_GOODIXMOC, FpDevice)
typedef enum {
FP_CMD_SEND = 0,
FP_CMD_GET_ACK,
FP_CMD_GET_DATA,
FP_CMD_NUM_STATES,
} FpCmdState;
typedef enum {
FP_INIT_VERSION = 0,
FP_INIT_CONFIG,
FP_INIT_NUM_STATES,
} FpInitState;
typedef enum {
FP_ENROLL_ENUM = 0,
FP_ENROLL_IDENTIFY,
FP_ENROLL_CREATE,
FP_ENROLL_CAPTURE,
FP_ENROLL_UPDATE,
FP_ENROLL_WAIT_FINGER_UP,
FP_ENROLL_CHECK_DUPLICATE,
FP_ENROLL_COMMIT,
FP_ENROLL_NUM_STATES,
} FpEnrollState;
typedef enum {
FP_VERIFY_CAPTURE = 0,
FP_VERIFY_IDENTIFY,
FP_VERIFY_NUM_STATES,
} FpVerifyState;

View File

@@ -0,0 +1,422 @@
/*
* Goodix Moc driver for libfprint
* Copyright (C) 2019 Shenzhen Goodix Technology Co., Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <glib.h>
#include "goodix_proto.h"
/*
* Crc functions
*/
#define WIDTH (8 * sizeof (uint32_t))
#define FINAL_XOR_VALUE 0xFFFFFFFF
#define REFLECT_DATA(X) ((uint8_t) reflect ((X), 8))
#define REFLECT_REMAINDER(X) ((unsigned int) reflect ((X), WIDTH))
uint8_t
gx_proto_crc8_calc (uint8_t *lubp_date, uint32_t lui_len)
{
const uint8_t *data = lubp_date;
unsigned int crc = 0;
int i, j;
for (j = lui_len; j; j--, data++)
{
crc ^= (*data << 8);
for (i = 8; i; i--)
{
if (crc & 0x8000)
crc ^= (0x1070 << 3);
crc <<= 1;
}
}
crc >>= 8;
crc = ~crc;
return (uint8_t) crc;
}
typedef struct
{
uint32_t crc;
} gf_crc32_context;
static uint32_t s_crc_table[256] =
{ 0x0, 0x4c11db7, 0x9823b6e, 0xd4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x18aeb13, 0x54bf6a4, 0x808d07d, 0xcc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x315d626, 0x7d4cb91, 0xa97ed48, 0xe56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x29f3d35, 0x65e2082, 0xb1d065b, 0xfdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4};
static uint32_t
reflect (uint32_t data, uint8_t n_bits)
{
unsigned long reflection = 0x00000000;
uint8_t bit;
/*
* Reflect the data about the center bit.
*/
for (bit = 0; bit < n_bits; ++bit)
{
/*
* If the LSB bit is set, set the reflection of it.
*/
if (data & 0x01)
reflection |= (1 << ((n_bits - 1) - bit));
data = (data >> 1);
}
return reflection;
}
static void
crc32_init (gf_crc32_context *ctx)
{
ctx->crc = 0xFFFFFFFF;
}
static void
crc32_update (gf_crc32_context *ctx, const uint8_t *message, uint32_t n_bytes)
{
uint8_t data;
uint32_t byte;
/*
* Divide the message by the polynomial, a byte at a time.
*/
for (byte = 0; byte < n_bytes; ++byte)
{
data = REFLECT_DATA (message[byte]) ^ (ctx->crc >> (WIDTH - 8));
ctx->crc = s_crc_table[data] ^ (ctx->crc << 8);
}
}
static void
crc32_final (gf_crc32_context *ctx, uint8_t *md)
{
ctx->crc = (REFLECT_REMAINDER (ctx->crc) ^ FINAL_XOR_VALUE);
memcpy (md, &ctx->crc, 4);
}
uint8_t
gx_proto_crc32_calc (uint8_t *pchMsg, uint32_t wDataLen, uint8_t *pchMsgDst)
{
gf_crc32_context context = { 0 };
if (!pchMsg)
return 0;
crc32_init (&context);
crc32_update (&context, pchMsg, wDataLen);
crc32_final (&context, pchMsgDst);
return 1;
}
/*
* protocol
*
*/
static uint8_t dump_seq = 0;
static void
init_pack_header (
ppack_header pheader,
uint16_t len,
uint16_t cmd,
uint8_t packagenum
)
{
g_assert (pheader);
memset (pheader, 0, sizeof (*pheader));
pheader->cmd0 = HIBYTE (cmd);
pheader->cmd1 = LOBYTE (cmd);
pheader->packagenum = packagenum;
pheader->reserved = dump_seq++;
pheader->len = len + PACKAGE_CRC_SIZE;
pheader->crc8 = gx_proto_crc8_calc ((uint8_t *) pheader, 6);
pheader->rev_crc8 = ~pheader->crc8;
}
int
gx_proto_build_package (uint8_t *ppackage,
uint32_t *package_len,
uint16_t cmd,
const uint8_t *payload,
uint32_t payload_size)
{
pack_header header;
if (!ppackage || !package_len)
return -1;
if(*package_len < (payload_size + PACKAGE_HEADER_SIZE + PACKAGE_CRC_SIZE))
return -1;
init_pack_header (&header, payload_size, cmd, 0);
memcpy (ppackage, &header, PACKAGE_HEADER_SIZE);
memcpy (ppackage + PACKAGE_HEADER_SIZE, payload, payload_size);
gx_proto_crc32_calc (ppackage, PACKAGE_HEADER_SIZE + payload_size, ppackage + PACKAGE_HEADER_SIZE + payload_size);
return 0;
}
int
gx_proto_parse_header (
uint8_t *buffer,
uint32_t buffer_len,
pack_header *pheader)
{
if (!buffer || !pheader)
return -1;
if (buffer_len < PACKAGE_HEADER_SIZE)
return -1;
memcpy (pheader, buffer, sizeof (pack_header));
pheader->len = GUINT16_FROM_LE (*(buffer + 4));
pheader->len -= PACKAGE_CRC_SIZE;
return 0;
}
static int
gx_proto_parse_fingerid (
uint8_t * fid_buffer,
uint16_t fid_buffer_size,
ptemplate_format_t template
)
{
uint8_t * buffer = NULL;
uint16_t Offset = 0;
if (!template || !fid_buffer)
return -1;
if (fid_buffer_size < 70)
return -1;
buffer = fid_buffer;
Offset = 0;
if (buffer[Offset++] != 67)
return -1;
template->type = buffer[Offset++];
template->finger_index = buffer[Offset++];
Offset++;
memcpy (template->accountid, &buffer[Offset], 32);
Offset += 32;
memcpy (template->tid, &buffer[Offset], 32);
Offset += 32; // Offset == 68
template->payload.size = buffer[Offset++];
memset (template->payload.data, 0, 56);
memcpy (template->payload.data, &buffer[Offset], template->payload.size);
return 0;
}
int
gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint32_t buffer_len, pgxfp_cmd_response_t presp)
{
uint32_t offset = 0;
uint8_t *fingerlist = NULL;
if (!buffer || !presp)
return -1;
if (buffer_len < 1)
return -1;
presp->result = buffer[0];
switch (HIBYTE (cmd))
{
case RESPONSE_PACKAGE_CMD:
{
presp->parse_msg.ack_cmd = buffer[1];
}
break;
case MOC_CMD0_UPDATE_CONFIG:
case MOC_CMD0_COMMITENROLLMENT:
case MOC_CMD0_DELETETEMPLATE:
break;
case MOC_CMD0_GET_VERSION:
memcpy (&presp->version_info, buffer + 1, sizeof (gxfp_version_info_t));
break;
case MOC_CMD0_CAPTURE_DATA:
if (LOBYTE (cmd) == MOC_CMD1_DEFAULT)
{
presp->capture_data_resp.img_quality = buffer[1];
presp->capture_data_resp.img_coverage = buffer[2];
}
break;
case MOC_CMD0_ENROLL_INIT:
if (presp->result == GX_SUCCESS)
memcpy (&presp->enroll_init.tid, &buffer[1], TEMPLATE_ID_SIZE);
break;
case MOC_CMD0_ENROLL:
presp->enroll_update.rollback = (buffer[0] < 0x80) ? false : true;
presp->enroll_update.img_overlay = buffer[1];
presp->enroll_update.img_preoverlay = buffer[2];
break;
case MOC_CMD0_CHECK4DUPLICATE:
presp->check_duplicate_resp.duplicate = (presp->result == 0) ? false : true;
if (presp->check_duplicate_resp.duplicate)
{
uint16_t tid_size = GUINT16_FROM_LE (*(buffer + 1));
memcpy (&presp->check_duplicate_resp.template, buffer + 3, tid_size);
}
break;
case MOC_CMD0_GETFINGERLIST:
if (presp->result != GX_SUCCESS)
break;
presp->finger_list_resp.finger_num = buffer[1];
if (presp->finger_list_resp.finger_num > FP_MAX_FINGERNUM)
{
presp->finger_list_resp.finger_num = 0;
presp->result = GX_ERROR_NO_AVAILABLE_SPACE;
break;
}
fingerlist = buffer + 2;
for(uint8_t num = 0; num < presp->finger_list_resp.finger_num; num++)
{
uint16_t fingerid_length = GUINT16_FROM_LE (*(fingerlist + offset));
offset += 2;
if (gx_proto_parse_fingerid (fingerlist + offset,
fingerid_length,
&presp->finger_list_resp.finger_list[num]) != 0)
{
g_error ("parse fingerlist error");
presp->finger_list_resp.finger_num = 0;
presp->result = GX_FAILED;
break;
}
offset += fingerid_length;
}
break;
case MOC_CMD0_IDENTIFY:
{
uint32_t score = 0;
uint8_t study = 0;
uint16_t fingerid_size = 0;
presp->verify.match = (buffer[0] == 0) ? true : false;
if (presp->verify.match)
{
offset += 1;
presp->verify.rejectdetail = GUINT16_FROM_LE (*(buffer + offset));
offset += 2;
score = GUINT32_FROM_LE (*(buffer + offset));
offset += 4;
study = GUINT16_FROM_LE (*(buffer + offset));
offset += 1;
fingerid_size = GUINT16_FROM_LE (*(buffer + offset));
offset += 2;
if (gx_proto_parse_fingerid (buffer + offset, fingerid_size, &presp->verify.template) != 0)
{
presp->result = GX_FAILED;
break;
}
g_debug ("match, score: %d, study: %d", score, study);
}
}
break;
case MOC_CMD0_FINGER_MODE:
presp->finger_status.status = buffer[0];
break;
default:
break;
}
return 0;
}
static uint8_t sensor_config[26] = {
0x00, 0x00, 0x64, 0x50, 0x0f, 0x41, 0x08, 0x0a, 0x18, 0x00, 0x00, 0x23, 0x00,
0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x05, 0x05
};
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));
//NOTICE: Do not change any value!
memcpy (&pconfig->config, sensor_config, 26);
pconfig->reserved[0] = 1;
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;
}

View File

@@ -0,0 +1,232 @@
/*
* Goodix Moc driver for libfprint
* Copyright (C) 2019 Shenzhen Goodix Technology Co., Ltd.
*
* 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 <stdint.h>
#include <stdbool.h>
#include <string.h>
#define PACKAGE_CRC_SIZE (4)
#define PACKAGE_HEADER_SIZE (8)
#define FP_MAX_FINGERNUM (10)
#define TEMPLATE_ID_SIZE (32)
#define GX_VERSION_LEN (8)
/* Type covert */
#define MAKE_CMD_EX(cmd0, cmd1) ((uint16_t) (((cmd0) << 8) | (cmd1)))
#define LOBYTE(value) ((uint8_t) (value))
#define HIBYTE(value) ((uint8_t) (((uint16_t) (value) >> 8) & 0xFF))
/* Error code */
#define GX_SUCCESS 0x00
#define GX_FAILED 0x80
#define GX_ERROR_FINGER_ID_NOEXIST 0x9C
#define GX_ERROR_TEMPLATE_INCOMPLETE 0xB8
#define GX_ERROR_WAIT_FINGER_UP_TIMEOUT 0xC7
#define GX_ERROR_NO_AVAILABLE_SPACE 0x8F
/* Command Type Define */
#define RESPONSE_PACKAGE_CMD 0xAA
#define MOC_CMD0_ENROLL 0xA0
#define MOC_CMD0_ENROLL_INIT 0xA1
#define MOC_CMD0_CAPTURE_DATA 0xA2
#define MOC_CMD0_CHECK4DUPLICATE 0xA3
#define MOC_CMD0_COMMITENROLLMENT 0xA4
#define MOC_CMD0_IDENTIFY 0xA5
#define MOC_CMD0_GETFINGERLIST 0xA6
#define MOC_CMD0_DELETETEMPLATE 0xA7
#define MOC_CMD1_DEFAULT 0x00
#define MOC_CMD1_UNTIL_DOWN 0x00
#define MOC_CMD1_WHEN_DOWN 0x01
#define MOC_CMD1_DELETE_TEMPLATE 0x04
#define MOC_CMD1_DELETE_ALL 0x01
#define MOC_CMD0_GET_VERSION 0xD0
#define MOC_CMD0_UPDATE_CONFIG 0xC0
#define MOC_CMD1_NWRITE_CFG_TO_FLASH 0x00
#define MOC_CMD1_WRITE_CFG_TO_FLASH 0x01
#define MOC_CMD0_FINGER_MODE 0xB0
#define MOC_CMD1_GET_FINGER_MODE 0x00
#define MOC_CMD1_SET_FINGER_DOWN 0x01
#define MOC_CMD1_SET_FINGER_UP 0x02
/* */
typedef struct _gxfp_version_info
{
uint8_t format[2];
uint8_t fwtype[GX_VERSION_LEN];
uint8_t fwversion[GX_VERSION_LEN];
uint8_t customer[GX_VERSION_LEN];
uint8_t mcu[GX_VERSION_LEN];
uint8_t sensor[GX_VERSION_LEN];
uint8_t algversion[GX_VERSION_LEN];
uint8_t interface[GX_VERSION_LEN];
uint8_t protocol[GX_VERSION_LEN];
uint8_t flashVersion[GX_VERSION_LEN];
uint8_t reserved[62];
} gxfp_version_info_t, *pgxfp_version_info_t;
typedef struct _gxfp_parse_msg
{
uint8_t ack_cmd;
bool has_no_config;
} gxfp_parse_msg_t, *pgxfp_parse_msg_t;
typedef struct _gxfp_enroll_init
{
uint8_t tid[TEMPLATE_ID_SIZE];
} gxfp_enroll_init_t, *pgxfp_enroll_init_t;
#pragma pack(push, 1)
typedef struct _template_format
{
uint8_t type;
uint8_t finger_index;
uint8_t accountid[32];
uint8_t tid[32];
struct
{
uint32_t size;
uint8_t data[56];
} payload;
uint8_t reserve[2];
} template_format_t, *ptemplate_format_t;
#pragma pack(pop)
typedef struct _gxfp_verify
{
bool match;
uint32_t rejectdetail;
template_format_t template;
} gxfp_verify_t, *pgxfp_verify_t;
typedef struct _gxfp_capturedata
{
uint8_t img_quality;
uint8_t img_coverage;
} gxfp_capturedata_t, *pgxfp_capturedata_t;
typedef struct _gxfp_check_duplicate
{
bool duplicate;
template_format_t template;
} gxfp_check_duplicate_t, *pgxfp_check_duplicate_t;
typedef struct _gxfp_enroll_update
{
bool rollback;
uint8_t img_overlay;
uint8_t img_preoverlay;
} gxfp_enroll_update_t, *Pgxfp_enroll_update_t;
typedef struct _gxfp_enum_fingerlist
{
uint8_t finger_num;
template_format_t finger_list[FP_MAX_FINGERNUM];
} gxfp_enum_fingerlist_t, *pgxfp_enum_fingerlist_t;
typedef struct _gxfp_enroll_commit
{
uint8_t result;
} gxfp_enroll_commit_t, *pgxfp_enroll_commit_t;
typedef struct _fp_finger_status
{
uint8_t status;
} fp_finger_status_t, *pfp_finger_status_t;
typedef struct _fp_cmd_response
{
uint8_t result;
union
{
gxfp_parse_msg_t parse_msg;
gxfp_verify_t verify;
gxfp_enroll_init_t enroll_init;
gxfp_capturedata_t capture_data_resp;
gxfp_check_duplicate_t check_duplicate_resp;
gxfp_enroll_commit_t enroll_commit;
gxfp_enroll_update_t enroll_update;
gxfp_enum_fingerlist_t finger_list_resp;
gxfp_version_info_t version_info;
fp_finger_status_t finger_status;
};
} gxfp_cmd_response_t, *pgxfp_cmd_response_t;
typedef struct _pack_header
{
uint8_t cmd0;
uint8_t cmd1;
uint8_t packagenum;
uint8_t reserved;
uint16_t len;
uint8_t crc8;
uint8_t rev_crc8;
} pack_header, *ppack_header;
typedef struct _gxfp_sensor_cfg
{
uint8_t config[26];
uint8_t reserved[98];
uint8_t crc_value[4];
} gxfp_sensor_cfg_t, *pgxfp_sensor_cfg_t;
/* */
int gx_proto_build_package (uint8_t *ppackage,
uint32_t *package_len,
uint16_t cmd,
const uint8_t *payload,
uint32_t payload_size);
int gx_proto_parse_header (uint8_t *buffer,
uint32_t buffer_len,
pack_header *pheader);
int gx_proto_parse_body (uint16_t cmd,
uint8_t *buffer,
uint32_t buffer_len,
pgxfp_cmd_response_t presponse);
int gx_proto_init_sensor_config (pgxfp_sensor_cfg_t pconfig);
uint8_t gx_proto_crc8_calc (uint8_t *lubp_date,
uint32_t lui_len);
uint8_t gx_proto_crc32_calc (uint8_t *pchMsg,
uint32_t wDataLen,
uint8_t *pchMsgDst);

View File

@@ -17,8 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef _BMKT_H_ #pragma once
#define _BMKT_H_
/**< User ID maximum length allowed */ /**< User ID maximum length allowed */
#define BMKT_MAX_USER_ID_LEN 100 #define BMKT_MAX_USER_ID_LEN 100
@@ -228,5 +227,3 @@ typedef struct bmkt_user_id
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* _BMKT_H_ */

View File

@@ -206,6 +206,7 @@ parse_get_enrolled_users_report (bmkt_msg_resp_t *msg_resp, bmkt_response_t *res
get_enroll_templates_resp->query_sequence = extract8 (msg_resp->payload, &offset); get_enroll_templates_resp->query_sequence = extract8 (msg_resp->payload, &offset);
int n = 0; int n = 0;
for (n = 0; n < BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH; n++) for (n = 0; n < BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH; n++)
{ {
if (offset >= msg_resp->payload_len) if (offset >= msg_resp->payload_len)

View File

@@ -16,10 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#pragma once
#ifndef BMKT_MESSAGE_H_
#define BMKT_MESSAGE_H_
#define BMKT_MESSAGE_HEADER_ID 0xFE #define BMKT_MESSAGE_HEADER_ID 0xFE
#define BMKT_MESSAGE_HEADER_LEN (4) #define BMKT_MESSAGE_HEADER_LEN (4)
@@ -90,4 +87,3 @@ int bmkt_parse_message_header (uint8_t *resp_buf,
bmkt_msg_resp_t *msg_resp); bmkt_msg_resp_t *msg_resp);
int bmkt_parse_message_payload (bmkt_msg_resp_t *msg_resp, int bmkt_parse_message_payload (bmkt_msg_resp_t *msg_resp,
bmkt_response_t *resp); bmkt_response_t *resp);
#endif /* BMKT_MESSAGE_H_ */

View File

@@ -17,9 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#pragma once
#ifndef _BMKT_RESPONSE_H_
#define _BMKT_RESPONSE_H_
#include "bmkt.h" #include "bmkt.h"
@@ -318,7 +316,7 @@ typedef struct bmkt_init_resp
*/ */
typedef struct bmkt_enroll_resp typedef struct bmkt_enroll_resp
{ {
int progress; /**< Shows current progress stutus [0-100] */ int progress; /**< Shows current progress status [0-100] */
uint8_t finger_id; /**< User's finger id [1-10] */ uint8_t finger_id; /**< User's finger id [1-10] */
uint8_t user_id[BMKT_MAX_USER_ID_LEN]; /**< User name to be enrolled */ uint8_t user_id[BMKT_MAX_USER_ID_LEN]; /**< User name to be enrolled */
} bmkt_enroll_resp_t; } bmkt_enroll_resp_t;
@@ -485,5 +483,3 @@ typedef struct bmkt_response
int complete; /**< Operation completion status 1: complete / 0: not completed */ int complete; /**< Operation completion status 1: complete / 0: not completed */
bmkt_response_data_t response; /**< Operation specific response union */ bmkt_response_data_t response; /**< Operation specific response union */
} bmkt_response_t; } bmkt_response_t;
#endif /* _BMKT_RESPONSE_H_ */

View File

@@ -16,8 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef _SENSOR_H_ #pragma once
#define _SENSOR_H_
#include "usb_transport.h" #include "usb_transport.h"
#define BMKT_MAX_PENDING_SESSIONS 2 #define BMKT_MAX_PENDING_SESSIONS 2
@@ -84,4 +83,3 @@ int bmkt_sensor_handle_response (bmkt_sensor_t *sensor,
bmkt_msg_resp_t *msg_resp); bmkt_msg_resp_t *msg_resp);
int bmkt_sensor_send_async_read_command (bmkt_sensor_t *sensor); int bmkt_sensor_send_async_read_command (bmkt_sensor_t *sensor);
#endif /* _SENSOR_H_ */

View File

@@ -29,13 +29,18 @@ G_DEFINE_TYPE (FpiDeviceSynaptics, fpi_device_synaptics, FP_TYPE_DEVICE)
static const FpIdEntry id_table[] = { static const FpIdEntry id_table[] = {
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0xBD, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0xBD, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0xE9, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0xDF, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0xF9, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0xFC, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0xC2, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0xC9, },
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
}; };
static void static void
cmd_recieve_cb (FpiUsbTransfer *transfer, cmd_receive_cb (FpiUsbTransfer *transfer,
FpDevice *device, FpDevice *device,
gpointer user_data, gpointer user_data,
GError *error) GError *error)
@@ -78,10 +83,17 @@ cmd_recieve_cb (FpiUsbTransfer *transfer,
if (msg_resp.payload[0] == 0x01) if (msg_resp.payload[0] == 0x01)
{ {
self->finger_on_sensor = TRUE; self->finger_on_sensor = TRUE;
fpi_device_report_finger_status_changes (device,
FP_FINGER_STATUS_PRESENT,
FP_FINGER_STATUS_NONE);
} }
else else
{ {
self->finger_on_sensor = FALSE; self->finger_on_sensor = FALSE;
fpi_device_report_finger_status_changes (device,
FP_FINGER_STATUS_NONE,
FP_FINGER_STATUS_PRESENT);
if (self->cmd_complete_on_removal) if (self->cmd_complete_on_removal)
{ {
fpi_ssm_mark_completed (transfer->ssm); fpi_ssm_mark_completed (transfer->ssm);
@@ -168,7 +180,7 @@ cmd_recieve_cb (FpiUsbTransfer *transfer,
* depending on resp.complete. */ * depending on resp.complete. */
if (self->cmd_pending_transfer) if (self->cmd_pending_transfer)
fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_SEND_PENDING); fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_SEND_PENDING);
else if (!resp.complete) else if (!resp.complete || self->cmd_complete_on_removal)
fpi_ssm_next_state (transfer->ssm); /* SYNAPTICS_CMD_WAIT_INTERRUPT */ fpi_ssm_next_state (transfer->ssm); /* SYNAPTICS_CMD_WAIT_INTERRUPT */
else else
fpi_ssm_mark_completed (transfer->ssm); fpi_ssm_mark_completed (transfer->ssm);
@@ -193,12 +205,19 @@ cmd_interrupt_cb (FpiUsbTransfer *transfer,
fpi_ssm_mark_failed (transfer->ssm, error); fpi_ssm_mark_failed (transfer->ssm, error);
return; return;
} }
g_clear_pointer (&error, g_error_free);
if (transfer->buffer[0] & USB_ASYNC_MESSAGE_PENDING || error) if (transfer->buffer[0] & USB_ASYNC_MESSAGE_PENDING)
{
fpi_ssm_next_state (transfer->ssm); fpi_ssm_next_state (transfer->ssm);
}
else else
fpi_usb_transfer_submit (transfer, 1000, NULL, cmd_interrupt_cb, NULL); {
fpi_usb_transfer_submit (fpi_usb_transfer_ref (transfer),
0,
NULL,
cmd_interrupt_cb,
NULL);
}
} }
static void static void
@@ -234,7 +253,7 @@ synaptics_cmd_run_state (FpiSsm *ssm,
fpi_usb_transfer_submit (transfer, fpi_usb_transfer_submit (transfer,
5000, 5000,
NULL, NULL,
cmd_recieve_cb, cmd_receive_cb,
fpi_ssm_get_data (ssm)); fpi_ssm_get_data (ssm));
break; break;
@@ -279,17 +298,10 @@ cmd_ssm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
self->cmd_ssm = NULL; self->cmd_ssm = NULL;
/* Notify about the SSM failure from here instead. */ /* Notify about the SSM failure from here instead. */
if (error) if (error || self->cmd_complete_on_removal)
{
callback (self, NULL, error); callback (self, NULL, error);
}
else if (self->cmd_complete_on_removal)
{
callback (self, NULL, self->cmd_complete_error);
self->cmd_complete_error = NULL;
}
self->cmd_complete_on_removal = FALSE; self->cmd_complete_on_removal = FALSE;
g_clear_pointer (&self->cmd_complete_error, g_error_free);
} }
static void static void
@@ -328,7 +340,7 @@ synaptics_sensor_cmd (FpiDeviceSynaptics *self,
g_assert (payload || payload_len == 0); g_assert (payload || payload_len == 0);
/* seq_num of 0 means a normal command, -1 means the current commands /* seq_num of 0 means a normal command, -1 means the current commands
* sequence number should not be udpated (i.e. second async command which * sequence number should not be updated (i.e. second async command which
* may only be a cancellation currently). */ * may only be a cancellation currently). */
if (seq_num <= 0) if (seq_num <= 0)
{ {
@@ -517,45 +529,12 @@ list_msg_cb (FpiDeviceSynaptics *self,
get_enroll_templates_resp->templates[n].finger_id, get_enroll_templates_resp->templates[n].finger_id,
uid); uid);
fpi_print_set_type (print, FP_PRINT_RAW); fpi_print_set_type (print, FPI_PRINT_RAW);
fpi_print_set_device_stored (print, TRUE); fpi_print_set_device_stored (print, TRUE);
g_object_set (print, "fp-data", data, NULL); g_object_set (print, "fpi-data", data, NULL);
g_object_set (print, "description", get_enroll_templates_resp->templates[n].user_id, NULL); g_object_set (print, "description", get_enroll_templates_resp->templates[n].user_id, NULL);
/* The format has 24 bytes at the start and some dashes in the right places */ fpi_print_fill_from_user_id (print, userid);
if (g_str_has_prefix (userid, "FP1-") && strlen (userid) >= 24 &&
userid[12] == '-' && userid[14] == '-' && userid[23] == '-')
{
g_autofree gchar *copy = g_strdup (userid);
gint32 date_ymd;
GDate *date = NULL;
gint32 finger;
gchar *username;
/* Try to parse information from the string. */
copy[12] = '\0';
date_ymd = g_ascii_strtod (copy + 4, NULL);
if (date_ymd > 0)
date = g_date_new_dmy (date_ymd % 100,
(date_ymd / 100) % 100,
date_ymd / 10000);
else
date = g_date_new ();
fp_print_set_enroll_date (print, date);
g_date_free (date);
copy[14] = '\0';
finger = g_ascii_strtoll (copy + 13, NULL, 16);
fp_print_set_finger (print, finger);
/* We ignore the next chunk, it is just random data.
* Then comes the username; nobody is the default if the metadata
* is unknown */
username = copy + 24;
if (strlen (username) > 0 && g_strcmp0 (username, "nobody") != 0)
fp_print_set_username (print, username);
}
g_ptr_array_add (self->list_result, g_object_ref_sink (print)); g_ptr_array_add (self->list_result, g_object_ref_sink (print));
} }
@@ -582,6 +561,22 @@ list (FpDevice *device)
synaptics_sensor_cmd (self, 0, BMKT_CMD_GET_TEMPLATE_RECORDS, NULL, 0, list_msg_cb); synaptics_sensor_cmd (self, 0, BMKT_CMD_GET_TEMPLATE_RECORDS, NULL, 0, list_msg_cb);
} }
static void
verify_complete_after_finger_removal (FpiDeviceSynaptics *self)
{
FpDevice *device = FP_DEVICE (self);
if (self->finger_on_sensor)
{
fp_dbg ("delaying verify report until after finger removal!");
self->cmd_complete_on_removal = TRUE;
}
else
{
fpi_device_verify_complete (device, NULL);
}
}
static void static void
verify_msg_cb (FpiDeviceSynaptics *self, verify_msg_cb (FpiDeviceSynaptics *self,
bmkt_response_t *resp, bmkt_response_t *resp,
@@ -592,16 +587,13 @@ verify_msg_cb (FpiDeviceSynaptics *self,
if (error) if (error)
{ {
fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); fpi_device_verify_complete (device, error);
return; return;
} }
if (resp == NULL && self->cmd_complete_on_removal) if (resp == NULL && self->cmd_complete_on_removal)
{ {
fpi_device_verify_complete (device, fpi_device_verify_complete (device, NULL);
GPOINTER_TO_INT (self->cmd_complete_data),
NULL,
error);
return; return;
} }
@@ -612,6 +604,9 @@ verify_msg_cb (FpiDeviceSynaptics *self,
switch (resp->response_id) switch (resp->response_id)
{ {
case BMKT_RSP_VERIFY_READY: case BMKT_RSP_VERIFY_READY:
fpi_device_report_finger_status_changes (device,
FP_FINGER_STATUS_NEEDED,
FP_FINGER_STATUS_NONE);
fp_info ("Place Finger on the Sensor!"); fp_info ("Place Finger on the Sensor!");
break; break;
@@ -622,37 +617,38 @@ verify_msg_cb (FpiDeviceSynaptics *self,
case BMKT_RSP_VERIFY_FAIL: case BMKT_RSP_VERIFY_FAIL:
if (resp->result == BMKT_SENSOR_STIMULUS_ERROR) if (resp->result == BMKT_SENSOR_STIMULUS_ERROR)
{ {
fp_dbg ("delaying retry error until after finger removal!"); fp_info ("Match error occurred");
self->cmd_complete_on_removal = TRUE; fpi_device_verify_report (device, FPI_MATCH_ERROR, NULL,
self->cmd_complete_data = GINT_TO_POINTER (FPI_MATCH_ERROR); fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
self->cmd_complete_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); verify_complete_after_finger_removal (self);
} }
else if (resp->result == BMKT_FP_NO_MATCH) else if (resp->result == BMKT_FP_NO_MATCH)
{ {
fp_dbg ("delaying match failure until after finger removal!"); fp_info ("Print didn't match");
self->cmd_complete_on_removal = TRUE; fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL);
self->cmd_complete_data = GINT_TO_POINTER (FPI_MATCH_FAIL); verify_complete_after_finger_removal (self);
self->cmd_complete_error = NULL;
} }
else if (BMKT_FP_DATABASE_NO_RECORD_EXISTS) else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS)
{ {
fp_info ("Print is not in database"); fp_info ("Print is not in database");
fpi_device_verify_complete (device, fpi_device_verify_complete (device,
FPI_MATCH_ERROR,
NULL,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND)); fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
} }
else else
{ {
fp_warn ("Verify has failed: %d", resp->result); fp_warn ("Verify has failed: %d", resp->result);
fpi_device_verify_complete (device, FPI_MATCH_FAIL, NULL, NULL); fpi_device_verify_complete (device,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Unexpected result from device %d",
resp->result));
} }
break; break;
case BMKT_RSP_VERIFY_OK: case BMKT_RSP_VERIFY_OK:
fp_info ("Verify was successful! for user: %s finger: %d score: %f", fp_info ("Verify was successful! for user: %s finger: %d score: %f",
verify_resp->user_id, verify_resp->finger_id, verify_resp->match_result); verify_resp->user_id, verify_resp->finger_id, verify_resp->match_result);
fpi_device_verify_complete (device, FPI_MATCH_SUCCESS, NULL, NULL); fpi_device_verify_report (device, FPI_MATCH_SUCCESS, NULL, NULL);
fpi_device_verify_complete (device, NULL);
break; break;
} }
} }
@@ -670,13 +666,11 @@ verify (FpDevice *device)
fpi_device_get_verify_data (device, &print); fpi_device_get_verify_data (device, &print);
g_object_get (print, "fp-data", &data, NULL); g_object_get (print, "fpi-data", &data, NULL);
g_debug ("data is %p", data); g_debug ("data is %p", data);
if (!parse_print_data (data, &finger, &user_id, &user_id_len)) if (!parse_print_data (data, &finger, &user_id, &user_id_len))
{ {
fpi_device_verify_complete (device, fpi_device_verify_complete (device,
FPI_MATCH_ERROR,
NULL,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
return; return;
} }
@@ -686,6 +680,155 @@ verify (FpDevice *device)
synaptics_sensor_cmd (self, 0, BMKT_CMD_VERIFY_USER, user_id, user_id_len, verify_msg_cb); synaptics_sensor_cmd (self, 0, BMKT_CMD_VERIFY_USER, user_id, user_id_len, verify_msg_cb);
} }
static void
identify_complete_after_finger_removal (FpiDeviceSynaptics *self)
{
FpDevice *device = FP_DEVICE (self);
if (self->finger_on_sensor)
{
fp_dbg ("delaying identify report until after finger removal!");
self->cmd_complete_on_removal = TRUE;
}
else
{
fpi_device_identify_complete (device, NULL);
}
}
static void
identify_msg_cb (FpiDeviceSynaptics *self,
bmkt_response_t *resp,
GError *error)
{
FpDevice *device = FP_DEVICE (self);
if (error)
{
fpi_device_identify_complete (device, error);
return;
}
if (resp == NULL && self->cmd_complete_on_removal)
{
fpi_device_identify_complete (device, NULL);
return;
}
g_assert (resp != NULL);
switch (resp->response_id)
{
case BMKT_RSP_ID_READY:
fp_info ("Place Finger on the Sensor!");
break;
case BMKT_RSP_ID_FAIL:
if (resp->result == BMKT_SENSOR_STIMULUS_ERROR)
{
fp_info ("Match error occurred");
fpi_device_identify_report (device, NULL, NULL,
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
identify_complete_after_finger_removal (self);
}
else if (resp->result == BMKT_FP_NO_MATCH)
{
fp_info ("Print didn't match");
fpi_device_identify_report (device, NULL, NULL, NULL);
identify_complete_after_finger_removal (self);
}
else if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS)
{
fp_info ("Print is not in database");
fpi_device_identify_complete (device,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
}
else
{
fp_warn ("identify has failed: %d", resp->result);
fpi_device_identify_complete (device,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Unexpected result from device %d",
resp->result));
}
break;
case BMKT_RSP_ID_OK:
{
FpPrint *print = NULL;
GPtrArray *prints = NULL;
g_autoptr(GVariant) data = NULL;
guint8 finger;
const guint8 *user_id;
gsize user_id_len = 0;
gint cnt = 0;
gboolean find = FALSE;
fpi_device_get_identify_data (device, &prints);
for (cnt = 0; cnt < prints->len; cnt++)
{
print = g_ptr_array_index (prints, cnt);
g_object_get (print, "fpi-data", &data, NULL);
g_debug ("data is %p", data);
parse_print_data (data, &finger, &user_id, &user_id_len);
if (user_id)
{
if (memcmp (resp->response.id_resp.user_id, user_id, user_id_len) == 0)
{
find = TRUE;
break;
}
}
}
if(find)
{
fpi_device_identify_report (device, print, print, NULL);
fpi_device_identify_complete (device, NULL);
}
else
{
fpi_device_identify_report (device, NULL, NULL, NULL);
identify_complete_after_finger_removal (self);
}
}
}
}
static void
identify (FpDevice *device)
{
FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device);
FpPrint *print = NULL;
GPtrArray *prints = NULL;
g_autoptr(GVariant) data = NULL;
guint8 finger;
const guint8 *user_id;
gsize user_id_len = 0;
gint cnt = 0;
fpi_device_get_identify_data (device, &prints);
for (cnt = 0; cnt < prints->len; cnt++)
{
print = g_ptr_array_index (prints, cnt);
g_object_get (print, "fpi-data", &data, NULL);
g_debug ("data is %p", data);
if (!parse_print_data (data, &finger, &user_id, &user_id_len))
{
fpi_device_identify_complete (device,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
return;
}
}
G_DEBUG_HERE ();
synaptics_sensor_cmd (self, 0, BMKT_CMD_ID_USER, NULL, 0, identify_msg_cb);
}
static void static void
enroll_msg_cb (FpiDeviceSynaptics *self, enroll_msg_cb (FpiDeviceSynaptics *self,
bmkt_response_t *resp, bmkt_response_t *resp,
@@ -707,6 +850,9 @@ enroll_msg_cb (FpiDeviceSynaptics *self,
case BMKT_RSP_ENROLL_READY: case BMKT_RSP_ENROLL_READY:
{ {
self->enroll_stage = 0; self->enroll_stage = 0;
fpi_device_report_finger_status_changes (device,
FP_FINGER_STATUS_NEEDED,
FP_FINGER_STATUS_NONE);
fp_info ("Place Finger on the Sensor!"); fp_info ("Place Finger on the Sensor!");
break; break;
} }
@@ -726,7 +872,7 @@ enroll_msg_cb (FpiDeviceSynaptics *self,
if (enroll_resp->progress < 100) if (enroll_resp->progress < 100)
done_stages = MIN (done_stages, ENROLL_SAMPLES - 1); done_stages = MIN (done_stages, ENROLL_SAMPLES - 1);
/* Emit a retry error if there has been no discernable /* Emit a retry error if there has been no discernible
* progress. Some firmware revisions report more required * progress. Some firmware revisions report more required
* touches. */ * touches. */
if (self->enroll_stage == done_stages) if (self->enroll_stage == done_stages)
@@ -791,8 +937,6 @@ enroll_msg_cb (FpiDeviceSynaptics *self,
} }
} }
#define TEMPLATE_ID_SIZE 20
static void static void
enroll (FpDevice *device) enroll (FpDevice *device)
{ {
@@ -800,52 +944,21 @@ enroll (FpDevice *device)
FpPrint *print = NULL; FpPrint *print = NULL;
GVariant *data = NULL; GVariant *data = NULL;
GVariant *uid = NULL; GVariant *uid = NULL;
const gchar *username;
guint finger; guint finger;
g_autofree gchar *user_id = NULL; g_autofree gchar *user_id = NULL;
gssize user_id_len; gssize user_id_len;
g_autofree guint8 *payload = NULL; g_autofree guint8 *payload = NULL;
const GDate *date;
gint y, m, d;
gint32 rand_id = 0;
fpi_device_get_enroll_data (device, &print); fpi_device_get_enroll_data (device, &print);
G_DEBUG_HERE (); G_DEBUG_HERE ();
date = fp_print_get_enroll_date (print); user_id = fpi_print_generate_user_id (print);
if (date && g_date_valid (date))
{
y = g_date_get_year (date);
m = g_date_get_month (date);
d = g_date_get_day (date);
}
else
{
y = 0;
m = 0;
d = 0;
}
username = fp_print_get_username (print);
if (!username)
username = "nobody";
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
rand_id = 0;
else
rand_id = g_random_int ();
user_id = g_strdup_printf ("FP1-%04d%02d%02d-%X-%08X-%s",
y, m, d,
fp_print_get_finger (print),
rand_id,
username);
user_id_len = strlen (user_id); user_id_len = strlen (user_id);
user_id_len = MIN (BMKT_MAX_USER_ID_LEN, user_id_len); user_id_len = MIN (BMKT_MAX_USER_ID_LEN, user_id_len);
/* We currently always use finger 1 from the devices piont of view */ /* We currently always use finger 1 from the devices point of view */
finger = 1; finger = 1;
uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
@@ -856,9 +969,9 @@ enroll (FpDevice *device)
finger, finger,
uid); uid);
fpi_print_set_type (print, FP_PRINT_RAW); fpi_print_set_type (print, FPI_PRINT_RAW);
fpi_print_set_device_stored (print, TRUE); fpi_print_set_device_stored (print, TRUE);
g_object_set (print, "fp-data", data, NULL); g_object_set (print, "fpi-data", data, NULL);
g_object_set (print, "description", user_id, NULL); g_object_set (print, "description", user_id, NULL);
g_debug ("user_id: %s, finger: %d", user_id, finger); g_debug ("user_id: %s, finger: %d", user_id, finger);
@@ -927,7 +1040,7 @@ delete_print (FpDevice *device)
fpi_device_get_delete_data (device, &print); fpi_device_get_delete_data (device, &print);
g_object_get (print, "fp-data", &data, NULL); g_object_get (print, "fpi-data", &data, NULL);
g_debug ("data is %p", data); g_debug ("data is %p", data);
if (!parse_print_data (data, &finger, &user_id, &user_id_len)) if (!parse_print_data (data, &finger, &user_id, &user_id_len))
{ {
@@ -958,6 +1071,7 @@ dev_probe (FpDevice *device)
const guint8 *data; const guint8 *data;
gboolean read_ok = TRUE; gboolean read_ok = TRUE;
g_autofree gchar *serial = NULL; g_autofree gchar *serial = NULL;
gboolean retry = TRUE;
G_DEBUG_HERE (); G_DEBUG_HERE ();
@@ -969,12 +1083,14 @@ dev_probe (FpDevice *device)
return; return;
} }
if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) if (!g_usb_device_reset (usb_dev, &error))
goto err_close; goto err_close;
if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error)) if (!g_usb_device_claim_interface (usb_dev, 0, 0, &error))
goto err_close; goto err_close;
while(1)
{
/* TODO: Do not do this synchronous. */ /* TODO: Do not do this synchronous. */
transfer = fpi_usb_transfer_new (device); transfer = fpi_usb_transfer_new (device);
fpi_usb_transfer_fill_bulk (transfer, USB_EP_REQUEST, SENSOR_FW_CMD_HEADER_LEN); fpi_usb_transfer_fill_bulk (transfer, USB_EP_REQUEST, SENSOR_FW_CMD_HEADER_LEN);
@@ -999,11 +1115,17 @@ dev_probe (FpDevice *device)
} }
if (status != 0) if (status != 0)
{ {
g_warning ("Device responded with error: %d", status); g_warning ("Device responded with error: %d retry: %d", status, retry);
if(retry)
{
retry = FALSE;
continue;
}
error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO); error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO);
goto err_close; goto err_close;
} }
break;
}
read_ok &= fpi_byte_reader_get_uint32_le (&reader, &self->mis_version.build_time); read_ok &= fpi_byte_reader_get_uint32_le (&reader, &self->mis_version.build_time);
read_ok &= fpi_byte_reader_get_uint32_le (&reader, &self->mis_version.build_num); read_ok &= fpi_byte_reader_get_uint32_le (&reader, &self->mis_version.build_num);
read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.version_major); read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.version_major);
@@ -1025,7 +1147,7 @@ dev_probe (FpDevice *device)
if (!read_ok) if (!read_ok)
{ {
g_warning ("Transfer in response to verison query was too short"); g_warning ("Transfer in response to version query was too short");
error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO); error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO);
goto err_close; goto err_close;
} }
@@ -1203,6 +1325,7 @@ fpi_device_synaptics_class_init (FpiDeviceSynapticsClass *klass)
dev_class->close = dev_exit; dev_class->close = dev_exit;
dev_class->probe = dev_probe; dev_class->probe = dev_probe;
dev_class->verify = verify; dev_class->verify = verify;
dev_class->identify = identify;
dev_class->enroll = enroll; dev_class->enroll = enroll;
dev_class->delete = delete_print; dev_class->delete = delete_print;
dev_class->cancel = cancel; dev_class->cancel = cancel;

View File

@@ -16,8 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __synaptics_h__ #pragma once
#define __synaptics_h__
#include "fpi-device.h" #include "fpi-device.h"
#include "fpi-ssm.h" #include "fpi-ssm.h"
@@ -111,8 +110,6 @@ struct _FpiDeviceSynaptics
FpiSsm *cmd_ssm; FpiSsm *cmd_ssm;
FpiUsbTransfer *cmd_pending_transfer; FpiUsbTransfer *cmd_pending_transfer;
gboolean cmd_complete_on_removal; gboolean cmd_complete_on_removal;
GError *cmd_complete_error;
void *cmd_complete_data;
bmkt_sensor_version_t mis_version; bmkt_sensor_version_t mis_version;
@@ -126,5 +123,3 @@ struct _FpiDeviceSynaptics
struct syna_enroll_resp_data enroll_resp_data; struct syna_enroll_resp_data enroll_resp_data;
syna_state_t state; syna_state_t state;
}; };
#endif //__synaptics_h__

View File

@@ -47,17 +47,11 @@ enum {
enum sonly_kill_transfers_action { enum sonly_kill_transfers_action {
NOT_KILLING = 0, NOT_KILLING = 0,
/* abort a SSM with an error code */
ABORT_SSM,
/* report an image session error */ /* report an image session error */
IMG_SESSION_ERROR, IMG_SESSION_ERROR,
/* iterate a SSM to the next state */ /* iterate a SSM to the next state */
ITERATE_SSM, ITERATE_SSM,
/* call a callback */
EXEC_CALLBACK,
}; };
enum sonly_fs { enum sonly_fs {
@@ -97,11 +91,7 @@ struct _FpiDeviceUpeksonly
enum sonly_kill_transfers_action killing_transfers; enum sonly_kill_transfers_action killing_transfers;
GError *kill_error; GError *kill_error;
union
{
FpiSsm *kill_ssm; FpiSsm *kill_ssm;
void (*kill_cb)(FpImageDevice *dev);
};
struct fpi_line_asmbl_ctx assembling_ctx; struct fpi_line_asmbl_ctx assembling_ctx;
}; };
@@ -176,11 +166,6 @@ last_transfer_killed (FpImageDevice *dev)
switch (self->killing_transfers) switch (self->killing_transfers)
{ {
case ABORT_SSM:
fp_dbg ("abort ssm error %s", self->kill_error->message);
fpi_ssm_mark_failed (self->kill_ssm, g_steal_pointer (&self->kill_error));
return;
case ITERATE_SSM: case ITERATE_SSM:
fp_dbg ("iterate ssm"); fp_dbg ("iterate ssm");
fpi_ssm_next_state (self->kill_ssm); fpi_ssm_next_state (self->kill_ssm);
@@ -531,6 +516,14 @@ img_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
return; return;
} }
/* NOTE: The old code assume 4096 bytes are received each time
* but there is no reason we need to enforce that. However, we
* always need full lines. */
if (transfer->actual_length % 64 != 0)
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Data packets need to be multiple of 64 bytes, got %zi bytes",
transfer->actual_length);
if (error) if (error)
{ {
fp_warn ("bad status %s, terminating session", error->message); fp_warn ("bad status %s, terminating session", error->message);
@@ -551,7 +544,7 @@ img_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
* the first 2 bytes are a sequence number * the first 2 bytes are a sequence number
* then there are 62 bytes for image data * then there are 62 bytes for image data
*/ */
for (i = 0; i < 4096; i += 64) for (i = 0; i + 64 <= transfer->actual_length; i += 64)
{ {
if (!is_capturing (self)) if (!is_capturing (self))
return; return;
@@ -560,7 +553,7 @@ img_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
if (is_capturing (self)) if (is_capturing (self))
{ {
fpi_usb_transfer_submit (transfer, fpi_usb_transfer_submit (fpi_usb_transfer_ref (transfer),
0, 0,
self->img_cancellable, self->img_cancellable,
img_data_cb, img_data_cb,
@@ -588,6 +581,8 @@ write_regs_finished (struct write_regs_data *wrdata, GError *error)
fpi_ssm_next_state (wrdata->ssm); fpi_ssm_next_state (wrdata->ssm);
else else
fpi_ssm_mark_failed (wrdata->ssm, error); fpi_ssm_mark_failed (wrdata->ssm, error);
g_free (wrdata);
} }
static void write_regs_iterate (struct write_regs_data *wrdata); static void write_regs_iterate (struct write_regs_data *wrdata);
@@ -634,9 +629,9 @@ write_regs_iterate (struct write_regs_data *wrdata)
1); 1);
transfer->short_is_error = TRUE; transfer->short_is_error = TRUE;
transfer->ssm = wrdata->ssm; transfer->ssm = wrdata->ssm;
fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, write_regs_cb, NULL);
transfer->buffer[0] = regwrite->value; transfer->buffer[0] = regwrite->value;
fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, write_regs_cb, wrdata);
} }
static void static void
@@ -675,10 +670,10 @@ sm_write_reg (FpiSsm *ssm,
1); 1);
transfer->short_is_error = TRUE; transfer->short_is_error = TRUE;
transfer->ssm = ssm; transfer->ssm = ssm;
transfer->buffer[0] = value;
fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL,
fpi_ssm_usb_transfer_cb, NULL); fpi_ssm_usb_transfer_cb, NULL);
transfer->buffer[0] = value;
} }
static void static void
@@ -908,7 +903,7 @@ capsm_fire_bulk (FpiSsm *ssm,
self->img_cancellable = g_cancellable_new (); self->img_cancellable = g_cancellable_new ();
for (i = 0; i < self->img_transfers->len; i++) for (i = 0; i < self->img_transfers->len; i++)
{ {
fpi_usb_transfer_submit (g_ptr_array_index (self->img_transfers, i), fpi_usb_transfer_submit (fpi_usb_transfer_ref (g_ptr_array_index (self->img_transfers, i)),
0, 0,
self->img_cancellable, self->img_cancellable,
img_data_cb, img_data_cb,
@@ -1406,8 +1401,12 @@ dev_activate (FpImageDevice *dev)
self->capturing = FALSE; self->capturing = FALSE;
self->num_flying = 0; self->num_flying = 0;
self->img_transfers = g_ptr_array_new_with_free_func ((GFreeFunc) fpi_usb_transfer_unref);
for (i = 0; i < self->img_transfers->len; i++) /* This might seem odd, but we do need multiple in-flight URBs so that
* we never stop polling the device for more data.
*/
for (i = 0; i < NUM_BULK_TRANSFERS; i++)
{ {
FpiUsbTransfer *transfer; FpiUsbTransfer *transfer;

View File

@@ -171,7 +171,7 @@ finger_present (unsigned char *img, size_t len, int sum_threshold)
if (img[i] < 160) if (img[i] < 160)
sum++; sum++;
fp_dbg ("finger_present: sum is %d\n", sum); fp_dbg ("finger_present: sum is %d", sum);
return sum < sum_threshold ? 0 : 1; return sum < sum_threshold ? 0 : 1;
} }
@@ -184,7 +184,7 @@ finger_det_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
if (error) if (error)
{ {
fp_dbg ("data transfer status %s\n", error->message); fp_dbg ("data transfer status %s", error->message);
fpi_image_device_session_error (dev, error); fpi_image_device_session_error (dev, error);
return; return;
} }
@@ -212,7 +212,7 @@ finger_det_cmd_cb (FpiUsbTransfer *t, FpDevice *device,
if (error) if (error)
{ {
fp_dbg ("req transfer status %s\n", error->message); fp_dbg ("req transfer status %s", error->message);
fpi_image_device_session_error (dev, error); fpi_image_device_session_error (dev, error);
return; return;
} }
@@ -411,7 +411,7 @@ dev_init (FpImageDevice *dev)
break; break;
default: default:
fp_err ("Device variant %lu is not known\n", driver_data); fp_err ("Device variant %lu is not known", driver_data);
g_assert_not_reached (); g_assert_not_reached ();
fpi_image_device_open_complete (dev, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); fpi_image_device_open_complete (dev, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
return; return;

View File

@@ -19,8 +19,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __UPEKTC_H #pragma once
#define __UPEKTC_H
#define UPEKTC_CMD_LEN 0x40 #define UPEKTC_CMD_LEN 0x40
#define IMAGE_WIDTH 208 #define IMAGE_WIDTH 208
@@ -1936,5 +1935,3 @@ static const unsigned char scan_cmd[0x40] = {
0x05, 0x90, 0xf6, 0x77, 0x84, 0xf5, 0x2f, 0x01, 0x05, 0x90, 0xf6, 0x77, 0x84, 0xf5, 0x2f, 0x01,
0x05, 0x90, 0xf6, 0x00, 0xc8, 0x00, 0xec, 0x00 0x05, 0x90, 0xf6, 0x00, 0xc8, 0x00, 0xec, 0x00
}; };
#endif

View File

@@ -78,6 +78,7 @@ upektc_img_cmd_update_crc (unsigned char *cmd_buf, size_t size)
cmd_buf[size - 1] = (crc & 0xff00) >> 8; cmd_buf[size - 1] = (crc & 0xff00) >> 8;
} }
FP_GNUC_ACCESS (read_only, 3, 4)
static void static void
upektc_img_submit_req (FpiSsm *ssm, upektc_img_submit_req (FpiSsm *ssm,
FpImageDevice *dev, FpImageDevice *dev,
@@ -193,7 +194,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
if (self->deactivating) if (self->deactivating)
{ {
fp_dbg ("Deactivate requested\n"); fp_dbg ("Deactivate requested");
fpi_ssm_mark_completed (transfer->ssm); fpi_ssm_mark_completed (transfer->ssm);
return; return;
} }
@@ -208,7 +209,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
if (fpi_ssm_get_cur_state (transfer->ssm) == CAPTURE_READ_DATA_TERM) if (fpi_ssm_get_cur_state (transfer->ssm) == CAPTURE_READ_DATA_TERM)
{ {
fp_dbg ("Terminating SSM\n"); fp_dbg ("Terminating SSM");
fpi_ssm_mark_completed (transfer->ssm); fpi_ssm_mark_completed (transfer->ssm);
return; return;
} }
@@ -219,7 +220,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
response_size += 9; /* 7 bytes for header, 2 for CRC */ response_size += 9; /* 7 bytes for header, 2 for CRC */
if (response_size > transfer->actual_length) if (response_size > transfer->actual_length)
{ {
fp_dbg ("response_size is %lu, actual_length is %d\n", fp_dbg ("response_size is %lu, actual_length is %d",
response_size, (gint) transfer->actual_length); response_size, (gint) transfer->actual_length);
fp_dbg ("Waiting for rest of transfer"); fp_dbg ("Waiting for rest of transfer");
BUG_ON (self->response_rest); BUG_ON (self->response_rest);
@@ -237,7 +238,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
{ {
/* No finger */ /* No finger */
case 0x28: case 0x28:
fp_dbg ("18th byte is %.2x\n", data[18]); fp_dbg ("18th byte is %.2x", data[18]);
switch (data[18]) switch (data[18])
{ {
case 0x0c: case 0x0c:
@@ -254,7 +255,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
case 0x1e: case 0x1e:
/* short scan */ /* short scan */
fp_err ("short scan, aborting\n"); fp_err ("short scan, aborting");
fpi_image_device_retry_scan (dev, fpi_image_device_retry_scan (dev,
FP_DEVICE_RETRY_TOO_SHORT); FP_DEVICE_RETRY_TOO_SHORT);
fpi_image_device_report_finger_status (dev, fpi_image_device_report_finger_status (dev,
@@ -265,7 +266,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
case 0x1d: case 0x1d:
/* too much horisontal movement */ /* too much horisontal movement */
fp_err ("too much horisontal movement, aborting\n"); fp_err ("too much horisontal movement, aborting");
fpi_image_device_retry_scan (dev, fpi_image_device_retry_scan (dev,
FP_DEVICE_RETRY_CENTER_FINGER); FP_DEVICE_RETRY_CENTER_FINGER);
fpi_image_device_report_finger_status (dev, fpi_image_device_report_finger_status (dev,
@@ -276,7 +277,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
default: default:
/* some error happened, cancel scan */ /* some error happened, cancel scan */
fp_err ("something bad happened, stop scan\n"); fp_err ("something bad happened, stop scan");
fpi_image_device_retry_scan (dev, fpi_image_device_retry_scan (dev,
FP_DEVICE_RETRY); FP_DEVICE_RETRY);
fpi_image_device_report_finger_status (dev, fpi_image_device_report_finger_status (dev,
@@ -307,9 +308,10 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
upektc_img_process_image_frame (self->image_bits + self->image_size, upektc_img_process_image_frame (self->image_bits + self->image_size,
data); data);
BUG_ON (self->image_size != IMAGE_SIZE); BUG_ON (self->image_size != IMAGE_SIZE);
fp_dbg ("Image size is %lu\n", fp_dbg ("Image size is %lu",
self->image_size); self->image_size);
img = fp_image_new (IMAGE_WIDTH, IMAGE_HEIGHT); img = fp_image_new (IMAGE_WIDTH, IMAGE_HEIGHT);
img->flags |= FPI_IMAGE_PARTIAL;
memcpy (img->data, self->image_bits, memcpy (img->data, self->image_bits,
IMAGE_SIZE); IMAGE_SIZE);
fpi_image_device_image_captured (dev, img); fpi_image_device_image_captured (dev, img);
@@ -319,7 +321,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
break; break;
default: default:
fp_err ("Unknown response!\n"); fp_err ("Unknown response!");
fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
break; break;
} }
@@ -330,7 +332,7 @@ capture_read_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
break; break;
default: default:
fp_err ("Not handled response!\n"); fp_err ("Not handled response!");
fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
} }
} }

View File

@@ -17,8 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __UPEKTC_IMG_H #pragma once
#define __UPEKTC_IMG_H
static const unsigned char upek2020_init_1[] = { static const unsigned char upek2020_init_1[] = {
'C', 'i', 'a', 'o', 'C', 'i', 'a', 'o',
@@ -140,5 +139,3 @@ static const unsigned char upek2020_ack_frame[] = {
0x30, 0x30,
0xac, 0x5b /* CRC */ 0xac, 0x5b /* CRC */
}; };
#endif

View File

@@ -35,7 +35,6 @@
#define TIMEOUT 5000 #define TIMEOUT 5000
#define MSG_READ_BUF_SIZE 0x40 #define MSG_READ_BUF_SIZE 0x40
#define MAX_DATA_IN_READ_BUF (MSG_READ_BUF_SIZE - 9)
struct _FpiDeviceUpekts struct _FpiDeviceUpekts
{ {
@@ -236,12 +235,18 @@ __handle_incoming_msg (FpDevice *device,
{ {
GError *error = NULL; GError *error = NULL;
guint8 *buf = udata->buffer; guint8 *buf = udata->buffer;
guint16 len = ((buf[5] & 0xf) << 8) | buf[6]; guint16 len;
guint16 computed_crc = udf_crc (buf + 4, len + 3); guint16 computed_crc;
guint16 msg_crc = (buf[len + 8] << 8) | buf[len + 7]; guint16 msg_crc;
unsigned char *retdata = NULL;
unsigned char code_a, code_b; unsigned char code_a, code_b;
g_assert (udata->buflen >= 6);
len = ((buf[5] & 0xf) << 8) | buf[6];
g_assert (udata->buflen >= len + 9);
computed_crc = udf_crc (buf + 4, len + 3);
msg_crc = (buf[len + 8] << 8) | buf[len + 7];
if (computed_crc != msg_crc) if (computed_crc != msg_crc)
{ {
fp_err ("CRC failed, got %04x expected %04x", msg_crc, computed_crc); fp_err ("CRC failed, got %04x expected %04x", msg_crc, computed_crc);
@@ -267,12 +272,7 @@ __handle_incoming_msg (FpDevice *device,
return; return;
} }
if (len > 0) udata->callback (device, READ_MSG_CMD, code_a, 0, buf + 7, len,
{
retdata = g_malloc (len);
memcpy (retdata, buf + 7, len);
}
udata->callback (device, READ_MSG_CMD, code_a, 0, retdata, len,
udata->user_data, NULL); udata->user_data, NULL);
goto done; goto done;
} }
@@ -309,14 +309,8 @@ __handle_incoming_msg (FpDevice *device,
innerlen = innerlen - 3; innerlen = innerlen - 3;
_subcmd = innerbuf[5]; _subcmd = innerbuf[5];
fp_dbg ("device responds to subcmd %x with %d bytes", _subcmd, innerlen); fp_dbg ("device responds to subcmd %x with %d bytes", _subcmd, innerlen);
if (innerlen > 0)
{
retdata = g_malloc (innerlen);
memcpy (retdata, innerbuf + 6, innerlen);
}
udata->callback (device, READ_MSG_RESPONSE, code_b, _subcmd, udata->callback (device, READ_MSG_RESPONSE, code_b, _subcmd,
retdata, innerlen, udata->user_data, NULL); innerbuf + 6, innerlen, udata->user_data, NULL);
g_free (retdata);
goto done; goto done;
} }
else else
@@ -358,7 +352,8 @@ read_msg_cb (FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error) gpointer user_data, GError *error)
{ {
struct read_msg_data *udata = user_data; struct read_msg_data *udata = user_data;
guint16 len; guint16 payload_len;
gsize packet_len;
if (error) if (error)
{ {
@@ -383,14 +378,15 @@ read_msg_cb (FpiUsbTransfer *transfer, FpDevice *device,
goto err; goto err;
} }
len = ((udata->buffer[5] & 0xf) << 8) | udata->buffer[6]; payload_len = ((udata->buffer[5] & 0xf) << 8) | udata->buffer[6];
packet_len = payload_len + 9;
if (transfer->actual_length != MSG_READ_BUF_SIZE && if (transfer->actual_length != MSG_READ_BUF_SIZE &&
(len + 9) > transfer->actual_length) packet_len > transfer->actual_length)
{ {
/* Check that the length claimed inside the message is in line with /* Check that the length claimed inside the message is in line with
* the amount of data that was transferred over USB. */ * the amount of data that was transferred over USB. */
fp_err ("msg didn't include enough data, expected=%d recv=%d", fp_err ("msg didn't include enough data, expected=%d recv=%d",
len + 9, (gint) transfer->actual_length); (gint) packet_len, (gint) transfer->actual_length);
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, error = fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Packet from device didn't include data"); "Packet from device didn't include data");
goto err; goto err;
@@ -399,14 +395,14 @@ read_msg_cb (FpiUsbTransfer *transfer, FpDevice *device,
/* We use a 64 byte buffer for reading messages. However, sometimes /* We use a 64 byte buffer for reading messages. However, sometimes
* messages are longer, in which case we have to do another USB bulk read * messages are longer, in which case we have to do another USB bulk read
* to read the remainder. This is handled below. */ * to read the remainder. This is handled below. */
if (len > MAX_DATA_IN_READ_BUF) if (packet_len > MSG_READ_BUF_SIZE)
{ {
int needed = len - MAX_DATA_IN_READ_BUF; int needed = packet_len - MSG_READ_BUF_SIZE;
FpiUsbTransfer *etransfer = fpi_usb_transfer_new (device); FpiUsbTransfer *etransfer = fpi_usb_transfer_new (device);
fp_dbg ("didn't fit in buffer, need to extend by %d bytes", needed); fp_dbg ("didn't fit in buffer, need to extend by %d bytes", needed);
udata->buffer = g_realloc ((gpointer) udata->buffer, len); udata->buffer = g_realloc ((gpointer) udata->buffer, packet_len);
udata->buflen = len; udata->buflen = packet_len;
fpi_usb_transfer_fill_bulk_full (etransfer, EP_IN, fpi_usb_transfer_fill_bulk_full (etransfer, EP_IN,
udata->buffer + MSG_READ_BUF_SIZE, udata->buffer + MSG_READ_BUF_SIZE,
@@ -531,15 +527,6 @@ initsm_read_msg_response_cb (FpiSsm *ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Unexpected response subcommand")); "Unexpected response subcommand"));
} }
else if (seq != upekdev->seq)
{
fp_err ("expected response to cmd seq=%02x, got response to %02x "
"in state %d", upekdev->seq, seq,
fpi_ssm_get_cur_state (ssm));
fpi_ssm_mark_failed (ssm,
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
"Unexpected sequence number in response"));
}
else else
{ {
fpi_ssm_next_state (ssm); fpi_ssm_next_state (ssm);
@@ -700,7 +687,7 @@ initsm_run_state (FpiSsm *ssm, FpDevice *dev)
break; break;
case SEND_RESP03:; case SEND_RESP03:;
transfer = alloc_send_cmd28_transfer (dev, ++upekdev->seq, init_resp03, sizeof (init_resp03)); transfer = alloc_send_cmdresponse_transfer (dev, ++upekdev->seq, init_resp03, sizeof (init_resp03));
transfer->ssm = ssm; transfer->ssm = ssm;
transfer->short_is_error = TRUE; transfer->short_is_error = TRUE;
fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL); fpi_usb_transfer_submit (transfer, TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL);
@@ -856,21 +843,14 @@ dev_init (FpDevice *dev)
fpi_ssm_start (ssm, initsm_done); fpi_ssm_start (ssm, initsm_done);
} }
static void
deinitsm_done (FpiSsm *ssm, FpDevice *dev, GError *error)
{
g_usb_device_release_interface (fpi_device_get_usb_device (dev), 0, 0, NULL);
fpi_device_close_complete (dev, error);
}
static void static void
dev_exit (FpDevice *dev) dev_exit (FpDevice *dev)
{ {
FpiSsm *ssm; GError *error = NULL;
ssm = fpi_ssm_new (dev, deinitsm_state_handler, DEINITSM_NUM_STATES); g_usb_device_release_interface (fpi_device_get_usb_device (dev), 0, 0, &error);
fpi_ssm_start (ssm, deinitsm_done);
fpi_device_close_complete (dev, error);
} }
static const unsigned char enroll_init[] = { static const unsigned char enroll_init[] = {
@@ -902,8 +882,10 @@ enroll_start_sm_cb_msg28 (FpDevice *dev,
FpiSsm *ssm = user_data; FpiSsm *ssm = user_data;
if (error) if (error)
{
fpi_ssm_mark_failed (ssm, error); fpi_ssm_mark_failed (ssm, error);
if (type != READ_MSG_RESPONSE) }
else if (type != READ_MSG_RESPONSE)
{ {
fp_err ("expected response, got %d seq=%x", type, seq); fp_err ("expected response, got %d seq=%x", type, seq);
fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
@@ -981,7 +963,9 @@ enroll_stop_deinit_cb (FpiSsm *ssm, FpDevice *dev, GError *error)
if (error) if (error)
fp_warn ("Error deinitializing: %s", error->message); fp_warn ("Error deinitializing: %s", error->message);
fpi_device_enroll_complete (dev, data->print, data->error); fpi_device_enroll_complete (dev,
g_steal_pointer (&data->print),
g_steal_pointer (&data->error));
} }
static void static void
@@ -990,7 +974,7 @@ do_enroll_stop (FpDevice *dev, FpPrint *print, GError *error)
EnrollStopData *data = g_new0 (EnrollStopData, 1); EnrollStopData *data = g_new0 (EnrollStopData, 1);
FpiSsm *ssm = deinitsm_new (dev, data); FpiSsm *ssm = deinitsm_new (dev, data);
data->print = g_object_ref (print); data->print = print;
data->error = error; data->error = error;
fpi_ssm_start (ssm, enroll_stop_deinit_cb); fpi_ssm_start (ssm, enroll_stop_deinit_cb);
@@ -1117,7 +1101,6 @@ e_handle_resp02 (FpDevice *dev, unsigned char *data,
else else
{ {
GVariant *fp_data; GVariant *fp_data;
print = fp_print_new (dev);
fpi_device_get_enroll_data (dev, &print); fpi_device_get_enroll_data (dev, &print);
@@ -1126,7 +1109,8 @@ e_handle_resp02 (FpDevice *dev, unsigned char *data,
data_len - sizeof (scan_comp), data_len - sizeof (scan_comp),
1); 1);
g_object_set (print, "fp-data", fp_data, NULL); fpi_print_set_type (print, FPI_PRINT_RAW);
g_object_set (print, "fpi-data", fp_data, NULL);
g_object_ref (print); g_object_ref (print);
} }
@@ -1225,7 +1209,6 @@ enroll (FpDevice *dev)
typedef struct typedef struct
{ {
FpiMatchResult res;
GError *error; GError *error;
} VerifyStopData; } VerifyStopData;
@@ -1244,7 +1227,12 @@ verify_stop_deinit_cb (FpiSsm *ssm, FpDevice *dev, GError *error)
if (error) if (error)
fp_warn ("Error deinitializing: %s", error->message); fp_warn ("Error deinitializing: %s", error->message);
fpi_device_verify_complete (dev, data->res, NULL, data->error); if (data->error)
fpi_device_verify_complete (dev, g_steal_pointer (&data->error));
else
fpi_device_verify_complete (dev, g_steal_pointer (&error));
g_clear_error (&error);
} }
static void static void
@@ -1253,7 +1241,10 @@ do_verify_stop (FpDevice *dev, FpiMatchResult res, GError *error)
VerifyStopData *data = g_new0 (VerifyStopData, 1); VerifyStopData *data = g_new0 (VerifyStopData, 1);
FpiSsm *ssm = deinitsm_new (dev, data); FpiSsm *ssm = deinitsm_new (dev, data);
data->res = res; /* Report the error immediately if possible, otherwise delay it. */
if (error && error->domain == FP_DEVICE_RETRY)
fpi_device_verify_report (dev, res, NULL, error);
else
data->error = error; data->error = error;
fpi_ssm_start (ssm, verify_stop_deinit_cb); fpi_ssm_start (ssm, verify_stop_deinit_cb);
@@ -1293,7 +1284,7 @@ verify_start_sm_run_state (FpiSsm *ssm, FpDevice *dev)
case VERIFY_INIT: case VERIFY_INIT:
fpi_device_get_verify_data (dev, &print); fpi_device_get_verify_data (dev, &print);
g_object_get (dev, "fp-data", &fp_data, NULL); g_object_get (print, "fpi-data", &fp_data, NULL);
data = g_variant_get_fixed_array (fp_data, &data_len, 1); data = g_variant_get_fixed_array (fp_data, &data_len, 1);

View File

@@ -81,7 +81,7 @@ static const struct uru4k_dev_profile
{ {
const char *name; const char *name;
gboolean auth_cr; gboolean auth_cr;
gboolean encryption; gboolean image_not_flipped;
} uru4k_dev_info[] = { } uru4k_dev_info[] = {
[MS_KBD] = { [MS_KBD] = {
.name = "Microsoft Keyboard with Fingerprint Reader", .name = "Microsoft Keyboard with Fingerprint Reader",
@@ -106,7 +106,7 @@ static const struct uru4k_dev_profile
[DP_URU4000B] = { [DP_URU4000B] = {
.name = "Digital Persona U.are.U 4000B", .name = "Digital Persona U.are.U 4000B",
.auth_cr = FALSE, .auth_cr = FALSE,
.encryption = TRUE, .image_not_flipped = TRUE, /* See comment in the code where it is used. */
}, },
}; };
@@ -122,7 +122,7 @@ struct _FpiDeviceUru4000
const struct uru4k_dev_profile *profile; const struct uru4k_dev_profile *profile;
uint8_t interface; uint8_t interface;
FpImageDeviceState activate_state; FpiImageDeviceState activate_state;
unsigned char last_reg_rd[16]; unsigned char last_reg_rd[16];
unsigned char last_hwstat; unsigned char last_hwstat;
@@ -408,22 +408,10 @@ change_state_write_reg_cb (FpiUsbTransfer *transfer,
} }
static void static void
dev_change_state (FpImageDevice *dev, FpImageDeviceState state) dev_change_state (FpImageDevice *dev, FpiImageDeviceState state)
{ {
FpiDeviceUru4000 *self = FPI_DEVICE_URU4000 (dev); FpiDeviceUru4000 *self = FPI_DEVICE_URU4000 (dev);
switch (state)
{
case FP_IMAGE_DEVICE_STATE_INACTIVE:
case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF:
case FP_IMAGE_DEVICE_STATE_CAPTURE:
break;
default:
g_assert_not_reached ();
}
self->activate_state = state; self->activate_state = state;
if (self->img_transfer != NULL) if (self->img_transfer != NULL)
return; return;
@@ -663,7 +651,11 @@ imaging_run_state (FpiSsm *ssm, FpDevice *_dev)
case IMAGING_CAPTURE: case IMAGING_CAPTURE:
self->img_lines_done = 0; self->img_lines_done = 0;
self->img_block = 0; self->img_block = 0;
fpi_usb_transfer_submit (self->img_transfer, 0, NULL, image_transfer_cb, NULL); fpi_usb_transfer_submit (fpi_usb_transfer_ref (self->img_transfer),
0,
NULL,
image_transfer_cb,
NULL);
break; break;
@@ -680,8 +672,8 @@ imaging_run_state (FpiSsm *ssm, FpDevice *_dev)
fpi_ssm_jump_to_state (ssm, IMAGING_CAPTURE); fpi_ssm_jump_to_state (ssm, IMAGING_CAPTURE);
return; return;
} }
if (!self->profile->encryption)
{ /* Detect whether image is encrypted (by checking how noisy it is) */
dev2 = calc_dev2 (img); dev2 = calc_dev2 (img);
fp_dbg ("dev2: %d", dev2); fp_dbg ("dev2: %d", dev2);
if (dev2 < ENC_THRESHOLD) if (dev2 < ENC_THRESHOLD)
@@ -690,7 +682,7 @@ imaging_run_state (FpiSsm *ssm, FpDevice *_dev)
return; return;
} }
fp_info ("image seems to be encrypted"); fp_info ("image seems to be encrypted");
}
buf[0] = img->key_number; buf[0] = img->key_number;
buf[1] = self->img_enc_seed; buf[1] = self->img_enc_seed;
buf[2] = self->img_enc_seed >> 8; buf[2] = self->img_enc_seed >> 8;
@@ -723,7 +715,7 @@ imaging_run_state (FpiSsm *ssm, FpDevice *_dev)
num_lines); num_lines);
if (flags & BLOCKF_CHANGE_KEY) if (flags & BLOCKF_CHANGE_KEY)
{ {
fp_dbg ("changing encryption keys.\n"); fp_dbg ("changing encryption keys.");
img->block_info[self->img_block].flags &= ~BLOCKF_CHANGE_KEY; img->block_info[self->img_block].flags &= ~BLOCKF_CHANGE_KEY;
img->key_number++; img->key_number++;
self->img_enc_seed = rand (); self->img_enc_seed = rand ();
@@ -769,11 +761,17 @@ imaging_run_state (FpiSsm *ssm, FpDevice *_dev)
} }
fpimg->flags = FPI_IMAGE_COLORS_INVERTED; fpimg->flags = FPI_IMAGE_COLORS_INVERTED;
if (!self->profile->encryption) /* NOTE: For some reason all but U4000B (or rather U4500?) flipped the
* image, we retain this behaviour here, but it is not clear whether it
* is correct.
* It may be that there are different models with the same USB ID that
* behave differently.
*/
if (self->profile->image_not_flipped)
fpimg->flags |= FPI_IMAGE_V_FLIPPED | FPI_IMAGE_H_FLIPPED; fpimg->flags |= FPI_IMAGE_V_FLIPPED | FPI_IMAGE_H_FLIPPED;
fpi_image_device_image_captured (dev, fpimg); fpi_image_device_image_captured (dev, fpimg);
if (self->activate_state == FP_IMAGE_DEVICE_STATE_CAPTURE) if (self->activate_state == FPI_IMAGE_DEVICE_STATE_CAPTURE)
fpi_ssm_jump_to_state (ssm, IMAGING_CAPTURE); fpi_ssm_jump_to_state (ssm, IMAGING_CAPTURE);
else else
fpi_ssm_mark_completed (ssm); fpi_ssm_mark_completed (ssm);
@@ -793,8 +791,7 @@ imaging_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
if (error) if (error)
fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error); fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error);
/* Freed by callback or cancellation */ g_clear_pointer (&self->img_transfer, fpi_usb_transfer_unref);
self->img_transfer = NULL;
g_free (self->img_data); g_free (self->img_data);
self->img_data = NULL; self->img_data = NULL;
@@ -1176,7 +1173,10 @@ deactivate_write_reg_cb (FpiUsbTransfer *transfer, FpDevice *dev,
static void static void
dev_deactivate (FpImageDevice *dev) dev_deactivate (FpImageDevice *dev)
{ {
dev_change_state (dev, FP_IMAGE_DEVICE_STATE_INACTIVE); /* This is started/handled by execute_state_change in order to delay the
* action until after the image transfer has completed.
* We just need to override the function so that the complete handler is
* not called automatically. */
} }
static void static void
@@ -1187,7 +1187,7 @@ execute_state_change (FpImageDevice *dev)
switch (self->activate_state) switch (self->activate_state)
{ {
case FP_IMAGE_DEVICE_STATE_INACTIVE: case FPI_IMAGE_DEVICE_STATE_DEACTIVATING:
fp_dbg ("deactivating"); fp_dbg ("deactivating");
self->irq_cb = NULL; self->irq_cb = NULL;
self->irq_cb_data = NULL; self->irq_cb_data = NULL;
@@ -1195,7 +1195,7 @@ execute_state_change (FpImageDevice *dev)
deactivate_write_reg_cb, NULL); deactivate_write_reg_cb, NULL);
break; break;
case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
fp_dbg ("wait finger on"); fp_dbg ("wait finger on");
if (!IRQ_HANDLER_IS_RUNNING (self)) if (!IRQ_HANDLER_IS_RUNNING (self))
{ {
@@ -1209,7 +1209,7 @@ execute_state_change (FpImageDevice *dev)
change_state_write_reg_cb, NULL); change_state_write_reg_cb, NULL);
break; break;
case FP_IMAGE_DEVICE_STATE_CAPTURE: case FPI_IMAGE_DEVICE_STATE_CAPTURE:
fp_dbg ("starting capture"); fp_dbg ("starting capture");
self->irq_cb = NULL; self->irq_cb = NULL;
@@ -1229,7 +1229,7 @@ execute_state_change (FpImageDevice *dev)
change_state_write_reg_cb, NULL); change_state_write_reg_cb, NULL);
break; break;
case FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF:
fp_dbg ("await finger off"); fp_dbg ("await finger off");
if (!IRQ_HANDLER_IS_RUNNING (self)) if (!IRQ_HANDLER_IS_RUNNING (self))
{ {
@@ -1242,6 +1242,12 @@ execute_state_change (FpImageDevice *dev)
write_reg (dev, REG_MODE, MODE_AWAIT_FINGER_OFF, write_reg (dev, REG_MODE, MODE_AWAIT_FINGER_OFF,
change_state_write_reg_cb, NULL); change_state_write_reg_cb, NULL);
break; break;
/* Ignored states */
case FPI_IMAGE_DEVICE_STATE_IDLE:
case FPI_IMAGE_DEVICE_STATE_ACTIVATING:
case FPI_IMAGE_DEVICE_STATE_INACTIVE:
break;
} }
} }

View File

@@ -42,6 +42,7 @@ async_write_callback (FpiUsbTransfer *transfer, FpDevice *device,
} }
/* Send data to EP1, the only out endpoint */ /* Send data to EP1, the only out endpoint */
FP_GNUC_ACCESS (read_only, 3, 4)
static void static void
async_write (FpiSsm *ssm, async_write (FpiSsm *ssm,
FpDevice *dev, FpDevice *dev,
@@ -117,9 +118,10 @@ async_abort_callback (FpiUsbTransfer *transfer, FpDevice *device,
int ep = transfer->endpoint; int ep = transfer->endpoint;
/* In normal case endpoint is empty */ /* In normal case endpoint is empty */
if (g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT)) if (g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT) ||
(g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0 && transfer->actual_length == 0))
{ {
g_free (error); g_clear_error (&error);
fpi_ssm_next_state (transfer->ssm); fpi_ssm_next_state (transfer->ssm);
return; return;
} }
@@ -156,6 +158,8 @@ async_abort (FpDevice *dev, FpiSsm *ssm, int ep)
else else
fpi_usb_transfer_fill_bulk (transfer, ep, VFS_USB_BUFFER_SIZE); fpi_usb_transfer_fill_bulk (transfer, ep, VFS_USB_BUFFER_SIZE);
transfer->ssm = ssm;
fpi_usb_transfer_submit (transfer, VFS_USB_ABORT_TIMEOUT, NULL, fpi_usb_transfer_submit (transfer, VFS_USB_ABORT_TIMEOUT, NULL,
async_abort_callback, NULL); async_abort_callback, NULL);
} }
@@ -240,6 +244,7 @@ prepare_image (FpDeviceVfs0050 *vdev)
/* Building GSList */ /* Building GSList */
GSList *lines = NULL; GSList *lines = NULL;
for (int i = height - 1; i >= 0; --i) for (int i = height - 1; i >= 0; --i)
lines = g_slist_prepend (lines, vdev->lines_buffer + i); lines = g_slist_prepend (lines, vdev->lines_buffer + i);
@@ -464,8 +469,8 @@ receive_callback (FpiUsbTransfer *transfer, FpDevice *device,
if (error) if (error)
g_error_free (error); g_error_free (error);
/* Check if fingerprint data is over */ /* Capture is done when there is no more data to transfer or device timed out */
if (transfer->actual_length == 0) if (transfer->actual_length <= 0)
{ {
fpi_ssm_next_state (transfer->ssm); fpi_ssm_next_state (transfer->ssm);
} }
@@ -473,7 +478,7 @@ receive_callback (FpiUsbTransfer *transfer, FpDevice *device,
{ {
self->bytes += transfer->actual_length; self->bytes += transfer->actual_length;
/* We need more data */ /* Try reading more data */
fpi_ssm_jump_to_state (transfer->ssm, fpi_ssm_jump_to_state (transfer->ssm,
fpi_ssm_get_cur_state (transfer->ssm)); fpi_ssm_get_cur_state (transfer->ssm));
} }
@@ -595,8 +600,7 @@ activate_ssm (FpiSsm *ssm, FpDevice *dev)
/* Receive chunk of data */ /* Receive chunk of data */
transfer = fpi_usb_transfer_new (dev); transfer = fpi_usb_transfer_new (dev);
fpi_usb_transfer_fill_bulk_full (transfer, 0x82, fpi_usb_transfer_fill_bulk_full (transfer, 0x82,
(guint8 *) (guint8 *) self->lines_buffer + self->bytes,
(self->lines_buffer + self->bytes),
VFS_USB_BUFFER_SIZE, NULL); VFS_USB_BUFFER_SIZE, NULL);
transfer->ssm = ssm; transfer->ssm = ssm;
fpi_usb_transfer_submit (transfer, VFS_USB_TIMEOUT, NULL, fpi_usb_transfer_submit (transfer, VFS_USB_TIMEOUT, NULL,
@@ -668,6 +672,7 @@ dev_activate (FpImageDevice *idev)
self->ssm_active = 1; self->ssm_active = 1;
FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (idev), activate_ssm, SSM_STATES); FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (idev), activate_ssm, SSM_STATES);
fpi_ssm_start (ssm, dev_activate_callback); fpi_ssm_start (ssm, dev_activate_callback);
} }
@@ -711,6 +716,7 @@ dev_open (FpImageDevice *idev)
/* Clearing previous device state */ /* Clearing previous device state */
FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (idev), activate_ssm, SSM_STATES); FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (idev), activate_ssm, SSM_STATES);
fpi_ssm_start (ssm, dev_open_callback); fpi_ssm_start (ssm, dev_open_callback);
} }

View File

@@ -147,18 +147,6 @@ m_loop_state (FpiSsm *ssm, FpDevice *_dev)
} }
} }
/* Complete loop sequential state machine */
static void
m_loop_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
{
if (error)
{
g_warning ("State machine completed with an error: %s", error->message);
g_error_free (error);
}
/* Free sequential state machine */
}
/* Exec init sequential state machine */ /* Exec init sequential state machine */
static void static void
m_init_state (FpiSsm *ssm, FpDevice *_dev) m_init_state (FpiSsm *ssm, FpDevice *_dev)
@@ -176,19 +164,7 @@ m_init_state (FpiSsm *ssm, FpDevice *_dev)
static void static void
m_init_complete (FpiSsm *ssm, FpDevice *dev, GError *error) m_init_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
{ {
FpiSsm *ssm_loop;
fpi_image_device_activate_complete (FP_IMAGE_DEVICE (dev), error); fpi_image_device_activate_complete (FP_IMAGE_DEVICE (dev), error);
if (!error)
{
/* Notify activate complete */
/* Start loop ssm */
ssm_loop = fpi_ssm_new (dev, m_loop_state, M_LOOP_NUM_STATES);
fpi_ssm_start (ssm_loop, m_loop_complete);
}
/* Free sequential state machine */
} }
/* Activate device */ /* Activate device */
@@ -213,6 +189,19 @@ dev_deactivate (FpImageDevice *dev)
fpi_image_device_deactivate_complete (dev, NULL); fpi_image_device_deactivate_complete (dev, NULL);
} }
static void
dev_change_state (FpImageDevice *dev, FpiImageDeviceState state)
{
FpiSsm *ssm_loop;
if (state != FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
return;
/* Start a capture operation. */
ssm_loop = fpi_ssm_new (FP_DEVICE (dev), m_loop_state, M_LOOP_NUM_STATES);
fpi_ssm_start (ssm_loop, NULL);
}
static void static void
dev_open (FpImageDevice *dev) dev_open (FpImageDevice *dev)
{ {
@@ -273,6 +262,7 @@ fpi_device_vfs301_class_init (FpDeviceVfs301Class *klass)
img_class->img_close = dev_close; img_class->img_close = dev_close;
img_class->activate = dev_activate; img_class->activate = dev_activate;
img_class->deactivate = dev_deactivate; img_class->deactivate = dev_deactivate;
img_class->change_state = dev_change_state;
img_class->bz3_threshold = 24; img_class->bz3_threshold = 24;

View File

@@ -93,6 +93,7 @@ usb_recv (FpDeviceVfs301 *dev, guint8 endpoint, int max_bytes, FpiUsbTransfer **
*out = g_steal_pointer (&transfer); *out = g_steal_pointer (&transfer);
} }
FP_GNUC_ACCESS (read_only, 2, 3)
static void static void
usb_send (FpDeviceVfs301 *dev, const guint8 *data, gssize length, GError **error) usb_send (FpDeviceVfs301 *dev, const guint8 *data, gssize length, GError **error)
{ {
@@ -177,6 +178,7 @@ translate_str (const char **srcL, gssize *len)
src_len += tmp; src_len += tmp;
} }
g_assert (src_len >= 2);
*len = src_len / 2; *len = src_len / 2;
res = g_malloc0 (*len); res = g_malloc0 (*len);
dst = res; dst = res;
@@ -211,11 +213,9 @@ vfs301_proto_generate (int type, int subtype, gssize *len)
*len = 1; *len = 1;
return data; return data;
} }
break;
case 0x0B: case 0x0B:
return vfs301_proto_generate_0B (subtype, len); return vfs301_proto_generate_0B (subtype, len);
break;
case 0x02D0: case 0x02D0:
{ {
@@ -231,22 +231,18 @@ vfs301_proto_generate (int type, int subtype, gssize *len)
g_assert ((int) subtype <= G_N_ELEMENTS (dataLs)); g_assert ((int) subtype <= G_N_ELEMENTS (dataLs));
return translate_str (dataLs[subtype - 1], len); return translate_str (dataLs[subtype - 1], len);
} }
break;
case 0x0220: case 0x0220:
switch (subtype) switch (subtype)
{ {
case 1: case 1:
return translate_str (vfs301_0220_01, len); return translate_str (vfs301_0220_01, len);
break;
case 2: case 2:
return translate_str (vfs301_0220_02, len); return translate_str (vfs301_0220_02, len);
break;
case 3: case 3:
return translate_str (vfs301_0220_03, len); return translate_str (vfs301_0220_03, len);
break;
case 0xFA00: case 0xFA00:
case 0x2C01: case 0x2C01:
@@ -269,7 +265,6 @@ vfs301_proto_generate (int type, int subtype, gssize *len)
field[3] = field[1]; field[3] = field[1];
return data; return data;
break;
} }
default: default:
@@ -437,7 +432,7 @@ img_process_data (int first_block, FpDeviceVfs301 *dev, const guint8 *buf, int l
usb_send (dev, data, len, NULL); \ usb_send (dev, data, len, NULL); \
} }
#define RAW_DATA(x) x, sizeof (x) #define RAW_DATA(x) g_memdup (x, sizeof (x)), sizeof (x)
#define IS_VFS301_FP_SEQ_START(b) ((b[0] == 0x01) && (b[1] == 0xfe)) #define IS_VFS301_FP_SEQ_START(b) ((b[0] == 0x01) && (b[1] == 0xfe))
@@ -507,30 +502,30 @@ vfs301_proto_process_event_cb (FpiUsbTransfer *transfer,
FpDevice *device, FpDevice *device,
gpointer user_data, GError *error) gpointer user_data, GError *error)
{ {
FpDeviceVfs301 *dev = user_data; FpDeviceVfs301 *self = FPI_DEVICE_VFS301 (device);
if (error) if (error)
{ {
g_warning ("Error receiving data: %s", error->message); g_warning ("Error receiving data: %s", error->message);
g_error_free (error); g_error_free (error);
dev->recv_progress = VFS301_FAILURE; self->recv_progress = VFS301_FAILURE;
return; return;
} }
else if (transfer->actual_length < transfer->length) else if (transfer->actual_length < transfer->length)
{ {
/* TODO: process the data anyway? */ /* TODO: process the data anyway? */
dev->recv_progress = VFS301_ENDED; self->recv_progress = VFS301_ENDED;
return; return;
} }
else else
{ {
FpiUsbTransfer *new; FpiUsbTransfer *new;
if (!vfs301_proto_process_data (dev, if (!vfs301_proto_process_data (self,
transfer->length == VFS301_FP_RECV_LEN_1, transfer->length == VFS301_FP_RECV_LEN_1,
transfer->buffer, transfer->buffer,
transfer->actual_length)) transfer->actual_length))
{ {
dev->recv_progress = VFS301_ENDED; self->recv_progress = VFS301_ENDED;
return; return;
} }

View File

@@ -381,9 +381,8 @@ submit_image (FpiSsm *ssm,
{ {
FpImage *img; FpImage *img;
if (self->lines_recorded == 0) if (self->lines_recorded < VFS5011_IMAGE_WIDTH)
{ {
/* == FP_ENROLL_RETRY_TOO_SHORT */
fpi_image_device_retry_scan (dev, FP_DEVICE_RETRY_TOO_SHORT); fpi_image_device_retry_scan (dev, FP_DEVICE_RETRY_TOO_SHORT);
return; return;
} }

View File

@@ -1,5 +1,4 @@
#ifndef __VFS5011_PROTO_H #pragma once
#define __VFS5011_PROTO_H
#define VFS5011_LINE_SIZE 240 #define VFS5011_LINE_SIZE 240
#define VFS5011_IMAGE_WIDTH 160 #define VFS5011_IMAGE_WIDTH 160
@@ -6182,5 +6181,3 @@ static unsigned char vfs5011_prepare_04[] = { /* 2903 B */
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
}; };
#endif

View File

@@ -21,7 +21,7 @@
/* /*
* This is a virtual driver to debug the image based drivers. A small * This is a virtual driver to debug the image based drivers. A small
* python script is provided to connect to it via a socket, allowing * python script is provided to connect to it via a socket, allowing
* prints to be sent to this device programatically. * prints to be sent to this device programmatically.
* Using this it is possible to test libfprint and fprintd. * Using this it is possible to test libfprint and fprintd.
*/ */
@@ -42,6 +42,7 @@ struct _FpDeviceVirtualImage
GSocketListener *listener; GSocketListener *listener;
GSocketConnection *connection; GSocketConnection *connection;
GCancellable *listen_cancellable;
GCancellable *cancellable; GCancellable *cancellable;
gint socket_fd; gint socket_fd;
@@ -72,14 +73,21 @@ recv_image_img_recv_cb (GObject *source_object,
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error);
if (!success || bytes == 0) /* Can't use self if the operation was cancelled. */
if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
device = FP_IMAGE_DEVICE (self);
/* Consider success if we received the right amount of data, otherwise
* an error must have happened. */
if (bytes < self->recv_img->width * self->recv_img->height)
{ {
if (!success) if (!success)
{ g_warning ("Error receiving image data: %s", error->message);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) else
return; g_warning ("Error receiving image data: end of stream before all data was read");
g_warning ("Error receiving header for image data: %s", error->message);
}
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
@@ -87,9 +95,6 @@ recv_image_img_recv_cb (GObject *source_object,
return; return;
} }
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
device = FP_IMAGE_DEVICE (self);
if (self->automatic_finger) if (self->automatic_finger)
fpi_image_device_report_finger_status (device, TRUE); fpi_image_device_report_finger_status (device, TRUE);
fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img)); fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img));
@@ -112,7 +117,7 @@ recv_image_hdr_recv_cb (GObject *source_object,
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error);
if (!success || bytes == 0) if (!success || bytes != sizeof (self->recv_img_hdr))
{ {
if (!success) if (!success)
{ {
@@ -121,6 +126,10 @@ recv_image_hdr_recv_cb (GObject *source_object,
return; return;
g_warning ("Error receiving header for image data: %s", error->message); g_warning ("Error receiving header for image data: %s", error->message);
} }
else if (bytes != 0)
{
g_warning ("Received incomplete header before end of stream.");
}
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
@@ -162,6 +171,11 @@ recv_image_hdr_recv_cb (GObject *source_object,
!!self->recv_img_hdr[1]); !!self->recv_img_hdr[1]);
break; break;
case -5:
/* -5 causes the device to disappear (no further data) */
fpi_device_remove (FP_DEVICE (self));
break;
default: default:
/* disconnect client, it didn't play fair */ /* disconnect client, it didn't play fair */
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
@@ -185,15 +199,25 @@ recv_image_hdr_recv_cb (GObject *source_object,
} }
static void static void
recv_image (FpDeviceVirtualImage *dev, GInputStream *stream) recv_image (FpDeviceVirtualImage *self, GInputStream *stream)
{
FpiImageDeviceState state;
g_object_get (self, "fpi-image-device-state", &state, NULL);
g_debug ("Starting image receive (if active), state is: %i", state);
/* Only register if the state is active. */
if (state >= FPI_IMAGE_DEVICE_STATE_IDLE)
{ {
g_input_stream_read_all_async (stream, g_input_stream_read_all_async (stream,
dev->recv_img_hdr, self->recv_img_hdr,
sizeof (dev->recv_img_hdr), sizeof (self->recv_img_hdr),
G_PRIORITY_DEFAULT, G_PRIORITY_DEFAULT,
dev->cancellable, self->cancellable,
recv_image_hdr_recv_cb, recv_image_hdr_recv_cb,
dev); self);
}
} }
static void static void
@@ -217,11 +241,25 @@ new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data
start_listen (dev); start_listen (dev);
} }
/* Always further connections (but we disconnect them immediately /* Always accept further connections (but we disconnect them immediately
* if we already have a connection). */ * if we already have a connection). */
start_listen (dev); start_listen (dev);
if (dev->connection) if (dev->connection)
{ {
/* We may not have noticed that the stream was closed,
* if the device is deactivated.
* Cancel any ongoing operation on the old connection. */
g_cancellable_cancel (dev->cancellable);
g_clear_object (&dev->cancellable);
dev->cancellable = g_cancellable_new ();
g_clear_object (&dev->connection);
}
if (dev->connection)
{
g_warning ("Rejecting new connection");
g_io_stream_close (G_IO_STREAM (connection), NULL, NULL); g_io_stream_close (G_IO_STREAM (connection), NULL, NULL);
g_object_unref (connection); g_object_unref (connection);
return; return;
@@ -231,16 +269,15 @@ new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data
dev->automatic_finger = TRUE; dev->automatic_finger = TRUE;
stream = g_io_stream_get_input_stream (G_IO_STREAM (connection)); stream = g_io_stream_get_input_stream (G_IO_STREAM (connection));
recv_image (dev, stream);
fp_dbg ("Got a new connection!"); fp_dbg ("Got a new connection!");
recv_image (dev, stream);
} }
static void static void
start_listen (FpDeviceVirtualImage *dev) start_listen (FpDeviceVirtualImage *dev)
{ {
g_socket_listener_accept_async (dev->listener, g_socket_listener_accept_async (dev->listener,
dev->cancellable, dev->listen_cancellable,
new_connection_cb, new_connection_cb,
dev); dev);
} }
@@ -252,6 +289,7 @@ dev_init (FpImageDevice *dev)
g_autoptr(GSocketListener) listener = NULL; g_autoptr(GSocketListener) listener = NULL;
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev); FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
const char *env; const char *env;
g_autoptr(GSocketAddress) addr = NULL; g_autoptr(GSocketAddress) addr = NULL;
G_DEBUG_HERE (); G_DEBUG_HERE ();
@@ -284,10 +322,12 @@ dev_init (FpImageDevice *dev)
self->listener = g_steal_pointer (&listener); self->listener = g_steal_pointer (&listener);
self->cancellable = g_cancellable_new (); self->cancellable = g_cancellable_new ();
self->listen_cancellable = g_cancellable_new ();
start_listen (self); start_listen (self);
fpi_image_device_open_complete (dev, NULL); /* Delay result to open up the possibility of testing race conditions. */
fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_open_complete, NULL, NULL);
} }
static void static void
@@ -298,16 +338,70 @@ dev_deinit (FpImageDevice *dev)
G_DEBUG_HERE (); G_DEBUG_HERE ();
g_cancellable_cancel (self->cancellable); g_cancellable_cancel (self->cancellable);
g_cancellable_cancel (self->listen_cancellable);
g_clear_object (&self->cancellable); g_clear_object (&self->cancellable);
g_clear_object (&self->listen_cancellable);
g_clear_object (&self->listener); g_clear_object (&self->listener);
g_clear_object (&self->connection); g_clear_object (&self->connection);
fpi_image_device_close_complete (dev, NULL); /* Delay result to open up the possibility of testing race conditions. */
fpi_device_add_timeout (FP_DEVICE (dev), 100, (FpTimeoutFunc) fpi_image_device_close_complete, NULL, NULL);
}
static void
dev_activate (FpImageDevice *dev)
{
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
fpi_image_device_activate_complete (dev, NULL);
if (self->connection)
recv_image (self, g_io_stream_get_input_stream (G_IO_STREAM (self->connection)));
}
static void
dev_deactivate (FpImageDevice *dev)
{
FpDeviceVirtualImage *self = FPI_DEVICE_VIRTUAL_IMAGE (dev);
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
self->cancellable = g_cancellable_new ();
/* XXX: Need to wait for the operation to be cancelled. */
fpi_device_add_timeout (FP_DEVICE (dev), 10, (FpTimeoutFunc) fpi_image_device_deactivate_complete, NULL, NULL);
}
static void
dev_notify_removed_cb (FpDevice *dev)
{
FpiImageDeviceState state;
gboolean removed;
g_object_get (dev,
"fpi-image-device-state", &state,
"removed", &removed,
NULL);
if (!removed || state == FPI_IMAGE_DEVICE_STATE_INACTIVE)
return;
/* This error will be converted to an FP_DEVICE_ERROR_REMOVED by the
* surrounding layers. */
fpi_image_device_session_error (FP_IMAGE_DEVICE (dev),
fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
} }
static void static void
fpi_device_virtual_image_init (FpDeviceVirtualImage *self) fpi_device_virtual_image_init (FpDeviceVirtualImage *self)
{ {
/* NOTE: This is not nice, but we can generally rely on the underlying
* system to throw errors on the transport layer.
*/
g_signal_connect (self,
"notify::removed",
G_CALLBACK (dev_notify_removed_cb),
NULL);
} }
static const FpIdEntry driver_ids[] = { static const FpIdEntry driver_ids[] = {
@@ -328,4 +422,7 @@ fpi_device_virtual_image_class_init (FpDeviceVirtualImageClass *klass)
img_class->img_open = dev_init; img_class->img_open = dev_init;
img_class->img_close = dev_deinit; img_class->img_close = dev_deinit;
img_class->activate = dev_activate;
img_class->deactivate = dev_deactivate;
} }

View File

@@ -2,6 +2,7 @@
* Driver API definitions * Driver API definitions
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net> * Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@@ -18,17 +19,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __DRIVERS_API_H__ #pragma once
#define __DRIVERS_API_H__
#include <config.h>
#include "fp_internal.h"
#include "fpi-compat.h"
#include "fpi-assembling.h"
#include "fpi-device.h"
#include "fpi-image-device.h"
#include "fpi-image.h"
#include "fpi-log.h" #include "fpi-log.h"
#include "fpi-print.h"
#include "fpi-usb-transfer.h" #include "fpi-usb-transfer.h"
#include "fpi-ssm.h" #include "fpi-ssm.h"
#include "fpi-assembling.h"
#include "fpi-image-device.h"
#endif

View File

@@ -57,6 +57,89 @@ enum {
}; };
static guint signals[LAST_SIGNAL] = { 0 }; static guint signals[LAST_SIGNAL] = { 0 };
static const char *
get_drivers_whitelist_env (void)
{
return g_getenv ("FP_DRIVERS_WHITELIST");
}
static gboolean
is_driver_allowed (const gchar *driver)
{
g_auto(GStrv) whitelisted_drivers = NULL;
const char *fp_drivers_whitelist_env;
int i;
g_return_val_if_fail (driver, TRUE);
fp_drivers_whitelist_env = get_drivers_whitelist_env ();
if (!fp_drivers_whitelist_env)
return TRUE;
whitelisted_drivers = g_strsplit (fp_drivers_whitelist_env, ":", -1);
for (i = 0; whitelisted_drivers[i]; ++i)
if (g_strcmp0 (driver, whitelisted_drivers[i]) == 0)
return TRUE;
return FALSE;
}
typedef struct
{
FpContext *context;
FpDevice *device;
} RemoveDeviceData;
static gboolean
remove_device_idle_cb (RemoveDeviceData *data)
{
FpContextPrivate *priv = fp_context_get_instance_private (data->context);
guint idx = 0;
g_return_val_if_fail (g_ptr_array_find (priv->devices, data->device, &idx), G_SOURCE_REMOVE);
g_signal_emit (data->context, signals[DEVICE_REMOVED_SIGNAL], 0, data->device);
g_ptr_array_remove_index_fast (priv->devices, idx);
g_free (data);
return G_SOURCE_REMOVE;
}
static void
remove_device (FpContext *context, FpDevice *device)
{
RemoveDeviceData *data;
data = g_new (RemoveDeviceData, 1);
data->context = context;
data->device = device;
g_idle_add ((GSourceFunc) remove_device_idle_cb, data);
}
static void
device_remove_on_notify_open_cb (FpContext *context, GParamSpec *pspec, FpDevice *device)
{
remove_device (context, device);
}
static void
device_removed_cb (FpContext *context, FpDevice *device)
{
gboolean open = FALSE;
g_object_get (device, "open", &open, NULL);
/* Wait for device close if the device is currently still open. */
if (open)
g_signal_connect_swapped (device, "notify::open", (GCallback) device_remove_on_notify_open_cb, context);
else
remove_device (context, device);
}
static void static void
async_device_init_done_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) async_device_init_done_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{ {
@@ -65,23 +148,25 @@ async_device_init_done_cb (GObject *source_object, GAsyncResult *res, gpointer u
FpContext *context; FpContext *context;
FpContextPrivate *priv; FpContextPrivate *priv;
device = (FpDevice *) g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error); device = FP_DEVICE (g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
if (!device) res, &error));
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return; return;
context = FP_CONTEXT (user_data); context = FP_CONTEXT (user_data);
priv = fp_context_get_instance_private (context); priv = fp_context_get_instance_private (context);
priv->pending_devices--; priv->pending_devices--;
if (error)
{
g_message ("Ignoring device due to initialization error: %s", error->message); g_message ("Ignoring device due to initialization error: %s", error->message);
return; return;
} }
context = FP_CONTEXT (user_data);
priv = fp_context_get_instance_private (context);
priv->pending_devices--;
g_ptr_array_add (priv->devices, device); g_ptr_array_add (priv->devices, device);
g_signal_connect_swapped (device, "removed", (GCallback) device_removed_cb, context);
g_signal_emit (context, signals[DEVICE_ADDED_SIGNAL], 0, device); g_signal_emit (context, signals[DEVICE_ADDED_SIGNAL], 0, device);
} }
@@ -102,14 +187,11 @@ usb_device_added_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx)
for (i = 0; i < priv->drivers->len; i++) for (i = 0; i < priv->drivers->len; i++)
{ {
GType driver = g_array_index (priv->drivers, GType, i); GType driver = g_array_index (priv->drivers, GType, i);
FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver)); g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver);
const FpIdEntry *entry; const FpIdEntry *entry;
if (cls->type != FP_DEVICE_TYPE_USB) if (cls->type != FP_DEVICE_TYPE_USB)
{
g_type_class_unref (cls);
continue; continue;
}
for (entry = cls->id_table; entry->pid; entry++) for (entry = cls->id_table; entry->pid; entry++)
{ {
@@ -129,13 +211,11 @@ usb_device_added_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx)
found_driver = driver; found_driver = driver;
found_entry = entry; found_entry = entry;
} }
g_type_class_unref (cls);
} }
if (found_driver == G_TYPE_NONE) if (found_driver == G_TYPE_NONE)
{ {
g_debug ("No driver found for USB device %04X:%04X", pid, vid); g_debug ("No driver found for USB device %04X:%04X", vid, pid);
return; return;
} }
@@ -145,8 +225,8 @@ usb_device_added_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx)
priv->cancellable, priv->cancellable,
async_device_init_done_cb, async_device_init_done_cb,
self, self,
"fp-usb-device", device, "fpi-usb-device", device,
"fp-driver-data", found_entry->driver_data, "fpi-driver-data", found_entry->driver_data,
NULL); NULL);
} }
@@ -166,12 +246,7 @@ usb_device_removed_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx
continue; continue;
if (fpi_device_get_usb_device (dev) == device) if (fpi_device_get_usb_device (dev) == device)
{ fpi_device_remove (dev);
g_signal_emit (self, signals[DEVICE_REMOVED_SIGNAL], 0, dev);
g_ptr_array_remove_index_fast (priv->devices, i);
return;
}
} }
} }
@@ -187,6 +262,7 @@ fp_context_finalize (GObject *object)
g_clear_object (&priv->cancellable); g_clear_object (&priv->cancellable);
g_clear_pointer (&priv->drivers, g_array_unref); g_clear_pointer (&priv->drivers, g_array_unref);
if (priv->usb_ctx)
g_object_run_dispose (G_OBJECT (priv->usb_ctx)); g_object_run_dispose (G_OBJECT (priv->usb_ctx));
g_clear_object (&priv->usb_ctx); g_clear_object (&priv->usb_ctx);
@@ -224,6 +300,10 @@ fp_context_class_init (FpContextClass *klass)
* @device: A #FpDevice * @device: A #FpDevice
* *
* This signal is emitted when a fingerprint reader is removed. * This signal is emitted when a fingerprint reader is removed.
*
* It is guaranteed that the device has been closed before this signal
* is emitted. See the #FpDevice removed signal documentation for more
* information.
**/ **/
signals[DEVICE_REMOVED_SIGNAL] = g_signal_new ("device-removed", signals[DEVICE_REMOVED_SIGNAL] = g_signal_new ("device-removed",
G_TYPE_FROM_CLASS (klass), G_TYPE_FROM_CLASS (klass),
@@ -242,9 +322,23 @@ fp_context_init (FpContext *self)
{ {
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
FpContextPrivate *priv = fp_context_get_instance_private (self); FpContextPrivate *priv = fp_context_get_instance_private (self);
guint i;
priv->drivers = g_array_new (TRUE, FALSE, sizeof (GType)); priv->drivers = fpi_get_driver_types ();
fpi_get_driver_types (priv->drivers);
if (get_drivers_whitelist_env ())
{
for (i = 0; i < priv->drivers->len;)
{
GType driver = g_array_index (priv->drivers, GType, i);
g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver);
if (!is_driver_allowed (cls->id))
g_array_remove_index (priv->drivers, i);
else
++i;
}
}
priv->devices = g_ptr_array_new_with_free_func (g_object_unref); priv->devices = g_ptr_array_new_with_free_func (g_object_unref);
@@ -252,7 +346,7 @@ fp_context_init (FpContext *self)
priv->usb_ctx = g_usb_context_new (&error); priv->usb_ctx = g_usb_context_new (&error);
if (!priv->usb_ctx) if (!priv->usb_ctx)
{ {
fp_warn ("Could not initialise USB Subsystem: %s", error->message); g_message ("Could not initialise USB Subsystem: %s", error->message);
} }
else else
{ {
@@ -305,13 +399,14 @@ fp_context_enumerate (FpContext *context)
priv->enumerated = TRUE; priv->enumerated = TRUE;
/* USB devices are handled from callbacks */ /* USB devices are handled from callbacks */
if (priv->usb_ctx)
g_usb_context_enumerate (priv->usb_ctx); g_usb_context_enumerate (priv->usb_ctx);
/* Handle Virtual devices based on environment variables */ /* Handle Virtual devices based on environment variables */
for (i = 0; i < priv->drivers->len; i++) for (i = 0; i < priv->drivers->len; i++)
{ {
GType driver = g_array_index (priv->drivers, GType, i); GType driver = g_array_index (priv->drivers, GType, i);
FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver)); g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver);
const FpIdEntry *entry; const FpIdEntry *entry;
if (cls->type != FP_DEVICE_TYPE_VIRTUAL) if (cls->type != FP_DEVICE_TYPE_VIRTUAL)
@@ -332,13 +427,11 @@ fp_context_enumerate (FpContext *context)
priv->cancellable, priv->cancellable,
async_device_init_done_cb, async_device_init_done_cb,
context, context,
"fp-environ", val, "fpi-environ", val,
"fp-driver-data", entry->driver_data, "fpi-driver-data", entry->driver_data,
NULL); NULL);
g_debug ("created"); g_debug ("created");
} }
g_type_class_unref (cls);
} }
while (priv->pending_devices) while (priv->pending_devices)

View File

@@ -0,0 +1,84 @@
/*
* FpDevice - A fingerprint reader device
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "fpi-device.h"
typedef struct
{
FpDeviceType type;
GUsbDevice *usb_device;
const gchar *virtual_env;
gboolean is_removed;
gboolean is_open;
gchar *device_id;
gchar *device_name;
FpScanType scan_type;
guint64 driver_data;
gint nr_enroll_stages;
GSList *sources;
/* We always make sure that only one task is run at a time. */
FpiDeviceAction current_action;
GTask *current_task;
GAsyncReadyCallback current_user_cb;
gulong current_cancellable_id;
GSource *current_idle_cancel_source;
GSource *current_task_idle_return_source;
/* State for tasks */
gboolean wait_for_finger;
FpFingerStatusFlags finger_status;
} FpDevicePrivate;
typedef struct
{
FpPrint *print;
FpEnrollProgress enroll_progress_cb;
gpointer enroll_progress_data;
GDestroyNotify enroll_progress_destroy;
} FpEnrollData;
void enroll_data_free (FpEnrollData *enroll_data);
typedef struct
{
FpPrint *enrolled_print; /* verify */
GPtrArray *gallery; /* identify */
gboolean result_reported;
FpPrint *match;
FpPrint *print;
GError *error;
FpMatchCb match_cb;
gpointer match_data;
GDestroyNotify match_destroy;
} FpMatchData;
void match_data_free (FpMatchData *match_data);

File diff suppressed because it is too large Load Diff

View File

@@ -79,7 +79,7 @@ typedef enum {
/** /**
* FpDeviceError: * FpDeviceError:
* @FP_DEVICE_ERROR_GENERAL: A general error occured. * @FP_DEVICE_ERROR_GENERAL: A general error occurred.
* @FP_DEVICE_ERROR_NOT_SUPPORTED: The device does not support the requested * @FP_DEVICE_ERROR_NOT_SUPPORTED: The device does not support the requested
* operation. * operation.
* @FP_DEVICE_ERROR_NOT_OPEN: The device needs to be opened to start this * @FP_DEVICE_ERROR_NOT_OPEN: The device needs to be opened to start this
@@ -90,6 +90,8 @@ typedef enum {
* @FP_DEVICE_ERROR_DATA_INVALID: The passed data is invalid * @FP_DEVICE_ERROR_DATA_INVALID: The passed data is invalid
* @FP_DEVICE_ERROR_DATA_NOT_FOUND: Requested print was not found on device * @FP_DEVICE_ERROR_DATA_NOT_FOUND: Requested print was not found on device
* @FP_DEVICE_ERROR_DATA_FULL: No space on device available for operation * @FP_DEVICE_ERROR_DATA_FULL: No space on device available for operation
* @FP_DEVICE_ERROR_DATA_DUPLICATE: Enrolling template duplicates storaged templates
* @FP_DEVICE_ERROR_REMOVED: The device has been removed.
* *
* Error codes for device operations. More specific errors from other domains * 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. * such as #G_IO_ERROR or #G_USB_DEVICE_ERROR may also be reported.
@@ -104,6 +106,9 @@ typedef enum {
FP_DEVICE_ERROR_DATA_INVALID, FP_DEVICE_ERROR_DATA_INVALID,
FP_DEVICE_ERROR_DATA_NOT_FOUND, FP_DEVICE_ERROR_DATA_NOT_FOUND,
FP_DEVICE_ERROR_DATA_FULL, FP_DEVICE_ERROR_DATA_FULL,
FP_DEVICE_ERROR_DATA_DUPLICATE,
/* Leave some room to add more DATA related errors */
FP_DEVICE_ERROR_REMOVED = 0x100,
} FpDeviceError; } FpDeviceError;
GQuark fp_device_retry_quark (void); GQuark fp_device_retry_quark (void);
@@ -113,7 +118,7 @@ GQuark fp_device_error_quark (void);
* FpEnrollProgress: * FpEnrollProgress:
* @device: a #FpDevice * @device: a #FpDevice
* @completed_stages: Number of completed stages * @completed_stages: Number of completed stages
* @print: (nullable) (transfer none): The last scaned print * @print: (nullable) (transfer none): The last scanned print
* @user_data: (nullable) (transfer none): User provided data * @user_data: (nullable) (transfer none): User provided data
* @error: (nullable) (transfer none): #GError or %NULL * @error: (nullable) (transfer none): #GError or %NULL
* *
@@ -125,11 +130,50 @@ typedef void (*FpEnrollProgress) (FpDevice *device,
gpointer user_data, gpointer user_data,
GError *error); GError *error);
/**
* FpMatchCb:
* @device: a #FpDevice
* @match: (nullable) (transfer none): The matching print if any matched @print
* @print: (nullable) (transfer none): The newly scanned print
* @user_data: (nullable) (transfer none): User provided data
* @error: (nullable) (transfer none): #GError or %NULL
*
* Report the result of a match (identify or verify) operation.
*
* If @match is non-%NULL, then it is set to the matching #FpPrint as passed
* to the match operation. In this case @error will always be %NULL.
*
* If @error is not %NULL then its domain is guaranteed to be
* %FP_DEVICE_RETRY. All other error conditions will not be reported using
* this callback. If such an error occurs before a match/no-match decision
* can be made, then this callback will not be called. Should an error
* happen afterwards, then you will get a match report through this callback
* and an error when the operation finishes.
*
* If @match and @error are %NULL, then a finger was presented but it did not
* match any known print.
*
* @print represents the newly scanned print. The driver may or may not
* provide this information. Image based devices will provide it and it
* allows access to the raw data.
*
* This callback exists because it makes sense for drivers to wait e.g. on
* finger removal before completing the match operation. However, the
* success/failure can often be reported at an earlier time, and there is
* no need to make the user wait.
*/
typedef void (*FpMatchCb) (FpDevice *device,
FpPrint *match,
FpPrint *print,
gpointer user_data,
GError *error);
const gchar *fp_device_get_driver (FpDevice *device); const gchar *fp_device_get_driver (FpDevice *device);
const gchar *fp_device_get_device_id (FpDevice *device); const gchar *fp_device_get_device_id (FpDevice *device);
const gchar *fp_device_get_name (FpDevice *device); const gchar *fp_device_get_name (FpDevice *device);
gboolean fp_device_is_open (FpDevice *device);
FpScanType fp_device_get_scan_type (FpDevice *device); FpScanType fp_device_get_scan_type (FpDevice *device);
FpFingerStatusFlags fp_device_get_finger_status (FpDevice *device);
gint fp_device_get_nr_enroll_stages (FpDevice *device); gint fp_device_get_nr_enroll_stages (FpDevice *device);
gboolean fp_device_supports_identify (FpDevice *device); gboolean fp_device_supports_identify (FpDevice *device);
@@ -159,12 +203,18 @@ void fp_device_enroll (FpDevice *device,
void fp_device_verify (FpDevice *device, void fp_device_verify (FpDevice *device,
FpPrint *enrolled_print, FpPrint *enrolled_print,
GCancellable *cancellable, GCancellable *cancellable,
FpMatchCb match_cb,
gpointer match_data,
GDestroyNotify match_destroy,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
void fp_device_identify (FpDevice *device, void fp_device_identify (FpDevice *device,
GPtrArray *prints, GPtrArray *prints,
GCancellable *cancellable, GCancellable *cancellable,
FpMatchCb match_cb,
gpointer match_data,
GDestroyNotify match_destroy,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
@@ -230,12 +280,16 @@ FpPrint * fp_device_enroll_sync (FpDevice *device,
gboolean fp_device_verify_sync (FpDevice *device, gboolean fp_device_verify_sync (FpDevice *device,
FpPrint *enrolled_print, FpPrint *enrolled_print,
GCancellable *cancellable, GCancellable *cancellable,
FpMatchCb match_cb,
gpointer match_data,
gboolean *match, gboolean *match,
FpPrint **print, FpPrint **print,
GError **error); GError **error);
gboolean fp_device_identify_sync (FpDevice *device, gboolean fp_device_identify_sync (FpDevice *device,
GPtrArray *prints, GPtrArray *prints,
GCancellable *cancellable, GCancellable *cancellable,
FpMatchCb match_cb,
gpointer match_data,
FpPrint **match, FpPrint **match,
FpPrint **print, FpPrint **print,
GError **error); GError **error);

View File

@@ -0,0 +1,45 @@
/*
* FpImageDevice - An image based fingerprint reader device
* Copyright (C) 2019 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
* 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-image-device.h"
#define IMG_ENROLL_STAGES 5
typedef struct
{
FpiImageDeviceState state;
gboolean active;
gboolean finger_present;
gint enroll_stage;
gboolean minutiae_scan_active;
GError *action_error;
FpImage *capture_image;
gint bz3_threshold;
} FpImageDevicePrivate;
void fpi_image_device_activate (FpImageDevice *image_device);
void fpi_image_device_deactivate (FpImageDevice *image_device,
gboolean cancelling);

View File

@@ -20,14 +20,9 @@
#define FP_COMPONENT "image_device" #define FP_COMPONENT "image_device"
#include "fpi-log.h" #include "fpi-log.h"
#include "fpi-image-device.h" #include "fp-image-device-private.h"
#include "fpi-enums.h"
#include "fpi-print.h"
#include "fpi-image.h"
#define MIN_ACCEPTABLE_MINUTIAE 10
#define BOZORTH3_DEFAULT_THRESHOLD 40 #define BOZORTH3_DEFAULT_THRESHOLD 40
#define IMG_ENROLL_STAGES 5
/** /**
* SECTION: fp-image-device * SECTION: fp-image-device
@@ -37,30 +32,6 @@
* This is a helper class for the commonly found image based devices. * This is a helper class for the commonly found image based devices.
*/ */
/**
* SECTION: fpi-image-device
* @title: Internal FpImageDevice
* @short_description: Internal image device routines
*
* See #FpImageDeviceClass for more details. Also see the public
* #FpImageDevice routines.
*/
typedef struct
{
FpImageDeviceState state;
gboolean active;
gboolean cancelling;
gboolean enroll_await_on_pending;
gint enroll_stage;
guint pending_activation_timeout_id;
gboolean pending_activation_timeout_waiting_finger_off;
gint bz3_threshold;
} FpImageDevicePrivate;
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (FpImageDevice, fp_image_device, FP_TYPE_DEVICE) G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (FpImageDevice, fp_image_device, FP_TYPE_DEVICE)
enum { enum {
@@ -85,91 +56,6 @@ static guint signals[LAST_SIGNAL] = { 0 };
* - sanitize_image seems a bit odd, in particular the sizing stuff. * - sanitize_image seems a bit odd, in particular the sizing stuff.
**/ **/
/* Static helper functions */
static void
fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
/* Cannot change to inactive using this function. */
g_assert (state != FP_IMAGE_DEVICE_STATE_INACTIVE);
/* We might have been waiting for the finger to go OFF to start the
* next operation. */
g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove);
fp_dbg ("Image device internal state change from %d to %d\n", priv->state, state);
priv->state = state;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]);
g_signal_emit (self, signals[FPI_STATE_CHANGED], 0, priv->state);
}
static void
fp_image_device_activate (FpImageDevice *self)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
g_assert (!priv->active);
/* We don't have a neutral ACTIVE state, but we always will
* go into WAIT_FINGER_ON afterwards. */
priv->state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]);
/* We might have been waiting for deactivation to finish before
* starting the next operation. */
g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove);
fp_dbg ("Activating image device\n");
cls->activate (self);
}
static void
fp_image_device_deactivate (FpDevice *device)
{
FpImageDevice *self = FP_IMAGE_DEVICE (device);
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (device);
if (!priv->active)
{
/* XXX: We currently deactivate both from minutiae scan result
* and finger off report. */
fp_dbg ("Already deactivated, ignoring request.");
return;
}
if (!priv->cancelling && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
g_warning ("Deactivating image device while waiting for finger, this should not happen.");
priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]);
fp_dbg ("Deactivating image device\n");
cls->deactivate (self);
}
static gboolean
pending_activation_timeout (gpointer user_data)
{
FpImageDevice *self = FP_IMAGE_DEVICE (user_data);
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
priv->pending_activation_timeout_id = 0;
if (priv->pending_activation_timeout_waiting_finger_off)
fpi_device_action_error (FP_DEVICE (self),
fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER,
"Remove finger before requesting another scan operation"));
else
fpi_device_action_error (FP_DEVICE (self),
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
return G_SOURCE_REMOVE;
}
/* Callbacks/vfuncs */ /* Callbacks/vfuncs */
static void static void
fp_image_device_open (FpDevice *device) fp_image_device_open (FpDevice *device)
@@ -188,47 +74,25 @@ fp_image_device_close (FpDevice *device)
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self); FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
/* In the close case we may need to wait/force deactivation first. g_assert (priv->active == FALSE);
* Three possible cases:
* 1. We are inactive
* -> immediately close
* 2. We are waiting for finger off
* -> imediately deactivate
* 3. We are deactivating
* -> handled by deactivate_complete */
if (!priv->active)
cls->img_close (self); cls->img_close (self);
else if (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE)
fp_image_device_deactivate (device);
} }
static void static void
fp_image_device_cancel_action (FpDevice *device) fp_image_device_cancel_action (FpDevice *device)
{ {
FpImageDevice *self = FP_IMAGE_DEVICE (device); FpImageDevice *self = FP_IMAGE_DEVICE (device);
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); FpiDeviceAction action;
FpDeviceAction action;
action = fpi_device_get_current_action (device); action = fpi_device_get_current_action (device);
/* We can only cancel capture operations, in that case, deactivate and return /* We can only cancel capture operations, in that case, deactivate and return
* an error immediately. */ * an error immediately. */
if (action == FP_DEVICE_ACTION_ENROLL || if (action == FPI_DEVICE_ACTION_ENROLL ||
action == FP_DEVICE_ACTION_VERIFY || action == FPI_DEVICE_ACTION_VERIFY ||
action == FP_DEVICE_ACTION_IDENTIFY || action == FPI_DEVICE_ACTION_IDENTIFY ||
action == FP_DEVICE_ACTION_CAPTURE) action == FPI_DEVICE_ACTION_CAPTURE)
{ fpi_image_device_deactivate (self, TRUE);
priv->cancelling = TRUE;
fp_image_device_deactivate (FP_DEVICE (self));
priv->cancelling = FALSE;
/* XXX: Some nicer way of doing this would be good. */
fpi_device_action_error (FP_DEVICE (self),
g_error_new (G_IO_ERROR,
G_IO_ERROR_CANCELLED,
"Device operation was cancelled"));
}
} }
static void static void
@@ -236,14 +100,14 @@ fp_image_device_start_capture_action (FpDevice *device)
{ {
FpImageDevice *self = FP_IMAGE_DEVICE (device); FpImageDevice *self = FP_IMAGE_DEVICE (device);
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpDeviceAction action; FpiDeviceAction action;
/* There is just one action that we cannot support out /* There is just one action that we cannot support out
* of the box, which is a capture without first waiting * of the box, which is a capture without first waiting
* for a finger to be on the device. * for a finger to be on the device.
*/ */
action = fpi_device_get_current_action (device); action = fpi_device_get_current_action (device);
if (action == FP_DEVICE_ACTION_CAPTURE) if (action == FPI_DEVICE_ACTION_CAPTURE)
{ {
gboolean wait_for_finger; gboolean wait_for_finger;
@@ -255,40 +119,22 @@ fp_image_device_start_capture_action (FpDevice *device)
return; return;
} }
} }
else if (action == FP_DEVICE_ACTION_ENROLL) else if (action == FPI_DEVICE_ACTION_ENROLL)
{ {
FpPrint *enroll_print = NULL; FpPrint *enroll_print = NULL;
fpi_device_get_enroll_data (device, &enroll_print); fpi_device_get_enroll_data (device, &enroll_print);
fpi_print_set_type (enroll_print, FP_PRINT_NBIS); fpi_print_set_type (enroll_print, FPI_PRINT_NBIS);
} }
priv->enroll_stage = 0; priv->enroll_stage = 0;
priv->enroll_await_on_pending = FALSE; /* The internal state machine guarantees both of these. */
g_assert (!priv->finger_present);
/* The device might still be deactivating from a previous call. g_assert (!priv->minutiae_scan_active);
* In that situation, try to wait for a bit before reporting back an
* error (which will usually say that the user should remove the
* finger).
*/
if (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE || priv->active)
{
g_debug ("Got a new request while the device was still active");
g_assert (priv->pending_activation_timeout_id == 0);
priv->pending_activation_timeout_id =
g_timeout_add (100, pending_activation_timeout, device);
if (priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
priv->pending_activation_timeout_waiting_finger_off = TRUE;
else
priv->pending_activation_timeout_waiting_finger_off = FALSE;
return;
}
/* And activate the device; we rely on fpi_image_device_activate_complete() /* And activate the device; we rely on fpi_image_device_activate_complete()
* to be called when done (or immediately). */ * to be called when done (or immediately). */
fp_image_device_activate (self); fpi_image_device_activate (self);
} }
@@ -301,7 +147,6 @@ fp_image_device_finalize (GObject *object)
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
g_assert (priv->active == FALSE); g_assert (priv->active == FALSE);
g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove);
G_OBJECT_CLASS (fp_image_device_parent_class)->finalize (object); G_OBJECT_CLASS (fp_image_device_parent_class)->finalize (object);
} }
@@ -338,6 +183,23 @@ fp_image_device_get_property (GObject *object,
} }
} }
static void
fp_image_device_constructed (GObject *obj)
{
FpImageDevice *self = FP_IMAGE_DEVICE (obj);
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
/* Set default values. */
fpi_device_set_nr_enroll_stages (FP_DEVICE (self), IMG_ENROLL_STAGES);
priv->bz3_threshold = BOZORTH3_DEFAULT_THRESHOLD;
if (cls->bz3_threshold > 0)
priv->bz3_threshold = cls->bz3_threshold;
G_OBJECT_CLASS (fp_image_device_parent_class)->constructed (obj);
}
static void static void
fp_image_device_class_init (FpImageDeviceClass *klass) fp_image_device_class_init (FpImageDeviceClass *klass)
{ {
@@ -346,6 +208,7 @@ fp_image_device_class_init (FpImageDeviceClass *klass)
object_class->finalize = fp_image_device_finalize; object_class->finalize = fp_image_device_finalize;
object_class->get_property = fp_image_device_get_property; object_class->get_property = fp_image_device_get_property;
object_class->constructed = fp_image_device_constructed;
fp_device_class->open = fp_image_device_open; fp_device_class->open = fp_image_device_open;
fp_device_class->close = fp_image_device_close; fp_device_class->close = fp_image_device_close;
@@ -360,21 +223,37 @@ fp_image_device_class_init (FpImageDeviceClass *klass)
klass->activate = fp_image_device_default_activate; klass->activate = fp_image_device_default_activate;
klass->deactivate = fp_image_device_default_deactivate; klass->deactivate = fp_image_device_default_deactivate;
/**
* FpImageDevice::fpi-image-device-state: (skip)
*
* This property is only for internal purposes.
*
* Stability: private
*/
properties[PROP_FPI_STATE] = properties[PROP_FPI_STATE] =
g_param_spec_enum ("fp-image-device-state", g_param_spec_enum ("fpi-image-device-state",
"Image Device State", "Image Device State",
"Private: The state of the image device", "Private: The state of the image device",
FP_TYPE_IMAGE_DEVICE_STATE, FPI_TYPE_IMAGE_DEVICE_STATE,
FP_IMAGE_DEVICE_STATE_INACTIVE, FPI_IMAGE_DEVICE_STATE_INACTIVE,
G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
/**
* FpImageDevice::fpi-image-device-state-changed: (skip)
* @image_device: A #FpImageDevice
* @new_state: The new state of the device
*
* This signal is only for internal purposes.
*
* Stability: private
*/
signals[FPI_STATE_CHANGED] = signals[FPI_STATE_CHANGED] =
g_signal_new ("fp-image-device-state-changed", g_signal_new ("fpi-image-device-state-changed",
G_TYPE_FROM_CLASS (object_class), G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_FIRST, G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (FpImageDeviceClass, change_state), G_STRUCT_OFFSET (FpImageDeviceClass, change_state),
NULL, NULL, NULL, NULL, NULL, NULL,
G_TYPE_NONE, 1, FP_TYPE_IMAGE_DEVICE_STATE); G_TYPE_NONE, 1, FPI_TYPE_IMAGE_DEVICE_STATE);
g_object_class_install_properties (object_class, N_PROPS, properties); g_object_class_install_properties (object_class, N_PROPS, properties);
} }
@@ -382,497 +261,4 @@ fp_image_device_class_init (FpImageDeviceClass *klass)
static void static void
fp_image_device_init (FpImageDevice *self) fp_image_device_init (FpImageDevice *self)
{ {
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
/* Set default values. */
fpi_device_set_nr_enroll_stages (FP_DEVICE (self), IMG_ENROLL_STAGES);
priv->bz3_threshold = BOZORTH3_DEFAULT_THRESHOLD;
if (cls->bz3_threshold > 0)
priv->bz3_threshold = cls->bz3_threshold;
}
static void
fp_image_device_enroll_maybe_await_finger_on (FpImageDevice *self)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
if (priv->enroll_await_on_pending)
{
priv->enroll_await_on_pending = FALSE;
fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON);
}
else
{
priv->enroll_await_on_pending = TRUE;
}
}
static void
fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FpImage) image = FP_IMAGE (source_object);
g_autoptr(FpPrint) print = NULL;
GError *error = NULL;
FpDevice *device = FP_DEVICE (user_data);
FpImageDevicePrivate *priv;
FpDeviceAction action;
/* Note: We rely on the device to not disappear during an operation. */
if (!fp_image_detect_minutiae_finish (image, res, &error))
{
/* Cancel operation . */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
fpi_device_action_error (device, g_steal_pointer (&error));
fp_image_device_deactivate (device);
return;
}
/* Replace error with a retry condition. */
g_warning ("Failed to detect minutiae: %s", error->message);
g_clear_pointer (&error, g_error_free);
error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_GENERAL, "Minutiae detection failed, please retry");
}
priv = fp_image_device_get_instance_private (FP_IMAGE_DEVICE (device));
action = fpi_device_get_current_action (device);
if (action == FP_DEVICE_ACTION_CAPTURE)
{
fpi_device_capture_complete (device, g_steal_pointer (&image), error);
fp_image_device_deactivate (device);
return;
}
if (!error)
{
print = fp_print_new (device);
fpi_print_set_type (print, FP_PRINT_NBIS);
if (!fpi_print_add_from_image (print, image, &error))
g_clear_object (&print);
}
if (action == FP_DEVICE_ACTION_ENROLL)
{
FpPrint *enroll_print;
fpi_device_get_enroll_data (device, &enroll_print);
if (print)
{
fpi_print_add_print (enroll_print, print);
priv->enroll_stage += 1;
}
fpi_device_enroll_progress (device, priv->enroll_stage,
g_steal_pointer (&print), error);
/* Start another scan or deactivate. */
if (priv->enroll_stage == IMG_ENROLL_STAGES)
{
fpi_device_enroll_complete (device, g_object_ref (enroll_print), NULL);
fp_image_device_deactivate (device);
}
else
{
fp_image_device_enroll_maybe_await_finger_on (FP_IMAGE_DEVICE (device));
}
}
else if (action == FP_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;
fpi_device_verify_complete (device, result, g_steal_pointer (&print), error);
fp_image_device_deactivate (device);
}
else if (action == FP_DEVICE_ACTION_IDENTIFY)
{
gint i;
GPtrArray *templates;
FpPrint *result = NULL;
fpi_device_get_identify_data (device, &templates);
for (i = 0; !error && i < templates->len; i++)
{
FpPrint *template = g_ptr_array_index (templates, i);
if (fpi_print_bz3_match (template, print, priv->bz3_threshold, &error) == FPI_MATCH_SUCCESS)
{
result = g_object_ref (template);
break;
}
}
fpi_device_identify_complete (device, result, g_steal_pointer (&print), error);
fp_image_device_deactivate (device);
}
else
{
/* XXX: This can be hit currently due to a race condition in the enroll code!
* In that case we scan a further image even though the minutiae for the previous
* one have not yet been detected.
* We need to keep track on the pending minutiae detection and the fact that
* it will finish eventually (or we may need to retry on error and activate the
* device again). */
g_assert_not_reached ();
}
}
/*********************************************************/
/* Private API */
/**
* fpi_image_device_set_bz3_threshold:
* @self: a #FpImageDevice imaging fingerprint device
* @bz3_threshold: BZ3 threshold to use
*
* Dynamically adjust the bz3 threshold. This is only needed for drivers
* that support devices with different properties. It should generally be
* called from the probe callback, but is acceptable to call from the open
* callback.
*/
void
fpi_image_device_set_bz3_threshold (FpImageDevice *self,
gint bz3_threshold)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
g_return_if_fail (FP_IS_IMAGE_DEVICE (self));
g_return_if_fail (bz3_threshold > 0);
priv->bz3_threshold = bz3_threshold;
}
/**
* fpi_image_device_report_finger_status:
* @self: a #FpImageDevice imaging fingerprint device
* @present: whether the finger is present on the sensor
*
* Reports from the driver whether the user's finger is on
* the sensor.
*/
void
fpi_image_device_report_finger_status (FpImageDevice *self,
gboolean present)
{
FpDevice *device = FP_DEVICE (self);
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpDeviceAction action;
if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE)
{
/* Do we really want to always ignore such reports? We could
* also track the state in case the user had the finger on
* the device at initialisation time and the driver reports
* this early.
*/
g_debug ("Ignoring finger presence report as the device is not active!");
return;
}
action = fpi_device_get_current_action (device);
g_assert (action != FP_DEVICE_ACTION_OPEN);
g_assert (action != FP_DEVICE_ACTION_CLOSE);
g_debug ("Image device reported finger status: %s", present ? "on" : "off");
if (present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
{
fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_CAPTURE);
}
else if (!present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
{
/* We need to deactivate or continue to await finger */
/* There are three possible situations:
* 1. We are deactivating the device and the action is still in progress
* (minutiae detection).
* 2. We are still deactivating the device after an action completed
* 3. We were waiting for finger removal to start the new action
* Either way, we always end up deactivating except for the enroll case.
*
* The enroll case is special as AWAIT_FINGER_ON should only happen after
* minutiae detection to prevent deactivation (without cancellation)
* from the AWAIT_FINGER_ON state.
*/
if (action != FP_DEVICE_ACTION_ENROLL)
fp_image_device_deactivate (device);
else
fp_image_device_enroll_maybe_await_finger_on (self);
}
}
/**
* fpi_image_device_image_captured:
* @self: a #FpImageDevice imaging fingerprint device
* @image: whether the finger is present on the sensor
*
* Reports an image capture. Only use this function if the image was
* captured successfully. If there was an issue where the user should
* retry, use fpi_image_device_retry_scan() to report the retry condition.
*
* In the event of a fatal error for the operation use
* fpi_image_device_session_error(). This will abort the entire operation
* including e.g. an enroll operation which captures multiple images during
* one session.
*/
void
fpi_image_device_image_captured (FpImageDevice *self, FpImage *image)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (image != NULL);
g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_CAPTURE);
g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL ||
action == FP_DEVICE_ACTION_VERIFY ||
action == FP_DEVICE_ACTION_IDENTIFY ||
action == FP_DEVICE_ACTION_CAPTURE);
fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF);
g_debug ("Image device captured an image");
/* XXX: We also detect minutiae in capture mode, we solely do this
* to normalize the image which will happen as a by-product. */
fp_image_detect_minutiae (image,
fpi_device_get_cancellable (FP_DEVICE (self)),
fpi_image_device_minutiae_detected,
self);
}
/**
* fpi_image_device_retry_scan:
* @self: a #FpImageDevice imaging fingerprint device
* @retry: The #FpDeviceRetry error code to report
*
* Reports a scan failure to the user. This may or may not abort the
* current session. It is the equivalent of fpi_image_device_image_captured()
* in the case of a retryable error condition (e.g. short swipe).
*/
void
fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpDeviceAction action;
GError *error;
action = fpi_device_get_current_action (FP_DEVICE (self));
/* We might be waiting for a finger at this point, so just accept
* all but INACTIVE */
g_return_if_fail (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE);
g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL ||
action == FP_DEVICE_ACTION_VERIFY ||
action == FP_DEVICE_ACTION_IDENTIFY ||
action == FP_DEVICE_ACTION_CAPTURE);
error = fpi_device_retry_new (retry);
if (action == FP_DEVICE_ACTION_ENROLL)
{
g_debug ("Reporting retry during enroll");
fpi_device_enroll_progress (FP_DEVICE (self), priv->enroll_stage, NULL, error);
}
else
{
/* We abort the operation and let the surrounding code retry in the
* non-enroll case (this is identical to a session error). */
g_debug ("Abort current operation due to retry (non-enroll case)");
fp_image_device_deactivate (FP_DEVICE (self));
fpi_device_action_error (FP_DEVICE (self), error);
}
}
/**
* fpi_image_device_session_error:
* @self: a #FpImageDevice imaging fingerprint device
* @error: The #GError to report
*
* Report an error while interacting with the device. This effectively
* aborts the current ongoing action.
*/
void
fpi_image_device_session_error (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
g_return_if_fail (self);
if (!error)
{
g_warning ("Driver did not provide an error, generating a generic one");
error = g_error_new (FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL, "Driver reported session error without an error");
}
if (!priv->active)
{
FpDeviceAction action = fpi_device_get_current_action (FP_DEVICE (self));
g_warning ("Driver reported session error, but device is inactive.");
if (action != FP_DEVICE_ACTION_NONE)
{
g_warning ("Translating to activation failure!");
fpi_image_device_activate_complete (self, error);
return;
}
}
else if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE)
{
g_warning ("Driver reported session error; translating to deactivation failure.");
fpi_image_device_deactivate_complete (self, error);
return;
}
if (error->domain == FP_DEVICE_RETRY)
g_warning ("Driver should report retries using fpi_image_device_retry_scan!");
fp_image_device_deactivate (FP_DEVICE (self));
fpi_device_action_error (FP_DEVICE (self), error);
}
/**
* fpi_image_device_activate_complete:
* @self: a #FpImageDevice imaging fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of device activation.
*/
void
fpi_image_device_activate_complete (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (priv->active == FALSE);
g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL ||
action == FP_DEVICE_ACTION_VERIFY ||
action == FP_DEVICE_ACTION_IDENTIFY ||
action == FP_DEVICE_ACTION_CAPTURE);
if (error)
{
g_debug ("Image device activation failed");
fpi_device_action_error (FP_DEVICE (self), error);
return;
}
g_debug ("Image device activation completed");
priv->active = TRUE;
/* We always want to capture at this point, move to AWAIT_FINGER
* state. */
fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON);
}
/**
* fpi_image_device_deactivate_complete:
* @self: a #FpImageDevice imaging fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of device deactivation.
*/
void
fpi_image_device_deactivate_complete (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
FpDeviceAction action;
g_return_if_fail (priv->active == TRUE);
g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE);
g_debug ("Image device deactivation completed");
priv->active = FALSE;
/* Deactivation completed. As we deactivate in the background
* there may already be a new task pending. Check whether we
* need to do anything. */
action = fpi_device_get_current_action (FP_DEVICE (self));
/* Special case, if we should be closing, but didn't due to a running
* deactivation, then do so now. */
if (action == FP_DEVICE_ACTION_CLOSE)
{
cls->img_close (self);
return;
}
/* We might be waiting to be able to activate again. */
if (priv->pending_activation_timeout_id)
{
g_clear_handle_id (&priv->pending_activation_timeout_id, g_source_remove);
priv->pending_activation_timeout_id =
g_idle_add ((GSourceFunc) fp_image_device_activate, self);
}
}
/**
* fpi_image_device_open_complete:
* @self: a #FpImageDevice imaging fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of open operation.
*/
void
fpi_image_device_open_complete (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (priv->active == FALSE);
g_return_if_fail (action == FP_DEVICE_ACTION_OPEN);
g_debug ("Image device open completed");
priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]);
fpi_device_open_complete (FP_DEVICE (self), error);
}
/**
* fpi_image_device_close_complete:
* @self: a #FpImageDevice imaging fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of close operation.
*/
void
fpi_image_device_close_complete (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_debug ("Image device close completed");
g_return_if_fail (priv->active == FALSE);
g_return_if_fail (action == FP_DEVICE_ACTION_CLOSE);
priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FPI_STATE]);
fpi_device_close_complete (FP_DEVICE (self), error);
} }

View File

@@ -19,7 +19,7 @@
#pragma once #pragma once
#include <fp-device.h> #include "fp-device.h"
G_BEGIN_DECLS G_BEGIN_DECLS

View File

@@ -18,15 +18,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#define FP_COMPONENT "image"
#include "fpi-image.h" #include "fpi-image.h"
#include "fpi-log.h" #include "fpi-log.h"
#include <nbis.h> #include <nbis.h>
#if HAVE_PIXMAN
#include <pixman.h>
#endif
/** /**
* SECTION: fp-image * SECTION: fp-image
* @title: FpImage * @title: FpImage
@@ -36,15 +34,6 @@
* this object allows accessing this data. * this object allows accessing this data.
*/ */
/**
* SECTION: fpi-image
* @title: Internal FpImage
* @short_description: Internal image handling routines
*
* Internal image handling routines. Also see the public <ulink
* url="libfprint-FpImage.html">FpImage routines</ulink>.
*/
G_DEFINE_TYPE (FpImage, fp_image, G_TYPE_OBJECT) G_DEFINE_TYPE (FpImage, fp_image, G_TYPE_OBJECT)
enum { enum {
@@ -195,10 +184,8 @@ fp_image_detect_minutiae_cb (GObject *source_object,
GTask *task = G_TASK (res); GTask *task = G_TASK (res);
FpImage *image; FpImage *image;
DetectMinutiaeData *data = g_task_get_task_data (task); DetectMinutiaeData *data = g_task_get_task_data (task);
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task); if (!g_task_had_error (task))
if (!cancellable || !g_cancellable_is_cancelled (cancellable))
{ {
gint i; gint i;
image = FP_IMAGE (source_object); image = FP_IMAGE (source_object);
@@ -294,6 +281,7 @@ fp_image_detect_minutiae_thread_func (GTask *task,
gint map_w, map_h; gint map_w, map_h;
gint bw, bh, bd; gint bw, bh, bd;
gint r; gint r;
g_autofree LFSPARMS *lfsparms;
/* Normalize the image first */ /* Normalize the image first */
if (data->flags & FPI_IMAGE_H_FLIPPED) if (data->flags & FPI_IMAGE_H_FLIPPED)
@@ -307,12 +295,15 @@ fp_image_detect_minutiae_thread_func (GTask *task,
data->flags &= ~(FPI_IMAGE_H_FLIPPED | FPI_IMAGE_V_FLIPPED | FPI_IMAGE_COLORS_INVERTED); data->flags &= ~(FPI_IMAGE_H_FLIPPED | FPI_IMAGE_V_FLIPPED | FPI_IMAGE_COLORS_INVERTED);
lfsparms = g_memdup (&g_lfsparms_V2, sizeof (LFSPARMS));
lfsparms->remove_perimeter_pts = data->flags & FPI_IMAGE_PARTIAL ? TRUE : FALSE;
timer = g_timer_new (); timer = g_timer_new ();
r = get_minutiae (&minutiae, &quality_map, &direction_map, r = get_minutiae (&minutiae, &quality_map, &direction_map,
&low_contrast_map, &low_flow_map, &high_curve_map, &low_contrast_map, &low_flow_map, &high_curve_map,
&map_w, &map_h, &bdata, &bw, &bh, &bd, &map_w, &map_h, &bdata, &bw, &bh, &bd,
data->image, data->width, data->height, 8, data->image, data->width, data->height, 8,
data->ppmm, &g_lfsparms_V2); data->ppmm, lfsparms);
g_timer_stop (timer); g_timer_stop (timer);
fp_dbg ("Minutiae scan completed in %f secs", g_timer_elapsed (timer, NULL)); fp_dbg ("Minutiae scan completed in %f secs", g_timer_elapsed (timer, NULL));
@@ -327,6 +318,14 @@ fp_image_detect_minutiae_thread_func (GTask *task,
return; return;
} }
if (!data->minutiae || data->minutiae->num == 0)
{
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
"No minutiae found");
g_object_unref (task);
return;
}
g_task_return_boolean (task, TRUE); g_task_return_boolean (task, TRUE);
g_object_unref (task); g_object_unref (task);
} }
@@ -479,78 +478,6 @@ fp_image_detect_minutiae_finish (FpImage *self,
return g_task_propagate_boolean (G_TASK (result), error); return g_task_propagate_boolean (G_TASK (result), error);
} }
/**
* fpi_std_sq_dev:
* @buf: buffer (usually bitmap, one byte per pixel)
* @size: size of @buffer
*
* Calculates the squared standard deviation of the individual
* pixels in the buffer, as per the following formula:
* |[<!-- -->
* mean = sum (buf[0..size]) / size
* sq_dev = sum ((buf[0.size] - mean) ^ 2)
* ]|
* This function is usually used to determine whether image
* is empty.
*
* Returns: the squared standard deviation for @buffer
*/
gint
fpi_std_sq_dev (const guint8 *buf,
gint size)
{
guint64 res = 0, mean = 0;
gint i;
for (i = 0; i < size; i++)
mean += buf[i];
mean /= size;
for (i = 0; i < size; i++)
{
int dev = (int) buf[i] - mean;
res += dev * dev;
}
return res / size;
}
/**
* fpi_mean_sq_diff_norm:
* @buf1: buffer (usually bitmap, one byte per pixel)
* @buf2: buffer (usually bitmap, one byte per pixel)
* @size: buffer size of smallest buffer
*
* This function calculates the normalized mean square difference of
* two buffers, usually two lines, as per the following formula:
* |[<!-- -->
* sq_diff = sum ((buf1[0..size] - buf2[0..size]) ^ 2) / size
* ]|
*
* This functions is usually used to get numerical difference
* between two images.
*
* Returns: the normalized mean squared difference between @buf1 and @buf2
*/
gint
fpi_mean_sq_diff_norm (const guint8 *buf1,
const guint8 *buf2,
gint size)
{
int res = 0, i;
for (i = 0; i < size; i++)
{
int dev = (int) buf1[i] - (int) buf2[i];
res += dev * dev;
}
return res / size;
}
/** /**
* fp_minutia_get_coords: * fp_minutia_get_coords:
* @min: A #FpMinutia * @min: A #FpMinutia
@@ -568,44 +495,3 @@ fp_minutia_get_coords (FpMinutia *min, gint *x, gint *y)
if (y) if (y)
*y = min->y; *y = min->y;
} }
#if HAVE_PIXMAN
FpImage *
fpi_image_resize (FpImage *orig_img,
guint w_factor,
guint h_factor)
{
int new_width = orig_img->width * w_factor;
int new_height = orig_img->height * h_factor;
pixman_image_t *orig, *resized;
pixman_transform_t transform;
FpImage *newimg;
orig = pixman_image_create_bits (PIXMAN_a8, orig_img->width, orig_img->height, (uint32_t *) orig_img->data, orig_img->width);
resized = pixman_image_create_bits (PIXMAN_a8, new_width, new_height, NULL, new_width);
pixman_transform_init_identity (&transform);
pixman_transform_scale (NULL, &transform, pixman_int_to_fixed (w_factor), pixman_int_to_fixed (h_factor));
pixman_image_set_transform (orig, &transform);
pixman_image_set_filter (orig, PIXMAN_FILTER_BILINEAR, NULL, 0);
pixman_image_composite32 (PIXMAN_OP_SRC,
orig, /* src */
NULL, /* mask */
resized, /* dst */
0, 0, /* src x y */
0, 0, /* mask x y */
0, 0, /* dst x y */
new_width, new_height /* width height */
);
newimg = fp_image_new (new_width, new_height);
newimg->flags = orig_img->flags;
memcpy (newimg->data, pixman_image_get_data (resized), new_width * new_height);
pixman_image_unref (orig);
pixman_image_unref (resized);
return newimg;
}
#endif

View File

@@ -1,6 +1,7 @@
/* /*
* Driver IDs * FPrint Print handling
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com> * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@@ -17,31 +18,29 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __DRIVER_IDS #include "fpi-print.h"
#define __DRIVER_IDS #include "fpi-image.h"
enum { #include <nbis.h>
UPEKTS_ID = 1,
URU4000_ID = 2, struct _FpPrint
AES4000_ID = 3, {
AES2501_ID = 4, GInitiallyUnowned parent_instance;
UPEKTC_ID = 5,
AES1610_ID = 6, FpiPrintType type;
/* FDU2000_ID = 7, */
VCOM5S_ID = 8, gchar *driver;
UPEKSONLY_ID = 9, gchar *device_id;
VFS101_ID = 10, gboolean device_stored;
VFS301_ID = 11,
AES2550_ID = 12, FpImage *image;
/* UPEKE2_ID = 13 */
AES1660_ID = 14, /* Metadata */
AES2660_ID = 15, FpFinger finger;
AES3500_ID = 16, gchar *username;
UPEKTC_IMG_ID = 17, gchar *description;
ETES603_ID = 18, GDate *enroll_date;
VFS5011_ID = 19,
VFS0050_ID = 20, GVariant *data;
ELAN_ID = 21, GPtrArray *prints;
}; };
#endif

View File

@@ -18,12 +18,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "fpi-print.h" #define FP_COMPONENT "print"
#include "fpi-image.h"
#include "fpi-log.h"
#include "fpi-device.h"
#include <nbis.h> #include "fp-print-private.h"
#include "fpi-compat.h"
#include "fpi-log.h"
/** /**
* SECTION: fp-print * SECTION: fp-print
@@ -42,28 +41,6 @@
* #FpPrint routines. * #FpPrint routines.
*/ */
struct _FpPrint
{
GInitiallyUnowned parent_instance;
FpPrintType type;
gchar *driver;
gchar *device_id;
gboolean device_stored;
FpImage *image;
/* Metadata */
FpFinger finger;
gchar *username;
gchar *description;
GDate *enroll_date;
GVariant *data;
GPtrArray *prints;
};
G_DEFINE_TYPE (FpPrint, fp_print, G_TYPE_INITIALLY_UNOWNED) G_DEFINE_TYPE (FpPrint, fp_print, G_TYPE_INITIALLY_UNOWNED)
enum { enum {
@@ -74,7 +51,7 @@ enum {
PROP_IMAGE, PROP_IMAGE,
/* The following is metadata that is stored by default for each print. /* The following is metadata that is stored by default for each print.
* Drivers may make use of these during enrollment (e.g. to additionaly store * Drivers may make use of these during enrollment (e.g. to additionally store
* the metadata on the device). */ * the metadata on the device). */
PROP_FINGER, PROP_FINGER,
PROP_USERNAME, PROP_USERNAME,
@@ -292,16 +269,30 @@ fp_print_class_init (FpPrintClass *klass)
G_TYPE_DATE, G_TYPE_DATE,
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
/**
* FpPrint::fpi-type: (skip)
*
* This property is only for internal purposes.
*
* Stability: private
*/
properties[PROP_FPI_TYPE] = properties[PROP_FPI_TYPE] =
g_param_spec_enum ("fp-type", g_param_spec_enum ("fpi-type",
"Type", "Type",
"Private: The type of the print data", "Private: The type of the print data",
FP_TYPE_PRINT_TYPE, FPI_TYPE_PRINT_TYPE,
FP_PRINT_RAW, FPI_PRINT_RAW,
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* FpPrint::fpi-data: (skip)
*
* This property is only for internal purposes.
*
* Stability: private
*/
properties[PROP_FPI_DATA] = properties[PROP_FPI_DATA] =
g_param_spec_variant ("fp-data", g_param_spec_variant ("fpi-data",
"Raw Data", "Raw Data",
"The raw data for internal use only", "The raw data for internal use only",
G_VARIANT_TYPE_ANY, G_VARIANT_TYPE_ANY,
@@ -540,223 +531,6 @@ fp_print_set_enroll_date (FpPrint *print,
g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_ENROLL_DATE]); g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_ENROLL_DATE]);
} }
/**
* fpi_print_add_print:
* @print: A #FpPrint
* @add: Print to append to @print
*
* Appends the single #FP_PRINT_NBIS print from @add to the collection of
* prints in @print. Both print objects need to be of type #FP_PRINT_NBIS
* for this to work.
*/
void
fpi_print_add_print (FpPrint *print, FpPrint *add)
{
g_return_if_fail (print->type == FP_PRINT_NBIS);
g_return_if_fail (add->type == FP_PRINT_NBIS);
g_assert (add->prints->len == 1);
g_ptr_array_add (print->prints, g_memdup (add->prints->pdata[0], sizeof (struct xyt_struct)));
}
/**
* fpi_print_set_type:
* @print: A #FpPrint
* @type: The newly type of the print data
*
* This function can only be called exactly once. Drivers should
* call it after creating a new print, or to initialize the template
* print passed during enrollment.
*/
void
fpi_print_set_type (FpPrint *print,
FpPrintType type)
{
g_return_if_fail (FP_IS_PRINT (print));
/* We only allow setting this once! */
g_return_if_fail (print->type == FP_PRINT_UNDEFINED);
print->type = type;
if (print->type == FP_PRINT_NBIS)
{
g_assert_null (print->prints);
print->prints = g_ptr_array_new_with_free_func (g_free);
}
g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_FPI_TYPE]);
}
/**
* fpi_print_set_device_stored:
* @print: A #FpPrint
* @device_stored: Whether the print is stored on the device or not
*
* Drivers must set this to %TRUE for any print that is really a handle
* for data that is stored on the device itself.
*/
void
fpi_print_set_device_stored (FpPrint *print,
gboolean device_stored)
{
g_return_if_fail (FP_IS_PRINT (print));
print->device_stored = device_stored;
g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_DEVICE_STORED]);
}
/* XXX: This is the old version, but wouldn't it be smarter to instead
* use the highest quality mintutiae? Possibly just using bz_prune from
* upstream? */
static void
minutiae_to_xyt (struct fp_minutiae *minutiae,
int bwidth,
int bheight,
struct xyt_struct *xyt)
{
int i;
struct fp_minutia *minutia;
struct minutiae_struct c[MAX_FILE_MINUTIAE];
/* struct xyt_struct uses arrays of MAX_BOZORTH_MINUTIAE (200) */
int nmin = min (minutiae->num, MAX_BOZORTH_MINUTIAE);
for (i = 0; i < nmin; i++)
{
minutia = minutiae->list[i];
lfs2nist_minutia_XYT (&c[i].col[0], &c[i].col[1], &c[i].col[2],
minutia, bwidth, bheight);
c[i].col[3] = sround (minutia->reliability * 100.0);
if (c[i].col[2] > 180)
c[i].col[2] -= 360;
}
qsort ((void *) &c, (size_t) nmin, sizeof (struct minutiae_struct),
sort_x_y);
for (i = 0; i < nmin; i++)
{
xyt->xcol[i] = c[i].col[0];
xyt->ycol[i] = c[i].col[1];
xyt->thetacol[i] = c[i].col[2];
}
xyt->nrows = nmin;
}
/**
* fpi_print_add_from_image:
* @print: A #FpPrint
* @image: A #FpImage
* @error: Return location for error
*
* Extracts the minutiae from the given image and adds it to @print of
* type #FP_PRINT_NBIS.
*
* The @image will be kept so that API users can get retrieve it e.g.
* for debugging purposes.
*
* Returns: %TRUE on success
*/
gboolean
fpi_print_add_from_image (FpPrint *print,
FpImage *image,
GError **error)
{
GPtrArray *minutiae;
struct fp_minutiae _minutiae;
struct xyt_struct *xyt;
if (print->type != FP_PRINT_NBIS || !image)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"Cannot add print data from image!");
return FALSE;
}
minutiae = fp_image_get_minutiae (image);
if (!minutiae || minutiae->len == 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"No minutiae found in image or not yet detected!");
return FALSE;
}
_minutiae.num = minutiae->len;
_minutiae.list = (struct fp_minutia **) minutiae->pdata;
_minutiae.alloc = minutiae->len;
xyt = g_new0 (struct xyt_struct, 1);
minutiae_to_xyt (&_minutiae, image->width, image->height, xyt);
g_ptr_array_add (print->prints, xyt);
g_clear_object (&print->image);
print->image = g_object_ref (image);
g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_IMAGE]);
return TRUE;
}
/**
* fpi_print_bz3_match:
* @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
*
* Match the newly scanned @print (containing exactly one print) against the
* prints contained in @template which will have been stored during enrollment.
*
* Both @template and @print need to be of type #FP_PRINT_NBIS for this to
* work.
*
* Returns: Whether the prints match, @error will be set if #FPI_MATCH_ERROR is returned
*/
FpiMatchResult
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 (template->type != FP_PRINT_NBIS || print->type != FP_PRINT_NBIS)
{
*error = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED,
"It is only possible to match NBIS type print data");
return FPI_MATCH_ERROR;
}
if (print->prints->len != 1)
{
*error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"New print contains more than one print!");
return FPI_MATCH_ERROR;
}
pstruct = g_ptr_array_index (print->prints, 0);
probe_len = bozorth_probe_init (pstruct);
for (i = 0; i < template->prints->len; i++)
{
struct xyt_struct *gstruct;
gint score;
gstruct = g_ptr_array_index (template->prints, i);
score = bozorth_to_gallery (probe_len, pstruct, gstruct);
fp_dbg ("score %d", score);
if (score >= bz3_threshold)
return FPI_MATCH_SUCCESS;
}
return FPI_MATCH_FAIL;
}
/** /**
* fp_print_compatible: * fp_print_compatible:
* @self: A #FpPrint * @self: A #FpPrint
@@ -796,8 +570,8 @@ fp_print_equal (FpPrint *self, FpPrint *other)
{ {
g_return_val_if_fail (FP_IS_PRINT (self), FALSE); g_return_val_if_fail (FP_IS_PRINT (self), FALSE);
g_return_val_if_fail (FP_IS_PRINT (other), FALSE); g_return_val_if_fail (FP_IS_PRINT (other), FALSE);
g_return_val_if_fail (self->type != FP_PRINT_UNDEFINED, FALSE); g_return_val_if_fail (self->type != FPI_PRINT_UNDEFINED, FALSE);
g_return_val_if_fail (other->type != FP_PRINT_UNDEFINED, FALSE); g_return_val_if_fail (other->type != FPI_PRINT_UNDEFINED, FALSE);
if (self->type != other->type) if (self->type != other->type)
return FALSE; return FALSE;
@@ -808,13 +582,13 @@ fp_print_equal (FpPrint *self, FpPrint *other)
if (g_strcmp0 (self->device_id, other->device_id)) if (g_strcmp0 (self->device_id, other->device_id))
return FALSE; return FALSE;
if (self->type == FP_PRINT_RAW) if (self->type == FPI_PRINT_RAW)
{ {
return g_variant_equal (self->data, other->data); return g_variant_equal (self->data, other->data);
} }
else if (self->type == FP_PRINT_NBIS) else if (self->type == FPI_PRINT_NBIS)
{ {
gint i; guint i;
if (self->prints->len != other->prints->len) if (self->prints->len != other->prints->len)
return FALSE; return FALSE;
@@ -836,7 +610,7 @@ fp_print_equal (FpPrint *self, FpPrint *other)
} }
} }
#define FP_PRINT_VARIANT_TYPE G_VARIANT_TYPE ("(issbymsmsia{sv}v)") #define FPI_PRINT_VARIANT_TYPE G_VARIANT_TYPE ("(issbymsmsia{sv}v)")
G_STATIC_ASSERT (sizeof (((struct xyt_struct *) NULL)->xcol[0]) == 4); G_STATIC_ASSERT (sizeof (((struct xyt_struct *) NULL)->xcol[0]) == 4);
@@ -859,7 +633,7 @@ fp_print_serialize (FpPrint *print,
GError **error) GError **error)
{ {
g_autoptr(GVariant) result = NULL; g_autoptr(GVariant) result = NULL;
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (FP_PRINT_VARIANT_TYPE); GVariantBuilder builder = G_VARIANT_BUILDER_INIT (FPI_PRINT_VARIANT_TYPE);
gsize len; gsize len;
g_assert (data); g_assert (data);
@@ -884,10 +658,10 @@ fp_print_serialize (FpPrint *print,
g_variant_builder_close (&builder); g_variant_builder_close (&builder);
/* Insert NBIS print data for type NBIS, otherwise the GVariant directly */ /* Insert NBIS print data for type NBIS, otherwise the GVariant directly */
if (print->type == FP_PRINT_NBIS) if (print->type == FPI_PRINT_NBIS)
{ {
GVariantBuilder nested = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(a(aiaiai))")); GVariantBuilder nested = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(a(aiaiai))"));
gint i; guint i;
g_variant_builder_open (&nested, G_VARIANT_TYPE ("a(aiaiai)")); g_variant_builder_open (&nested, G_VARIANT_TYPE ("a(aiaiai)"));
for (i = 0; i < print->prints->len; i++) for (i = 0; i < print->prints->len; i++)
@@ -979,14 +753,14 @@ fp_print_deserialize (const guchar *data,
g_autoptr(GVariant) raw_value = NULL; g_autoptr(GVariant) raw_value = NULL;
g_autoptr(GVariant) value = NULL; g_autoptr(GVariant) value = NULL;
g_autoptr(GVariant) print_data = NULL; g_autoptr(GVariant) print_data = NULL;
g_autoptr(GDate) date = NULL;
guchar *aligned_data = NULL; guchar *aligned_data = NULL;
GDate *date = NULL;
guint8 finger_int8; guint8 finger_int8;
FpFinger finger; FpFinger finger;
g_autofree gchar *username = NULL; g_autofree gchar *username = NULL;
g_autofree gchar *description = NULL; g_autofree gchar *description = NULL;
gint julian_date; gint julian_date;
FpPrintType type; FpiPrintType type;
const gchar *driver; const gchar *driver;
const gchar *device_id; const gchar *device_id;
gboolean device_stored; gboolean device_stored;
@@ -1007,7 +781,7 @@ fp_print_deserialize (const guchar *data,
* longer. */ * longer. */
aligned_data = g_malloc (length - 3); aligned_data = g_malloc (length - 3);
memcpy (aligned_data, data + 3, length - 3); memcpy (aligned_data, data + 3, length - 3);
raw_value = g_variant_new_from_data (FP_PRINT_VARIANT_TYPE, raw_value = g_variant_new_from_data (FPI_PRINT_VARIANT_TYPE,
aligned_data, length - 3, aligned_data, length - 3,
FALSE, g_free, aligned_data); FALSE, g_free, aligned_data);
@@ -1035,20 +809,20 @@ fp_print_deserialize (const guchar *data,
finger = finger_int8; finger = finger_int8;
/* Assume data is valid at this point if the values are somewhat sane. */ /* Assume data is valid at this point if the values are somewhat sane. */
if (type == FP_PRINT_NBIS) if (type == FPI_PRINT_NBIS)
{ {
g_autoptr(GVariant) prints = g_variant_get_child_value (print_data, 0); g_autoptr(GVariant) prints = g_variant_get_child_value (print_data, 0);
gint i; guint i;
result = g_object_new (FP_TYPE_PRINT, result = g_object_new (FP_TYPE_PRINT,
"driver", driver, "driver", driver,
"device-id", device_id, "device-id", device_id,
"device-stored", device_stored, "device-stored", device_stored,
NULL); NULL);
fpi_print_set_type (result, FP_PRINT_NBIS); fpi_print_set_type (result, FPI_PRINT_NBIS);
for (i = 0; i < g_variant_n_children (prints); i++) for (i = 0; i < g_variant_n_children (prints); i++)
{ {
g_autofree struct xyt_struct *xyt = g_new0 (struct xyt_struct, 1); g_autofree struct xyt_struct *xyt = NULL;
const gint32 *xcol, *ycol, *thetacol; const gint32 *xcol, *ycol, *thetacol;
gsize xlen, ylen, thetalen; gsize xlen, ylen, thetalen;
g_autoptr(GVariant) xyt_data = NULL; g_autoptr(GVariant) xyt_data = NULL;
@@ -1074,6 +848,7 @@ fp_print_deserialize (const guchar *data,
if (xlen > G_N_ELEMENTS (xyt->xcol)) if (xlen > G_N_ELEMENTS (xyt->xcol))
goto invalid_format; goto invalid_format;
xyt = g_new0 (struct xyt_struct, 1);
xyt->nrows = xlen; xyt->nrows = xlen;
memcpy (xyt->xcol, xcol, sizeof (xcol[0]) * xlen); memcpy (xyt->xcol, xcol, sizeof (xcol[0]) * xlen);
memcpy (xyt->ycol, ycol, sizeof (xcol[0]) * xlen); memcpy (xyt->ycol, ycol, sizeof (xcol[0]) * xlen);
@@ -1082,16 +857,16 @@ fp_print_deserialize (const guchar *data,
g_ptr_array_add (result->prints, g_steal_pointer (&xyt)); g_ptr_array_add (result->prints, g_steal_pointer (&xyt));
} }
} }
else if (type == FP_PRINT_RAW) else if (type == FPI_PRINT_RAW)
{ {
g_autoptr(GVariant) fp_data = g_variant_get_child_value (print_data, 0); g_autoptr(GVariant) fp_data = g_variant_get_child_value (print_data, 0);
result = g_object_new (FP_TYPE_PRINT, result = g_object_new (FP_TYPE_PRINT,
"fp-type", type, "fpi-type", type,
"driver", driver, "driver", driver,
"device-id", device_id, "device-id", device_id,
"device-stored", device_stored, "device-stored", device_stored,
"fp-data", fp_data, "fpi-data", fp_data,
NULL); NULL);
} }
else else
@@ -1108,8 +883,6 @@ fp_print_deserialize (const guchar *data,
"enroll_date", date, "enroll_date", date,
NULL); NULL);
g_date_free (date);
return g_steal_pointer (&result); return g_steal_pointer (&result);
invalid_format: invalid_format:

View File

@@ -28,6 +28,9 @@ G_BEGIN_DECLS
#define FP_TYPE_PRINT (fp_print_get_type ()) #define FP_TYPE_PRINT (fp_print_get_type ())
G_DECLARE_FINAL_TYPE (FpPrint, fp_print, FP, PRINT, GInitiallyUnowned) G_DECLARE_FINAL_TYPE (FpPrint, fp_print, FP, PRINT, GInitiallyUnowned)
#define FP_FINGER_IS_VALID(finger) \
((finger) >= FP_FINGER_FIRST && (finger) <= FP_FINGER_LAST)
#include "fp-device.h" #include "fp-device.h"
/** /**
@@ -43,6 +46,8 @@ G_DECLARE_FINAL_TYPE (FpPrint, fp_print, FP, PRINT, GInitiallyUnowned)
* @FP_FINGER_RIGHT_MIDDLE: Right middle finger * @FP_FINGER_RIGHT_MIDDLE: Right middle finger
* @FP_FINGER_RIGHT_RING: Right ring finger * @FP_FINGER_RIGHT_RING: Right ring finger
* @FP_FINGER_RIGHT_LITTLE: Right little finger * @FP_FINGER_RIGHT_LITTLE: Right little finger
* @FP_FINGER_FIRST: The first finger in the fp-print order
* @FP_FINGER_LAST: The last finger in the fp-print order
*/ */
typedef enum { typedef enum {
FP_FINGER_UNKNOWN = 0, FP_FINGER_UNKNOWN = 0,
@@ -56,8 +61,23 @@ typedef enum {
FP_FINGER_RIGHT_MIDDLE, FP_FINGER_RIGHT_MIDDLE,
FP_FINGER_RIGHT_RING, FP_FINGER_RIGHT_RING,
FP_FINGER_RIGHT_LITTLE, FP_FINGER_RIGHT_LITTLE,
FP_FINGER_FIRST = FP_FINGER_LEFT_THUMB,
FP_FINGER_LAST = FP_FINGER_RIGHT_LITTLE,
} FpFinger; } FpFinger;
/**
* FpFingerStatusFlags:
* @FP_FINGER_STATUS_NONE: Sensor has not the finger on it, nor requires it
* @FP_FINGER_STATUS_NEEDED: Sensor waits for the finger
* @FP_FINGER_STATUS_PRESENT: Sensor has the finger on it
*/
typedef enum {
FP_FINGER_STATUS_NONE = 0,
FP_FINGER_STATUS_NEEDED = 1 << 0,
FP_FINGER_STATUS_PRESENT = 1 << 1,
} FpFingerStatusFlags;
FpPrint *fp_print_new (FpDevice *device); FpPrint *fp_print_new (FpDevice *device);
FpPrint *fp_print_new_from_data (guchar *data, FpPrint *fp_print_new_from_data (guchar *data,

View File

@@ -21,7 +21,8 @@
#define FP_COMPONENT "assembling" #define FP_COMPONENT "assembling"
#include "fp_internal.h" #include "fpi-log.h"
#include "fpi-image.h"
#include <string.h> #include <string.h>
@@ -51,6 +52,9 @@ calc_error (struct fpi_frame_asmbl_ctx *ctx,
width = ctx->frame_width - (dx > 0 ? dx : -dx); width = ctx->frame_width - (dx > 0 ? dx : -dx);
height = ctx->frame_height - dy; height = ctx->frame_height - dy;
if (height == 0 || width == 0)
return INT_MAX;
y1 = 0; y1 = 0;
y2 = dy; y2 = dy;
i = 0; i = 0;
@@ -85,9 +89,6 @@ calc_error (struct fpi_frame_asmbl_ctx *ctx,
err *= (ctx->frame_height * ctx->frame_width); err *= (ctx->frame_height * ctx->frame_width);
err /= (height * width); err /= (height * width);
if (err == 0)
return INT_MAX;
return err; return err;
} }
@@ -98,6 +99,8 @@ static void
find_overlap (struct fpi_frame_asmbl_ctx *ctx, find_overlap (struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *first_frame, struct fpi_frame *first_frame,
struct fpi_frame *second_frame, struct fpi_frame *second_frame,
int *dx_out,
int *dy_out,
unsigned int *min_error) unsigned int *min_error)
{ {
int dx, dy; int dx, dy;
@@ -119,8 +122,8 @@ find_overlap (struct fpi_frame_asmbl_ctx *ctx,
if (err < *min_error) if (err < *min_error)
{ {
*min_error = err; *min_error = err;
second_frame->delta_x = -dx; *dx_out = -dx;
second_frame->delta_y = dy; *dy_out = dy;
} }
} }
} }
@@ -132,7 +135,7 @@ do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
{ {
GSList *l; GSList *l;
GTimer *timer; GTimer *timer;
guint num_frames = 0; guint num_frames = 1;
struct fpi_frame *prev_stripe; struct fpi_frame *prev_stripe;
unsigned int min_error; unsigned int min_error;
/* Max error is width * height * 255, for AES2501 which has the largest /* Max error is width * height * 255, for AES2501 which has the largest
@@ -142,20 +145,27 @@ do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
unsigned long long total_error = 0; unsigned long long total_error = 0;
timer = g_timer_new (); timer = g_timer_new ();
/* Skip the first frame */
prev_stripe = stripes->data; prev_stripe = stripes->data;
for (l = stripes; l != NULL; l = l->next, num_frames++)
for (l = stripes->next; l != NULL; l = l->next, num_frames++)
{ {
struct fpi_frame *cur_stripe = l->data; struct fpi_frame *cur_stripe = l->data;
if (reverse) if (reverse)
{ {
find_overlap (ctx, prev_stripe, cur_stripe, &min_error); find_overlap (ctx, prev_stripe, cur_stripe,
&cur_stripe->delta_x, &cur_stripe->delta_y,
&min_error);
cur_stripe->delta_y = -cur_stripe->delta_y; cur_stripe->delta_y = -cur_stripe->delta_y;
cur_stripe->delta_x = -cur_stripe->delta_x; cur_stripe->delta_x = -cur_stripe->delta_x;
} }
else else
{ {
find_overlap (ctx, cur_stripe, prev_stripe, &min_error); find_overlap (ctx, cur_stripe, prev_stripe,
&cur_stripe->delta_x, &cur_stripe->delta_y,
&min_error);
} }
total_error += min_error; total_error += min_error;
@@ -327,19 +337,10 @@ fpi_assemble_frames (struct fpi_frame_asmbl_ctx *ctx,
{ {
fpi_frame = l->data; fpi_frame = l->data;
if(reverse)
{
y += fpi_frame->delta_y; y += fpi_frame->delta_y;
x += fpi_frame->delta_x; x += fpi_frame->delta_x;
}
aes_blit_stripe (ctx, img, fpi_frame, x, y); aes_blit_stripe (ctx, img, fpi_frame, x, y);
if(!reverse)
{
y += fpi_frame->delta_y;
x += fpi_frame->delta_x;
}
} }
return img; return img;

View File

@@ -17,8 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __FPI_ASSEMBLING_H__ #pragma once
#define __FPI_ASSEMBLING_H__
#include <fprint.h> #include <fprint.h>
@@ -116,5 +115,3 @@ struct fpi_line_asmbl_ctx
FpImage *fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx, FpImage *fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx,
GSList *lines, GSList *lines,
size_t num_lines); size_t num_lines);
#endif

View File

@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#ifndef __FPI_BYTE_READER_H__ #pragma once
#define __FPI_BYTE_READER_H__
#include <glib.h> #include <glib.h>
#include "fpi-byte-utils.h" #include "fpi-byte-utils.h"
@@ -676,5 +675,3 @@ fpi_byte_reader_skip_inline (FpiByteReader * reader, guint nbytes)
#endif /* FPI_BYTE_READER_DISABLE_INLINES */ #endif /* FPI_BYTE_READER_DISABLE_INLINES */
G_END_DECLS G_END_DECLS
#endif /* __FPI_BYTE_READER_H__ */

View File

@@ -21,9 +21,7 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#pragma once
#ifndef __FP_UTILS_H__
#define __FP_UTILS_H__
#include <glib.h> #include <glib.h>
@@ -485,4 +483,3 @@ FP_WRITE_DOUBLE_BE(guint8 *data, gdouble num)
G_END_DECLS G_END_DECLS
#endif /* __GTK_DOC_IGNORE__ */ #endif /* __GTK_DOC_IGNORE__ */
#endif /* __FP_UTILS_H__ */

View File

@@ -18,8 +18,7 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#ifndef __FPI_BYTE_WRITER_H__ #pragma once
#define __FPI_BYTE_WRITER_H__
#include "fpi-byte-reader.h" #include "fpi-byte-reader.h"
#include <string.h> #include <string.h>
@@ -409,5 +408,3 @@ fpi_byte_writer_fill_inline (FpiByteWriter * writer, guint8 value, guint size)
#endif #endif
G_END_DECLS G_END_DECLS
#endif /* __FPI_BYTE_WRITER_H__ */

45
libfprint/fpi-compat.h Normal file
View File

@@ -0,0 +1,45 @@
/*
* 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
* 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 <glib-object.h>
#if !GLIB_CHECK_VERSION (2, 57, 0)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GTypeClass, g_type_class_unref);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GEnumClass, g_type_class_unref);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GParamSpec, g_param_spec_unref);
#else
/* Re-define G_SOURCE_FUNC as we are technically not allowed to use it with
* the version we depend on currently. */
#undef G_SOURCE_FUNC
#endif
#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void))(f))
#if !GLIB_CHECK_VERSION (2, 63, 3)
typedef struct _FpDeviceClass FpDeviceClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpDeviceClass, g_type_class_unref);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GDate, g_date_free);
#endif
#if __GNUC__ > 10 || (__GNUC__ == 10 && __GNUC_MINOR__ >= 1)
#define FP_GNUC_ACCESS(m, p, s) __attribute__((access (m, p, s)))
#else
#define FP_GNUC_ACCESS(m, p, s)
#endif

View File

@@ -20,14 +20,16 @@
#include <gusb.h> #include <gusb.h>
#include "fp-context.h" #include "fp-context.h"
#include "fpi-compat.h"
/** /**
* fpi_get_driver_types: * fpi_get_driver_types:
* @drivers: #GArray to be filled with all driver types
* *
* This function is purely for private used. It is solely part of the public * This function is purely for private used. It is solely part of the public
* API as it is useful during build time. * API as it is useful during build time.
* *
* Stability: private * Stability: private
* Returns: (element-type GType) (transfer container): a #GArray filled with
* all driver types
*/ */
void fpi_get_driver_types (GArray *drivers); GArray *fpi_get_driver_types (void);

1553
libfprint/fpi-device.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -65,10 +65,10 @@ struct _FpIdEntry
* @probe: Called immediately for all devices. Most drivers will not need to * @probe: Called immediately for all devices. Most drivers will not need to
* implement this. Drivers should setup the device identifier from the probe * implement this. Drivers should setup the device identifier from the probe
* callback which will be used to verify the compatibility of stored * callback which will be used to verify the compatibility of stored
* #FpPrint's. It is permissable to temporarily open the USB device if this * #FpPrint's. It is permissible to temporarily open the USB device if this
* is required for the operation. If an error is returned, then the device * is required for the operation. If an error is returned, then the device
* will be destroyed again immediately and never reported to the API user. * will be destroyed again immediately and never reported to the API user.
* @open: Open the device for futher operations. Any of the normal actions are * @open: Open the device for further operations. Any of the normal actions are
* guaranteed to only happen when the device is open (this includes delete). * guaranteed to only happen when the device is open (this includes delete).
* @close: Close the device again * @close: Close the device again
* @enroll: Start an enroll operation * @enroll: Start an enroll operation
@@ -142,39 +142,39 @@ typedef void (*FpTimeoutFunc) (FpDevice *device,
gpointer user_data); gpointer user_data);
/** /**
* FpDeviceAction: * FpiDeviceAction:
* @FP_DEVICE_ACTION_NONE: No action is active. * @FPI_DEVICE_ACTION_NONE: No action is active.
* @FP_DEVICE_ACTION_PROBE: Probe device for support and information. * @FPI_DEVICE_ACTION_PROBE: Probe device for support and information.
* @FP_DEVICE_ACTION_OPEN: Device is currently being opened. * @FPI_DEVICE_ACTION_OPEN: Device is currently being opened.
* @FP_DEVICE_ACTION_CLOSE: Device is currently being closed. * @FPI_DEVICE_ACTION_CLOSE: Device is currently being closed.
* @FP_DEVICE_ACTION_ENROLL: Device is currently enrolling. * @FPI_DEVICE_ACTION_ENROLL: Device is currently enrolling.
* @FP_DEVICE_ACTION_VERIFY: Device is currently verifying. * @FPI_DEVICE_ACTION_VERIFY: Device is currently verifying.
* @FP_DEVICE_ACTION_IDENTIFY: Device is currently identifying. * @FPI_DEVICE_ACTION_IDENTIFY: Device is currently identifying.
* @FP_DEVICE_ACTION_CAPTURE: Device is currently capturing an image. * @FPI_DEVICE_ACTION_CAPTURE: Device is currently capturing an image.
* @FP_DEVICE_ACTION_LIST: Device stored prints are being queried. * @FPI_DEVICE_ACTION_LIST: Device stored prints are being queried.
* @FP_DEVICE_ACTION_DELETE: Device stored print is being deleted. * @FPI_DEVICE_ACTION_DELETE: Device stored print is being deleted.
* *
* Current active action of the device. A driver can retrieve the action. * Current active action of the device. A driver can retrieve the action.
*/ */
typedef enum { typedef enum {
FP_DEVICE_ACTION_NONE = 0, FPI_DEVICE_ACTION_NONE = 0,
FP_DEVICE_ACTION_PROBE, FPI_DEVICE_ACTION_PROBE,
FP_DEVICE_ACTION_OPEN, FPI_DEVICE_ACTION_OPEN,
FP_DEVICE_ACTION_CLOSE, FPI_DEVICE_ACTION_CLOSE,
FP_DEVICE_ACTION_ENROLL, FPI_DEVICE_ACTION_ENROLL,
FP_DEVICE_ACTION_VERIFY, FPI_DEVICE_ACTION_VERIFY,
FP_DEVICE_ACTION_IDENTIFY, FPI_DEVICE_ACTION_IDENTIFY,
FP_DEVICE_ACTION_CAPTURE, FPI_DEVICE_ACTION_CAPTURE,
FP_DEVICE_ACTION_LIST, FPI_DEVICE_ACTION_LIST,
FP_DEVICE_ACTION_DELETE, FPI_DEVICE_ACTION_DELETE,
} FpDeviceAction; } FpiDeviceAction;
GUsbDevice *fpi_device_get_usb_device (FpDevice *device); GUsbDevice *fpi_device_get_usb_device (FpDevice *device);
const gchar *fpi_device_get_virtual_env (FpDevice *device); const gchar *fpi_device_get_virtual_env (FpDevice *device);
//const gchar *fpi_device_get_spi_dev (FpDevice *device); //const gchar *fpi_device_get_spi_dev (FpDevice *device);
FpDeviceAction fpi_device_get_current_action (FpDevice *device); FpiDeviceAction fpi_device_get_current_action (FpDevice *device);
gboolean fpi_device_action_is_cancelled (FpDevice *device); gboolean fpi_device_action_is_cancelled (FpDevice *device);
GError * fpi_device_retry_new (FpDeviceRetry error); GError * fpi_device_retry_new (FpDeviceRetry error);
@@ -202,6 +202,7 @@ void fpi_device_get_delete_data (FpDevice *device,
FpPrint **print); FpPrint **print);
GCancellable *fpi_device_get_cancellable (FpDevice *device); GCancellable *fpi_device_get_cancellable (FpDevice *device);
void fpi_device_remove (FpDevice *device);
GSource * fpi_device_add_timeout (FpDevice *device, GSource * fpi_device_add_timeout (FpDevice *device,
gint interval, gint interval,
@@ -230,12 +231,8 @@ void fpi_device_enroll_complete (FpDevice *device,
FpPrint *print, FpPrint *print,
GError *error); GError *error);
void fpi_device_verify_complete (FpDevice *device, void fpi_device_verify_complete (FpDevice *device,
FpiMatchResult result,
FpPrint *print,
GError *error); GError *error);
void fpi_device_identify_complete (FpDevice *device, void fpi_device_identify_complete (FpDevice *device,
FpPrint *match,
FpPrint *print,
GError *error); GError *error);
void fpi_device_capture_complete (FpDevice *device, void fpi_device_capture_complete (FpDevice *device,
FpImage *image, FpImage *image,
@@ -250,5 +247,19 @@ void fpi_device_enroll_progress (FpDevice *device,
gint completed_stages, gint completed_stages,
FpPrint *print, FpPrint *print,
GError *error); GError *error);
void fpi_device_verify_report (FpDevice *device,
FpiMatchResult result,
FpPrint *print,
GError *error);
void fpi_device_identify_report (FpDevice *device,
FpPrint *match,
FpPrint *print,
GError *error);
gboolean fpi_device_report_finger_status (FpDevice *device,
FpFingerStatusFlags finger_status);
gboolean fpi_device_report_finger_status_changes (FpDevice *device,
FpFingerStatusFlags added_status,
FpFingerStatusFlags removed_status);
G_END_DECLS G_END_DECLS

View File

@@ -0,0 +1,742 @@
/*
* FpImageDevice - An image based fingerprint reader device - Private APIs
* Copyright (C) 2019 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
* 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 "image_device"
#include "fpi-log.h"
#include "fp-image-device-private.h"
#include "fp-image-device.h"
/**
* SECTION: fpi-image-device
* @title: Internal FpImageDevice
* @short_description: Internal image device functions
*
* Internal image device functions. See #FpImageDevice for public routines.
*/
/* Manually redefine what G_DEFINE_* macro does */
static inline gpointer
fp_image_device_get_instance_private (FpImageDevice *self)
{
FpImageDeviceClass *img_class = g_type_class_peek_static (FP_TYPE_IMAGE_DEVICE);
return G_STRUCT_MEMBER_P (self,
g_type_class_get_instance_private_offset (img_class));
}
static void fp_image_device_change_state (FpImageDevice *self,
FpiImageDeviceState state);
/* Private shared functions */
void
fpi_image_device_activate (FpImageDevice *self)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
g_assert (!priv->active);
fp_dbg ("Activating image device");
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_ACTIVATING);
cls->activate (self);
}
void
fpi_image_device_deactivate (FpImageDevice *self, gboolean cancelling)
{
FpDevice *device = FP_DEVICE (self);
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (device);
if (!priv->active || priv->state == FPI_IMAGE_DEVICE_STATE_DEACTIVATING)
{
/* XXX: We currently deactivate both from minutiae scan result
* and finger off report. */
fp_dbg ("Already deactivated, ignoring request.");
return;
}
if (!cancelling && priv->state != FPI_IMAGE_DEVICE_STATE_IDLE)
g_warning ("Deactivating image device while it is not idle, this should not happen.");
fp_dbg ("Deactivating image device");
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_DEACTIVATING);
cls->deactivate (self);
}
/* Static helper functions */
/* This should not be called directly to activate/deactivate the device! */
static void
fp_image_device_change_state (FpImageDevice *self, FpiImageDeviceState state)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
g_autofree char *prev_state_str = NULL;
g_autofree char *state_str = NULL;
gboolean transition_is_valid = FALSE;
gint i;
struct
{
FpiImageDeviceState from;
FpiImageDeviceState to;
} valid_transitions[] = {
{ FPI_IMAGE_DEVICE_STATE_INACTIVE, FPI_IMAGE_DEVICE_STATE_ACTIVATING },
{ FPI_IMAGE_DEVICE_STATE_ACTIVATING, FPI_IMAGE_DEVICE_STATE_IDLE },
{ FPI_IMAGE_DEVICE_STATE_ACTIVATING, FPI_IMAGE_DEVICE_STATE_INACTIVE },
{ FPI_IMAGE_DEVICE_STATE_IDLE, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON },
{ FPI_IMAGE_DEVICE_STATE_IDLE, FPI_IMAGE_DEVICE_STATE_CAPTURE }, /* raw mode -- currently not supported */
{ FPI_IMAGE_DEVICE_STATE_IDLE, FPI_IMAGE_DEVICE_STATE_DEACTIVATING },
{ FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON, FPI_IMAGE_DEVICE_STATE_CAPTURE },
{ FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON, FPI_IMAGE_DEVICE_STATE_DEACTIVATING }, /* cancellation */
{ FPI_IMAGE_DEVICE_STATE_CAPTURE, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF },
{ FPI_IMAGE_DEVICE_STATE_CAPTURE, FPI_IMAGE_DEVICE_STATE_IDLE }, /* raw mode -- currently not supported */
{ FPI_IMAGE_DEVICE_STATE_CAPTURE, FPI_IMAGE_DEVICE_STATE_DEACTIVATING }, /* cancellation */
{ FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF, FPI_IMAGE_DEVICE_STATE_IDLE },
{ FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF, FPI_IMAGE_DEVICE_STATE_DEACTIVATING }, /* cancellation */
{ FPI_IMAGE_DEVICE_STATE_DEACTIVATING, FPI_IMAGE_DEVICE_STATE_INACTIVE },
};
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++)
{
if (valid_transitions[i].from == priv->state && valid_transitions[i].to == state)
{
transition_is_valid = TRUE;
break;
}
}
if (!transition_is_valid)
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");
g_signal_emit_by_name (self, "fpi-image-device-state-changed", priv->state);
if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
{
fpi_device_report_finger_status_changes (FP_DEVICE (self),
FP_FINGER_STATUS_NEEDED,
FP_FINGER_STATUS_NONE);
}
else if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
{
fpi_device_report_finger_status_changes (FP_DEVICE (self),
FP_FINGER_STATUS_NONE,
FP_FINGER_STATUS_NEEDED);
}
}
static void
fp_image_device_enroll_maybe_await_finger_on (FpImageDevice *self)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
/* We wait for both the minutiae scan to complete and the finger to
* be removed before we switch to AWAIT_FINGER_ON. */
if (priv->minutiae_scan_active || priv->finger_present)
return;
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON);
}
static void
fp_image_device_maybe_complete_action (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpDevice *device = FP_DEVICE (self);
FpiDeviceAction action;
if (error)
{
/* Keep the first error we encountered, but not if it is of type retry */
if (priv->action_error && !(priv->action_error->domain == FP_DEVICE_RETRY))
{
g_warning ("Will complete with first error, new error was: %s", error->message);
g_clear_error (&error);
}
else
{
g_clear_error (&priv->action_error);
priv->action_error = error;
}
}
/* Do not complete if the device is still active or a minutiae scan is pending. */
if (priv->active || priv->minutiae_scan_active)
return;
if (!priv->action_error)
g_cancellable_set_error_if_cancelled (fpi_device_get_cancellable (device), &priv->action_error);
if (priv->action_error)
{
fpi_device_action_error (device, g_steal_pointer (&priv->action_error));
g_clear_object (&priv->capture_image);
return;
}
/* We are done, report the result. */
action = fpi_device_get_current_action (FP_DEVICE (self));
if (action == FPI_DEVICE_ACTION_ENROLL)
{
FpPrint *enroll_print;
fpi_device_get_enroll_data (device, &enroll_print);
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);
}
else if (action == FPI_DEVICE_ACTION_CAPTURE)
{
fpi_device_capture_complete (device, g_steal_pointer (&priv->capture_image), NULL);
}
else
{
g_assert_not_reached ();
}
}
static void
fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FpImage) image = FP_IMAGE (source_object);
g_autoptr(FpPrint) print = NULL;
GError *error = NULL;
FpImageDevice *self = FP_IMAGE_DEVICE (user_data);
FpDevice *device = FP_DEVICE (self);
FpImageDevicePrivate *priv;
FpiDeviceAction action;
/* Note: We rely on the device to not disappear during an operation. */
priv = fp_image_device_get_instance_private (FP_IMAGE_DEVICE (device));
priv->minutiae_scan_active = FALSE;
if (!fp_image_detect_minutiae_finish (image, res, &error))
{
/* Cancel operation . */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
fp_image_device_maybe_complete_action (self, g_steal_pointer (&error));
fpi_image_device_deactivate (self, TRUE);
return;
}
/* Replace error with a retry condition. */
g_warning ("Failed to detect minutiae: %s", error->message);
g_clear_pointer (&error, g_error_free);
error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_GENERAL, "Minutiae detection failed, please retry");
}
action = fpi_device_get_current_action (device);
if (action == FPI_DEVICE_ACTION_CAPTURE)
{
priv->capture_image = g_steal_pointer (&image);
fp_image_device_maybe_complete_action (self, g_steal_pointer (&error));
return;
}
if (!error)
{
print = fp_print_new (device);
fpi_print_set_type (print, FPI_PRINT_NBIS);
if (!fpi_print_add_from_image (print, image, &error))
{
g_clear_object (&print);
if (error->domain != FP_DEVICE_RETRY)
{
fp_image_device_maybe_complete_action (self, g_steal_pointer (&error));
/* We might not yet be deactivating, if we are enrolling. */
fpi_image_device_deactivate (self, TRUE);
return;
}
}
}
if (action == FPI_DEVICE_ACTION_ENROLL)
{
FpPrint *enroll_print;
fpi_device_get_enroll_data (device, &enroll_print);
if (print)
{
fpi_print_add_print (enroll_print, print);
priv->enroll_stage += 1;
}
fpi_device_enroll_progress (device, priv->enroll_stage,
g_steal_pointer (&print), error);
/* Start another scan or deactivate. */
if (priv->enroll_stage == IMG_ENROLL_STAGES)
{
fp_image_device_maybe_complete_action (self, g_steal_pointer (&error));
fpi_image_device_deactivate (self, FALSE);
}
else
{
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;
GPtrArray *templates;
FpPrint *result = NULL;
fpi_device_get_identify_data (device, &templates);
for (i = 0; !error && i < templates->len; i++)
{
FpPrint *template = g_ptr_array_index (templates, i);
if (fpi_print_bz3_match (template, print, priv->bz3_threshold, &error) == FPI_MATCH_SUCCESS)
{
result = template;
break;
}
}
if (!error || error->domain == FP_DEVICE_RETRY)
fpi_device_identify_report (device, result, g_steal_pointer (&print), g_steal_pointer (&error));
fp_image_device_maybe_complete_action (self, g_steal_pointer (&error));
}
else
{
/* XXX: This can be hit currently due to a race condition in the enroll code!
* In that case we scan a further image even though the minutiae for the previous
* one have not yet been detected.
* We need to keep track on the pending minutiae detection and the fact that
* it will finish eventually (or we may need to retry on error and activate the
* device again). */
g_assert_not_reached ();
}
}
/*********************************************************/
/* Private API */
/**
* fpi_image_device_set_bz3_threshold:
* @self: a #FpImageDevice imaging fingerprint device
* @bz3_threshold: BZ3 threshold to use
*
* Dynamically adjust the bz3 threshold. This is only needed for drivers
* that support devices with different properties. It should generally be
* called from the probe callback, but is acceptable to call from the open
* callback.
*/
void
fpi_image_device_set_bz3_threshold (FpImageDevice *self,
gint bz3_threshold)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
g_return_if_fail (FP_IS_IMAGE_DEVICE (self));
g_return_if_fail (bz3_threshold > 0);
priv->bz3_threshold = bz3_threshold;
}
/**
* fpi_image_device_report_finger_status:
* @self: a #FpImageDevice imaging fingerprint device
* @present: whether the finger is present on the sensor
*
* Reports from the driver whether the user's finger is on
* the sensor.
*/
void
fpi_image_device_report_finger_status (FpImageDevice *self,
gboolean present)
{
FpDevice *device = FP_DEVICE (self);
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpiDeviceAction action;
if (present)
{
fpi_device_report_finger_status_changes (device,
FP_FINGER_STATUS_PRESENT,
FP_FINGER_STATUS_NONE);
}
else
{
fpi_device_report_finger_status_changes (device,
FP_FINGER_STATUS_NONE,
FP_FINGER_STATUS_PRESENT);
}
if (priv->state == FPI_IMAGE_DEVICE_STATE_INACTIVE)
{
/* Do we really want to always ignore such reports? We could
* also track the state in case the user had the finger on
* the device at initialisation time and the driver reports
* this early.
*/
g_debug ("Ignoring finger presence report as the device is not active!");
return;
}
action = fpi_device_get_current_action (device);
g_assert (action != FPI_DEVICE_ACTION_OPEN);
g_assert (action != FPI_DEVICE_ACTION_CLOSE);
g_debug ("Image device reported finger status: %s", present ? "on" : "off");
priv->finger_present = present;
if (present && priv->state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
{
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_CAPTURE);
}
else if (!present && priv->state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
{
/* If we are in the non-enroll case, we always deactivate.
*
* In the enroll case, the decision can only be made after minutiae
* detection has finished.
*/
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_IDLE);
if (action != FPI_DEVICE_ACTION_ENROLL)
fpi_image_device_deactivate (self, FALSE);
else
fp_image_device_enroll_maybe_await_finger_on (self);
}
}
/**
* fpi_image_device_image_captured:
* @self: a #FpImageDevice imaging fingerprint device
* @image: whether the finger is present on the sensor
*
* Reports an image capture. Only use this function if the image was
* captured successfully. If there was an issue where the user should
* retry, use fpi_image_device_retry_scan() to report the retry condition.
*
* In the event of a fatal error for the operation use
* fpi_image_device_session_error(). This will abort the entire operation
* including e.g. an enroll operation which captures multiple images during
* one session.
*/
void
fpi_image_device_image_captured (FpImageDevice *self, FpImage *image)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
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);
g_debug ("Image device captured an image");
priv->minutiae_scan_active = TRUE;
/* XXX: We also detect minutiae in capture mode, we solely do this
* to normalize the image which will happen as a by-product. */
fp_image_detect_minutiae (image,
fpi_device_get_cancellable (FP_DEVICE (self)),
fpi_image_device_minutiae_detected,
self);
/* XXX: This is wrong if we add support for raw capture mode. */
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF);
}
/**
* fpi_image_device_retry_scan:
* @self: a #FpImageDevice imaging fingerprint device
* @retry: The #FpDeviceRetry error code to report
*
* Reports a scan failure to the user. This may or may not abort the
* current session. It is the equivalent of fpi_image_device_image_captured()
* in the case of a retryable error condition (e.g. short swipe).
*/
void
fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpiDeviceAction action;
GError *error;
action = fpi_device_get_current_action (FP_DEVICE (self));
/* We might be waiting for a finger at this point, so just accept
* 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);
error = fpi_device_retry_new (retry);
if (action == FPI_DEVICE_ACTION_ENROLL)
{
g_debug ("Reporting retry during enroll");
fpi_device_enroll_progress (FP_DEVICE (self), priv->enroll_stage, NULL, error);
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);
fp_image_device_maybe_complete_action (self, NULL);
fpi_image_device_deactivate (self, TRUE);
}
else
{
/* The capture case where there is no early reporting. */
g_debug ("Abort current operation due to retry (no early-reporting)");
fp_image_device_maybe_complete_action (self, error);
fpi_image_device_deactivate (self, TRUE);
}
}
/**
* fpi_image_device_session_error:
* @self: a #FpImageDevice imaging fingerprint device
* @error: The #GError to report
*
* Report an error while interacting with the device. This effectively
* aborts the current ongoing action. Note that doing so will result in
* the deactivation handler to be called and this function must not be
* used to report an error during deactivation.
*/
void
fpi_image_device_session_error (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
g_return_if_fail (self);
if (!error)
{
g_warning ("Driver did not provide an error, generating a generic one");
error = g_error_new (FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL, "Driver reported session error without an error");
}
if (!priv->active)
{
FpiDeviceAction action = fpi_device_get_current_action (FP_DEVICE (self));
g_warning ("Driver reported session error, but device is inactive.");
if (action != FPI_DEVICE_ACTION_NONE)
{
g_warning ("Translating to activation failure!");
fpi_image_device_activate_complete (self, error);
return;
}
}
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
fpi_device_action_is_cancelled (FP_DEVICE (self)))
{
/* Ignore cancellation errors here, as we will explicitly deactivate
* anyway (or, may already have done so at this point).
*/
g_debug ("Driver reported a cancellation error, this is expected but not required. Ignoring.");
g_clear_error (&error);
return;
}
else if (priv->state == FPI_IMAGE_DEVICE_STATE_INACTIVE)
{
g_warning ("Driver reported session error while deactivating already, ignoring. This indicates a driver bug.");
g_clear_error (&error);
return;
}
if (error->domain == FP_DEVICE_RETRY)
g_warning ("Driver should report retries using fpi_image_device_retry_scan!");
fp_image_device_maybe_complete_action (self, error);
fpi_image_device_deactivate (self, TRUE);
}
/**
* fpi_image_device_activate_complete:
* @self: a #FpImageDevice imaging fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of device activation.
*/
void
fpi_image_device_activate_complete (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
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);
if (error)
{
g_debug ("Image device activation failed");
fpi_device_action_error (FP_DEVICE (self), error);
return;
}
g_debug ("Image device activation completed");
priv->active = TRUE;
/* We always want to capture at this point, move to AWAIT_FINGER
* state. */
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_IDLE);
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON);
}
/**
* fpi_image_device_deactivate_complete:
* @self: a #FpImageDevice imaging fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of device deactivation.
*/
void
fpi_image_device_deactivate_complete (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
g_return_if_fail (priv->active == TRUE);
g_return_if_fail (priv->state == FPI_IMAGE_DEVICE_STATE_DEACTIVATING);
g_debug ("Image device deactivation completed");
priv->active = FALSE;
/* Assume finger was removed. */
priv->finger_present = FALSE;
fp_image_device_change_state (self, FPI_IMAGE_DEVICE_STATE_INACTIVE);
fp_image_device_maybe_complete_action (self, error);
}
/**
* fpi_image_device_open_complete:
* @self: a #FpImageDevice imaging fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of open operation.
*/
void
fpi_image_device_open_complete (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (priv->active == FALSE);
g_return_if_fail (action == FPI_DEVICE_ACTION_OPEN);
g_debug ("Image device open completed");
priv->state = FPI_IMAGE_DEVICE_STATE_INACTIVE;
g_object_notify (G_OBJECT (self), "fpi-image-device-state");
fpi_device_report_finger_status (FP_DEVICE (self), FP_FINGER_STATUS_NONE);
fpi_device_open_complete (FP_DEVICE (self), error);
}
/**
* fpi_image_device_close_complete:
* @self: a #FpImageDevice imaging fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of close operation.
*/
void
fpi_image_device_close_complete (FpImageDevice *self, GError *error)
{
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_debug ("Image device close completed");
g_return_if_fail (priv->active == FALSE);
g_return_if_fail (action == FPI_DEVICE_ACTION_CLOSE);
priv->state = FPI_IMAGE_DEVICE_STATE_INACTIVE;
g_object_notify (G_OBJECT (self), "fpi-image-device-state");
fpi_device_close_complete (FP_DEVICE (self), error);
}

View File

@@ -23,11 +23,14 @@
#include "fp-image-device.h" #include "fp-image-device.h"
/** /**
* FpImageDeviceState: * FpiImageDeviceState:
* @FP_IMAGE_DEVICE_STATE_INACTIVE: inactive * @FPI_IMAGE_DEVICE_STATE_INACTIVE: inactive
* @FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped * @FPI_IMAGE_DEVICE_STATE_ACTIVATING: State during activate callback
* @FP_IMAGE_DEVICE_STATE_CAPTURE: capturing an image * @FPI_IMAGE_DEVICE_STATE_IDLE: Activated but idle
* @FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed * @FPI_IMAGE_DEVICE_STATE_DEACTIVATING: State during deactivate callback
* @FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped
* @FPI_IMAGE_DEVICE_STATE_CAPTURE: capturing an image
* @FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed
* *
* The state of an imaging device while doing a capture. The state is * The state of an imaging device while doing a capture. The state is
* passed through to the driver using the ::activate() or ::change_state() vfuncs. * passed through to the driver using the ::activate() or ::change_state() vfuncs.
@@ -35,13 +38,37 @@
* The driver needs to call fpi_image_device_report_finger_status() to move * The driver needs to call fpi_image_device_report_finger_status() to move
* between the different states. Note that the capture state might be entered * between the different states. Note that the capture state might be entered
* unconditionally if the device supports raw capturing. * unconditionally if the device supports raw capturing.
*
* A usual run would look like:
* - inactive -> activating: activate vfunc is called
* - activating -> idle: fpi_image_device_activate_complete()
* - idle -> await-finger-on
* - await-finger-on -> capture: fpi_image_device_report_finger_status()
* - capture -> await-finger-off: fpi_image_device_image_captured()
* - await-finger-off -> idle: fpi_image_device_report_finger_status()
* - idle -> deactivating: deactivate vfunc is called
* - deactivating -> inactive: fpi_image_device_deactivate_complete()
*
* Raw mode is currently not supported (not waiting for finger), but in that
* case the following transitions are valid:
* - idle -> capture
* - capture -> idle
*
* Also valid are these transitions in case of errors or cancellations:
* - activating -> inactive: fpi_image_device_activate_complete()
* - await-finger-on -> deactivating: deactivate vfunc is called
* - capture -> deactivating: deactivate vfunc is called
* - await-finger-off -> deactivating: deactivate vfunc is called
*/ */
typedef enum { typedef enum {
FP_IMAGE_DEVICE_STATE_INACTIVE, FPI_IMAGE_DEVICE_STATE_INACTIVE,
FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON, FPI_IMAGE_DEVICE_STATE_ACTIVATING,
FP_IMAGE_DEVICE_STATE_CAPTURE, FPI_IMAGE_DEVICE_STATE_DEACTIVATING,
FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF, FPI_IMAGE_DEVICE_STATE_IDLE,
} FpImageDeviceState; FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON,
FPI_IMAGE_DEVICE_STATE_CAPTURE,
FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF,
} FpiImageDeviceState;
/** /**
* FpImageDeviceClass: * FpImageDeviceClass:
@@ -58,11 +85,6 @@ typedef enum {
* finger or image capture). Implementing this is optional, it can e.g. be * finger or image capture). Implementing this is optional, it can e.g. be
* used to flash an LED when waiting for a finger. * used to flash an LED when waiting for a finger.
* *
* These are the main entry points for image based drivers. For all but the
* change_state vfunc, implementations *must* eventually call the corresponding
* function to finish the operation. It is also acceptable to call the generic
*
*
* These are the main entry points for drivers to implement. Drivers may not * These are the main entry points for drivers to implement. Drivers may not
* implement all of these entry points if they do not support the operation * implement all of these entry points if they do not support the operation
* (or a default implementation is sufficient). * (or a default implementation is sufficient).
@@ -72,9 +94,8 @@ typedef enum {
* fpi_device_action_error() function but doing so is not recommended in most * fpi_device_action_error() function but doing so is not recommended in most
* usecases. * usecases.
* *
* Drivers *must* also handle cancellation properly for any long running * Image drivers must expect a @deactivate call to happen at any point during
* operation (i.e. any operation that requires capturing). It is entirely fine * image capture.
* to ignore cancellation requests for short operations (e.g. open/close).
* *
* This API is solely intended for drivers. It is purely internal and neither * This API is solely intended for drivers. It is purely internal and neither
* API nor ABI stable. * API nor ABI stable.
@@ -91,7 +112,7 @@ struct _FpImageDeviceClass
void (*img_close) (FpImageDevice *dev); void (*img_close) (FpImageDevice *dev);
void (*activate) (FpImageDevice *dev); void (*activate) (FpImageDevice *dev);
void (*change_state) (FpImageDevice *dev, void (*change_state) (FpImageDevice *dev,
FpImageDeviceState state); FpiImageDeviceState state);
void (*deactivate) (FpImageDevice *dev); void (*deactivate) (FpImageDevice *dev);
}; };

149
libfprint/fpi-image.c Normal file
View File

@@ -0,0 +1,149 @@
/*
* FPrint Image - Private APIs
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 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
* 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 "image"
#include "fpi-image.h"
#include "fpi-log.h"
#include <nbis.h>
#if HAVE_PIXMAN
#include <pixman.h>
#endif
/**
* SECTION: fpi-image
* @title: Internal FpImage
* @short_description: Internal image handling routines
*
* Internal image handling routines. See #FpImage for public routines.
*/
/**
* fpi_std_sq_dev:
* @buf: buffer (usually bitmap, one byte per pixel)
* @size: size of @buffer
*
* Calculates the squared standard deviation of the individual
* pixels in the buffer, as per the following formula:
* |[<!-- -->
* mean = sum (buf[0..size]) / size
* sq_dev = sum ((buf[0.size] - mean) ^ 2)
* ]|
* This function is usually used to determine whether image
* is empty.
*
* Returns: the squared standard deviation for @buffer
*/
gint
fpi_std_sq_dev (const guint8 *buf,
gint size)
{
guint64 res = 0, mean = 0;
gint i;
for (i = 0; i < size; i++)
mean += buf[i];
mean /= size;
for (i = 0; i < size; i++)
{
int dev = (int) buf[i] - mean;
res += dev * dev;
}
return res / size;
}
/**
* fpi_mean_sq_diff_norm:
* @buf1: buffer (usually bitmap, one byte per pixel)
* @buf2: buffer (usually bitmap, one byte per pixel)
* @size: buffer size of smallest buffer
*
* This function calculates the normalized mean square difference of
* two buffers, usually two lines, as per the following formula:
* |[<!-- -->
* sq_diff = sum ((buf1[0..size] - buf2[0..size]) ^ 2) / size
* ]|
*
* This functions is usually used to get numerical difference
* between two images.
*
* Returns: the normalized mean squared difference between @buf1 and @buf2
*/
gint
fpi_mean_sq_diff_norm (const guint8 *buf1,
const guint8 *buf2,
gint size)
{
int res = 0, i;
for (i = 0; i < size; i++)
{
int dev = (int) buf1[i] - (int) buf2[i];
res += dev * dev;
}
return res / size;
}
#if HAVE_PIXMAN
FpImage *
fpi_image_resize (FpImage *orig_img,
guint w_factor,
guint h_factor)
{
int new_width = orig_img->width * w_factor;
int new_height = orig_img->height * h_factor;
pixman_image_t *orig, *resized;
pixman_transform_t transform;
FpImage *newimg;
orig = pixman_image_create_bits (PIXMAN_a8, orig_img->width, orig_img->height, (uint32_t *) orig_img->data, orig_img->width);
resized = pixman_image_create_bits (PIXMAN_a8, new_width, new_height, NULL, new_width);
pixman_transform_init_identity (&transform);
pixman_transform_scale (NULL, &transform, pixman_int_to_fixed (w_factor), pixman_int_to_fixed (h_factor));
pixman_image_set_transform (orig, &transform);
pixman_image_set_filter (orig, PIXMAN_FILTER_BILINEAR, NULL, 0);
pixman_image_composite32 (PIXMAN_OP_SRC,
orig, /* src */
NULL, /* mask */
resized, /* dst */
0, 0, /* src x y */
0, 0, /* mask x y */
0, 0, /* dst x y */
new_width, new_height /* width height */
);
newimg = fp_image_new (new_width, new_height);
newimg->flags = orig_img->flags;
memcpy (newimg->data, pixman_image_get_data (resized), new_width * new_height);
pixman_image_unref (orig);
pixman_image_unref (resized);
return newimg;
}
#endif

View File

@@ -37,6 +37,7 @@ typedef enum {
FPI_IMAGE_V_FLIPPED = 1 << 0, FPI_IMAGE_V_FLIPPED = 1 << 0,
FPI_IMAGE_H_FLIPPED = 1 << 1, FPI_IMAGE_H_FLIPPED = 1 << 1,
FPI_IMAGE_COLORS_INVERTED = 1 << 2, FPI_IMAGE_COLORS_INVERTED = 1 << 2,
FPI_IMAGE_PARTIAL = 1 << 3,
} FpiImageFlags; } FpiImageFlags;
/** /**

View File

@@ -17,8 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __FPI_LOG_H__ #pragma once
#define __FPI_LOG_H__
/** /**
* SECTION:fpi-log * SECTION:fpi-log
@@ -94,5 +93,3 @@
* Same as BUG_ON() but is always true. * Same as BUG_ON() but is always true.
*/ */
#define BUG() BUG_ON (1) #define BUG() BUG_ON (1)
#endif

362
libfprint/fpi-print.c Normal file
View File

@@ -0,0 +1,362 @@
/*
* FPrint Print handling - Private APIs
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 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
* 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 "print"
#include "fpi-log.h"
#include "fp-print-private.h"
#include "fpi-device.h"
#include "fpi-compat.h"
/**
* SECTION: fpi-print
* @title: Internal FpPrint
* @short_description: Internal fingerprint handling routines
*
* Interaction with prints and their storage. See also the public
* #FpPrint routines.
*/
/**
* fpi_print_add_print:
* @print: A #FpPrint
* @add: Print to append to @print
*
* Appends the single #FPI_PRINT_NBIS print from @add to the collection of
* prints in @print. Both print objects need to be of type #FPI_PRINT_NBIS
* for this to work.
*/
void
fpi_print_add_print (FpPrint *print, FpPrint *add)
{
g_return_if_fail (print->type == FPI_PRINT_NBIS);
g_return_if_fail (add->type == FPI_PRINT_NBIS);
g_assert (add->prints->len == 1);
g_ptr_array_add (print->prints, g_memdup (add->prints->pdata[0], sizeof (struct xyt_struct)));
}
/**
* fpi_print_set_type:
* @print: A #FpPrint
* @type: The newly type of the print data
*
* This function can only be called exactly once. Drivers should
* call it after creating a new print, or to initialize the template
* print passed during enrollment.
*/
void
fpi_print_set_type (FpPrint *print,
FpiPrintType type)
{
g_return_if_fail (FP_IS_PRINT (print));
/* We only allow setting this once! */
g_return_if_fail (print->type == FPI_PRINT_UNDEFINED);
print->type = type;
if (print->type == FPI_PRINT_NBIS)
{
g_assert_null (print->prints);
print->prints = g_ptr_array_new_with_free_func (g_free);
}
g_object_notify (G_OBJECT (print), "fpi-type");
}
/**
* fpi_print_set_device_stored:
* @print: A #FpPrint
* @device_stored: Whether the print is stored on the device or not
*
* Drivers must set this to %TRUE for any print that is really a handle
* for data that is stored on the device itself.
*/
void
fpi_print_set_device_stored (FpPrint *print,
gboolean device_stored)
{
g_return_if_fail (FP_IS_PRINT (print));
print->device_stored = device_stored;
g_object_notify (G_OBJECT (print), "device-stored");
}
/* XXX: This is the old version, but wouldn't it be smarter to instead
* use the highest quality mintutiae? Possibly just using bz_prune from
* upstream? */
static void
minutiae_to_xyt (struct fp_minutiae *minutiae,
int bwidth,
int bheight,
struct xyt_struct *xyt)
{
int i;
struct fp_minutia *minutia;
struct minutiae_struct c[MAX_FILE_MINUTIAE];
/* struct xyt_struct uses arrays of MAX_BOZORTH_MINUTIAE (200) */
int nmin = min (minutiae->num, MAX_BOZORTH_MINUTIAE);
for (i = 0; i < nmin; i++)
{
minutia = minutiae->list[i];
lfs2nist_minutia_XYT (&c[i].col[0], &c[i].col[1], &c[i].col[2],
minutia, bwidth, bheight);
c[i].col[3] = sround (minutia->reliability * 100.0);
if (c[i].col[2] > 180)
c[i].col[2] -= 360;
}
qsort ((void *) &c, (size_t) nmin, sizeof (struct minutiae_struct),
sort_x_y);
for (i = 0; i < nmin; i++)
{
xyt->xcol[i] = c[i].col[0];
xyt->ycol[i] = c[i].col[1];
xyt->thetacol[i] = c[i].col[2];
}
xyt->nrows = nmin;
}
/**
* fpi_print_add_from_image:
* @print: A #FpPrint
* @image: A #FpImage
* @error: Return location for error
*
* Extracts the minutiae from the given image and adds it to @print of
* type #FPI_PRINT_NBIS.
*
* The @image will be kept so that API users can get retrieve it e.g.
* for debugging purposes.
*
* Returns: %TRUE on success
*/
gboolean
fpi_print_add_from_image (FpPrint *print,
FpImage *image,
GError **error)
{
GPtrArray *minutiae;
struct fp_minutiae _minutiae;
struct xyt_struct *xyt;
if (print->type != FPI_PRINT_NBIS || !image)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"Cannot add print data from image!");
return FALSE;
}
minutiae = fp_image_get_minutiae (image);
if (!minutiae || minutiae->len == 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"No minutiae found in image or not yet detected!");
return FALSE;
}
_minutiae.num = minutiae->len;
_minutiae.list = (struct fp_minutia **) minutiae->pdata;
_minutiae.alloc = minutiae->len;
xyt = g_new0 (struct xyt_struct, 1);
minutiae_to_xyt (&_minutiae, image->width, image->height, xyt);
g_ptr_array_add (print->prints, xyt);
g_clear_object (&print->image);
print->image = g_object_ref (image);
g_object_notify (G_OBJECT (print), "image");
return TRUE;
}
/**
* fpi_print_bz3_match:
* @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
*
* Match the newly scanned @print (containing exactly one print) against the
* prints contained in @template which will have been stored during enrollment.
*
* Both @template and @print need to be of type #FPI_PRINT_NBIS for this to
* work.
*
* Returns: Whether the prints match, @error will be set if #FPI_MATCH_ERROR is returned
*/
FpiMatchResult
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 (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");
return FPI_MATCH_ERROR;
}
if (print->prints->len != 1)
{
*error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"New print contains more than one print!");
return FPI_MATCH_ERROR;
}
pstruct = g_ptr_array_index (print->prints, 0);
probe_len = bozorth_probe_init (pstruct);
for (i = 0; i < template->prints->len; i++)
{
struct xyt_struct *gstruct;
gint score;
gstruct = g_ptr_array_index (template->prints, i);
score = bozorth_to_gallery (probe_len, pstruct, gstruct);
fp_dbg ("score %d/%d", score, bz3_threshold);
if (score >= bz3_threshold)
return FPI_MATCH_SUCCESS;
}
return FPI_MATCH_FAIL;
}
/**
* fpi_print_generate_user_id:
* @print: #FpPrint to generate the ID for
*
* Generates a string identifier for the represented print. This identifier
* encodes some metadata about the print. It also includes a random string
* and may be assumed to be unique.
*
* This is useful if devices are able to store a string identifier, but more
* storing more metadata may be desirable. In effect, this means the driver
* can provide somewhat more meaningful data to fp_device_list_prints().
*
* The generated ID may be truncated after 23 characters. However, more space
* is required to include the username, and it is recommended to store at
* at least 31 bytes.
*
* The generated format may change in the future. It is versioned though and
* decoding should remain functional.
*
* Returns: A unique string of 23 + strlen(username) characters
*/
gchar *
fpi_print_generate_user_id (FpPrint *print)
{
const gchar *username = NULL;
gchar *user_id = NULL;
const GDate *date;
gint y = 0, m = 0, d = 0;
gint32 rand_id = 0;
g_assert (print);
date = fp_print_get_enroll_date (print);
if (date && g_date_valid (date))
{
y = g_date_get_year (date);
m = g_date_get_month (date);
d = g_date_get_day (date);
}
username = fp_print_get_username (print);
if (!username)
username = "nobody";
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
rand_id = 0;
else
rand_id = g_random_int ();
user_id = g_strdup_printf ("FP1-%04d%02d%02d-%X-%08X-%s",
y, m, d,
fp_print_get_finger (print),
rand_id,
username);
return user_id;
}
/**
* fpi_print_fill_from_user_id:
* @print: #FpPrint to fill metadata into
* @user_id: An ID that was likely encoded using fpi_print_generate_user_id()
*
* This is the reverse operation of fpi_print_generate_user_id(), allowing
* the driver to encode some print metadata in a string.
*
* Returns: Whether a valid ID was found
*/
gboolean
fpi_print_fill_from_user_id (FpPrint *print, const char *user_id)
{
g_return_val_if_fail (user_id, FALSE);
/* The format has 24 bytes at the start and some dashes in the right places */
if (g_str_has_prefix (user_id, "FP1-") && strlen (user_id) >= 24 &&
user_id[12] == '-' && user_id[14] == '-' && user_id[23] == '-')
{
g_autofree gchar *copy = g_strdup (user_id);
g_autoptr(GDate) date = NULL;
gint32 date_ymd;
gint32 finger;
gchar *username;
/* Try to parse information from the string. */
copy[12] = '\0';
date_ymd = g_ascii_strtod (copy + 4, NULL);
if (date_ymd > 0)
date = g_date_new_dmy (date_ymd % 100,
(date_ymd / 100) % 100,
date_ymd / 10000);
else
date = g_date_new ();
fp_print_set_enroll_date (print, date);
copy[14] = '\0';
finger = g_ascii_strtoll (copy + 13, NULL, 16);
fp_print_set_finger (print, finger);
/* We ignore the next chunk, it is just random data.
* Then comes the username; nobody is the default if the metadata
* is unknown */
username = copy + 24;
if (strlen (username) > 0 && g_strcmp0 (username, "nobody") != 0)
fp_print_set_username (print, username);
return TRUE;
}
return FALSE;
}

View File

@@ -7,34 +7,34 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/** /**
* FpPrintType: * FpiPrintType:
* @FP_PRINT_UNDEFINED: Undefined type, this happens prior to enrollment * @FPI_PRINT_UNDEFINED: Undefined type, this happens prior to enrollment
* @FP_PRINT_RAW: A raw print where the data is directly compared * @FPI_PRINT_RAW: A raw print where the data is directly compared
* @FP_PRINT_NBIS: NBIS minutiae comparison * @FPI_PRINT_NBIS: NBIS minutiae comparison
*/ */
typedef enum { typedef enum {
FP_PRINT_UNDEFINED = 0, FPI_PRINT_UNDEFINED = 0,
FP_PRINT_RAW, FPI_PRINT_RAW,
FP_PRINT_NBIS, FPI_PRINT_NBIS,
} FpPrintType; } FpiPrintType;
/** /**
* FpiMatchResult: * FpiMatchResult:
* @FPI_MATCH_ERROR: An error occured during matching * @FPI_MATCH_ERROR: An error occurred during matching
* @FPI_MATCH_SUCCESS: The prints matched
* @FPI_MATCH_FAIL: The prints did not match * @FPI_MATCH_FAIL: The prints did not match
* @FPI_MATCH_SUCCESS: The prints matched
*/ */
typedef enum { typedef enum {
FPI_MATCH_ERROR = 0, FPI_MATCH_ERROR = -1, /* -1 for g_task_propagate_int */
FPI_MATCH_SUCCESS,
FPI_MATCH_FAIL, FPI_MATCH_FAIL,
FPI_MATCH_SUCCESS,
} FpiMatchResult; } FpiMatchResult;
void fpi_print_add_print (FpPrint *print, void fpi_print_add_print (FpPrint *print,
FpPrint *add); FpPrint *add);
void fpi_print_set_type (FpPrint *print, void fpi_print_set_type (FpPrint *print,
FpPrintType type); FpiPrintType type);
void fpi_print_set_device_stored (FpPrint *print, void fpi_print_set_device_stored (FpPrint *print,
gboolean device_stored); gboolean device_stored);
@@ -47,4 +47,9 @@ FpiMatchResult fpi_print_bz3_match (FpPrint * template,
gint bz3_threshold, gint bz3_threshold,
GError **error); GError **error);
/* Helpers to encode metadata into user ID strings. */
gchar * fpi_print_generate_user_id (FpPrint *print);
gboolean fpi_print_fill_from_user_id (FpPrint *print,
const char *user_id);
G_END_DECLS G_END_DECLS

View File

@@ -115,7 +115,7 @@ struct _FpiSsm
* @dev: a #fp_dev fingerprint device * @dev: a #fp_dev fingerprint device
* @handler: the callback function * @handler: the callback function
* @nr_states: the number of states * @nr_states: the number of states
* @name: the name of the state machine (for debug purposes) * @machine_name: the name of the state machine (for debug purposes)
* *
* Allocate a new ssm, with @nr_states states. The @handler callback * Allocate a new ssm, with @nr_states states. The @handler callback
* will be called after each state transition. * will be called after each state transition.
@@ -126,7 +126,7 @@ FpiSsm *
fpi_ssm_new_full (FpDevice *dev, fpi_ssm_new_full (FpDevice *dev,
FpiSsmHandlerCallback handler, FpiSsmHandlerCallback handler,
int nr_states, int nr_states,
const char *name) const char *machine_name)
{ {
FpiSsm *machine; FpiSsm *machine;
@@ -137,7 +137,7 @@ fpi_ssm_new_full (FpDevice *dev,
machine->handler = handler; machine->handler = handler;
machine->nr_states = nr_states; machine->nr_states = nr_states;
machine->dev = dev; machine->dev = dev;
machine->name = g_strdup (name); machine->name = g_strdup (machine_name);
machine->completed = TRUE; machine->completed = TRUE;
return machine; return machine;
} }
@@ -213,6 +213,9 @@ on_delayed_action_cancelled (GCancellable *cancellable,
{ {
CancelledActionIdleData *data; CancelledActionIdleData *data;
fp_dbg ("[%s] %s cancelled delayed state change",
fp_device_get_driver (machine->dev), machine->name);
g_clear_pointer (&machine->timeout, g_source_destroy); g_clear_pointer (&machine->timeout, g_source_destroy);
data = g_new0 (CancelledActionIdleData, 1); data = g_new0 (CancelledActionIdleData, 1);
@@ -335,7 +338,10 @@ fpi_ssm_start_subsm (FpiSsm *parent, FpiSsm *child)
{ {
BUG_ON (parent->timeout); BUG_ON (parent->timeout);
child->parentsm = parent; child->parentsm = parent;
g_clear_pointer (&parent->timeout, g_source_destroy);
fpi_ssm_clear_delayed_action (parent);
fpi_ssm_clear_delayed_action (child);
fpi_ssm_start (child, __subsm_complete); fpi_ssm_start (child, __subsm_complete);
} }
@@ -469,6 +475,9 @@ fpi_ssm_cancel_delayed_state_change (FpiSsm *machine)
BUG_ON (machine->completed); BUG_ON (machine->completed);
BUG_ON (machine->timeout == NULL); BUG_ON (machine->timeout == NULL);
fp_dbg ("[%s] %s cancelled delayed state change",
fp_device_get_driver (machine->dev), machine->name);
fpi_ssm_clear_delayed_action (machine); fpi_ssm_clear_delayed_action (machine);
} }
@@ -573,8 +582,7 @@ fpi_ssm_jump_to_state_delayed (FpiSsm *machine,
g_autofree char *source_name = NULL; g_autofree char *source_name = NULL;
g_return_if_fail (machine != NULL); g_return_if_fail (machine != NULL);
BUG_ON (machine->completed); BUG_ON (state < 0 || state >= machine->nr_states);
BUG_ON (machine->timeout != NULL);
data = g_new0 (FpiSsmJumpToStateDelayedData, 1); data = g_new0 (FpiSsmJumpToStateDelayedData, 1);
data->machine = machine; data->machine = machine;

View File

@@ -22,7 +22,6 @@
#pragma once #pragma once
#include "fp-device.h" #include "fp-device.h"
#include "fpi-usb-transfer.h"
/* async drv <--> lib comms */ /* async drv <--> lib comms */
@@ -39,7 +38,7 @@ typedef struct _FpiSsm FpiSsm;
* FpiSsmCompletedCallback: * FpiSsmCompletedCallback:
* @ssm: a #FpiSsm state machine * @ssm: a #FpiSsm state machine
* @dev: the #fp_dev fingerprint device * @dev: the #fp_dev fingerprint device
* @error: The #GError or %NULL on successful completion * @error: (transfer full): The #GError or %NULL on successful completion
* *
* The callback called when a state machine completes successfully, * The callback called when a state machine completes successfully,
* as set when calling fpi_ssm_start(). * as set when calling fpi_ssm_start().
@@ -101,6 +100,8 @@ int fpi_ssm_get_cur_state (FpiSsm *machine);
/* Callbacks to be used by the driver instead of implementing their own /* Callbacks to be used by the driver instead of implementing their own
* logic. * logic.
*/ */
typedef struct _FpiUsbTransfer FpiUsbTransfer;
void fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, void fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer,
FpDevice *device, FpDevice *device,
gpointer unused_data, gpointer unused_data,
@@ -109,3 +110,5 @@ void fpi_ssm_usb_transfer_with_weak_pointer_cb (FpiUsbTransfer *transfer,
FpDevice *device, FpDevice *device,
gpointer weak_ptr, gpointer weak_ptr,
GError *error); GError *error);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpiSsm, fpi_ssm_free)

View File

@@ -105,6 +105,7 @@ fpi_usb_transfer_new (FpDevice * device)
self = g_slice_new0 (FpiUsbTransfer); self = g_slice_new0 (FpiUsbTransfer);
self->ref_count = 1; self->ref_count = 1;
self->type = FP_TRANSFER_NONE;
self->device = device; self->device = device;
@@ -353,6 +354,24 @@ transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_dat
fpi_usb_transfer_unref (transfer); fpi_usb_transfer_unref (transfer);
} }
static gboolean
transfer_cancel_cb (FpiUsbTransfer *transfer)
{
GError *error;
FpiUsbTransferCallback callback;
error = g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_CANCELLED,
"Transfer was cancelled before being started");
callback = transfer->callback;
transfer->callback = NULL;
transfer->actual_length = -1;
callback (transfer, transfer->device, transfer->user_data, error);
fpi_usb_transfer_unref (transfer);
return G_SOURCE_REMOVE;
}
/** /**
* fpi_usb_transfer_submit: * fpi_usb_transfer_submit:
@@ -366,7 +385,7 @@ transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_dat
* *
* Note that #FpiUsbTransfer will be stolen when this function is called. * Note that #FpiUsbTransfer will be stolen when this function is called.
* So that all associated data will be free'ed automatically, after the * So that all associated data will be free'ed automatically, after the
* callback ran unless fpi_usb_transfer_ref() is explictly called. * callback ran unless fpi_usb_transfer_ref() is explicitly called.
*/ */
void void
fpi_usb_transfer_submit (FpiUsbTransfer *transfer, fpi_usb_transfer_submit (FpiUsbTransfer *transfer,
@@ -386,6 +405,18 @@ fpi_usb_transfer_submit (FpiUsbTransfer *transfer,
log_transfer (transfer, TRUE, NULL); log_transfer (transfer, TRUE, NULL);
/* Work around libgusb cancellation issue, see
* https://github.com/hughsie/libgusb/pull/42
* should be fixed with libgusb 0.3.7.
* Note that this is not race free, we rely on libfprint and API users
* not cancelling from a different thread here.
*/
if (cancellable && g_cancellable_is_cancelled (cancellable))
{
g_idle_add ((GSourceFunc) transfer_cancel_cb, transfer);
return;
}
switch (transfer->type) switch (transfer->type)
{ {
case FP_TRANSFER_BULK: case FP_TRANSFER_BULK:

View File

@@ -20,6 +20,7 @@
#pragma once #pragma once
#include <gusb.h> #include <gusb.h>
#include "fpi-compat.h"
#include "fpi-device.h" #include "fpi-device.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@@ -30,8 +31,7 @@ G_BEGIN_DECLS
#define FPI_USB_ENDPOINT_OUT 0x00 #define FPI_USB_ENDPOINT_OUT 0x00
typedef struct _FpiUsbTransfer FpiUsbTransfer; typedef struct _FpiUsbTransfer FpiUsbTransfer;
typedef struct _FpiSsm FpiSsm;
#include "fpi-ssm.h"
typedef void (*FpiUsbTransferCallback)(FpiUsbTransfer *transfer, typedef void (*FpiUsbTransferCallback)(FpiUsbTransfer *transfer,
FpDevice *dev, FpDevice *dev,
@@ -48,10 +48,10 @@ typedef void (*FpiUsbTransferCallback)(FpiUsbTransfer *transfer,
* Type of the transfer. * Type of the transfer.
*/ */
typedef enum { typedef enum {
FP_TRANSFER_NONE = 0, FP_TRANSFER_NONE = -1,
FP_TRANSFER_BULK, FP_TRANSFER_CONTROL = 0,
FP_TRANSFER_CONTROL, FP_TRANSFER_BULK = 2,
FP_TRANSFER_INTERRUPT, FP_TRANSFER_INTERRUPT = 3,
} FpiTransferType; } FpiTransferType;
/** /**
@@ -62,7 +62,7 @@ typedef enum {
* @length: The requested length of the transfer in bytes. * @length: The requested length of the transfer in bytes.
* @actual_length: The actual length of the transfer * @actual_length: The actual length of the transfer
* (see also fpi_usb_transfer_set_short_error()) * (see also fpi_usb_transfer_set_short_error())
* @buffer: The transfered data. * @buffer: The transferred data.
* *
* Helper for handling USB transfers. * Helper for handling USB transfers.
*/ */
@@ -116,6 +116,7 @@ void fpi_usb_transfer_fill_bulk (FpiUsbTransfer *transfer,
guint8 endpoint, guint8 endpoint,
gsize length); gsize length);
FP_GNUC_ACCESS (read_only, 3, 4)
void fpi_usb_transfer_fill_bulk_full (FpiUsbTransfer *transfer, void fpi_usb_transfer_fill_bulk_full (FpiUsbTransfer *transfer,
guint8 endpoint, guint8 endpoint,
guint8 *buffer, guint8 *buffer,
@@ -135,6 +136,7 @@ void fpi_usb_transfer_fill_interrupt (FpiUsbTransfer *transfer,
guint8 endpoint, guint8 endpoint,
gsize length); gsize length);
FP_GNUC_ACCESS (read_only, 3, 4)
void fpi_usb_transfer_fill_interrupt_full (FpiUsbTransfer *transfer, void fpi_usb_transfer_fill_interrupt_full (FpiUsbTransfer *transfer,
guint8 endpoint, guint8 endpoint,
guint8 *buffer, guint8 *buffer,

View File

@@ -2,7 +2,7 @@
* Copyright (C) 2009 Red Hat <mjg@redhat.com> * Copyright (C) 2009 Red Hat <mjg@redhat.com>
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net> * Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
* Copyright (C) 2008 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net> * Copyright (C) 2008 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net>
* Coypright (C) 2019 Benjamin Berg <bberg@redhat.com> * Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@@ -31,23 +31,18 @@ GHashTable *printed = NULL;
static GList * static GList *
insert_drivers (GList *list) insert_drivers (GList *list)
{ {
g_autoptr(GArray) drivers = g_array_new (FALSE, FALSE, sizeof (GType)); g_autoptr(GArray) drivers = fpi_get_driver_types ();
gint i; gint i;
fpi_get_driver_types (drivers);
/* Find the best driver to handle this USB device. */ /* Find the best driver to handle this USB device. */
for (i = 0; i < drivers->len; i++) for (i = 0; i < drivers->len; i++)
{ {
GType driver = g_array_index (drivers, GType, i); GType driver = g_array_index (drivers, GType, i);
FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver)); g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver);
const FpIdEntry *entry; const FpIdEntry *entry;
if (cls->type != FP_DEVICE_TYPE_USB) if (cls->type != FP_DEVICE_TYPE_USB)
{
g_type_class_unref (cls);
continue; continue;
}
for (entry = cls->id_table; entry->vid; entry++) for (entry = cls->id_table; entry->vid; entry++)
{ {
@@ -65,8 +60,6 @@ insert_drivers (GList *list)
list = g_list_prepend (list, g_strdup_printf ("%s | %s\n", key, cls->full_name)); list = g_list_prepend (list, g_strdup_printf ("%s | %s\n", key, cls->full_name));
} }
g_type_class_unref (cls);
} }
return list; return list;

View File

@@ -2,7 +2,7 @@
* Copyright (C) 2009 Red Hat <mjg@redhat.com> * Copyright (C) 2009 Red Hat <mjg@redhat.com>
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net> * Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
* Copyright (C) 2008 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net> * Copyright (C) 2008 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net>
* Coypright (C) 2019 Benjamin Berg <bberg@redhat.com> * Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@@ -25,11 +25,78 @@
#include "fpi-device.h" #include "fpi-device.h"
static const FpIdEntry whitelist_id_table[] = { static const FpIdEntry whitelist_id_table[] = {
/* Unsupported (for now) Validity Sensors finger print readers */ /* Currently known and unsupported devices.
{ .vid = 0x138a, .pid = 0x0090 }, /* Found on e.g. Lenovo T460s */ * You can generate this list from the wiki page using e.g.:
* gio cat https://gitlab.freedesktop.org/libfprint/wiki/-/wikis/Unsupported-Devices.md | sed -n 's!|.*\([0-9a-fA-F]\{4\}\):\([0-9a-fA-F]\{4\}\).*|.*! { .vid = 0x\1, .pid = 0x\2 },!p'
*/
{ .vid = 0x04f3, .pid = 0x036b },
{ .vid = 0x04f3, .pid = 0x0c00 },
{ .vid = 0x04f3, .pid = 0x0c4b },
{ .vid = 0x04f3, .pid = 0x0c4c },
{ .vid = 0x04f3, .pid = 0x0c4f },
{ .vid = 0x04f3, .pid = 0x0c57 },
{ .vid = 0x04f3, .pid = 0x2706 },
{ .vid = 0x06cb, .pid = 0x0081 },
{ .vid = 0x06cb, .pid = 0x0088 },
{ .vid = 0x06cb, .pid = 0x008a },
{ .vid = 0x06cb, .pid = 0x009a },
{ .vid = 0x06cb, .pid = 0x009b },
{ .vid = 0x06cb, .pid = 0x00a2 },
{ .vid = 0x06cb, .pid = 0x00b7 },
{ .vid = 0x06cb, .pid = 0x00bb },
{ .vid = 0x06cb, .pid = 0x00be },
{ .vid = 0x06cb, .pid = 0x00c2 },
{ .vid = 0x06cb, .pid = 0x00c9 },
{ .vid = 0x06cb, .pid = 0x00cb },
{ .vid = 0x06cb, .pid = 0x00d8 },
{ .vid = 0x06cb, .pid = 0x00da },
{ .vid = 0x06cb, .pid = 0x00e7 },
{ .vid = 0x0a5c, .pid = 0x5801 },
{ .vid = 0x0a5c, .pid = 0x5805 },
{ .vid = 0x0a5c, .pid = 0x5834 },
{ .vid = 0x0a5c, .pid = 0x5843 },
{ .vid = 0x10a5, .pid = 0x0007 },
{ .vid = 0x1188, .pid = 0x9545 },
{ .vid = 0x138a, .pid = 0x0007 },
{ .vid = 0x138a, .pid = 0x003a },
{ .vid = 0x138a, .pid = 0x003c },
{ .vid = 0x138a, .pid = 0x003d },
{ .vid = 0x138a, .pid = 0x003f },
{ .vid = 0x138a, .pid = 0x0090 },
{ .vid = 0x138a, .pid = 0x0091 }, { .vid = 0x138a, .pid = 0x0091 },
{ .vid = 0x138a, .pid = 0x0092 },
{ .vid = 0x138a, .pid = 0x0094 }, { .vid = 0x138a, .pid = 0x0094 },
{ .vid = 0x138a, .pid = 0x0097 }, /* Found on e.g. Lenovo T470s */ { .vid = 0x138a, .pid = 0x0097 },
{ .vid = 0x138a, .pid = 0x009d },
{ .vid = 0x138a, .pid = 0x00ab },
{ .vid = 0x147e, .pid = 0x1002 },
{ .vid = 0x1491, .pid = 0x0088 },
{ .vid = 0x16d1, .pid = 0x1027 },
{ .vid = 0x1c7a, .pid = 0x0300 },
{ .vid = 0x1c7a, .pid = 0x0570 },
{ .vid = 0x1c7a, .pid = 0x0575 },
{ .vid = 0x27c6, .pid = 0x5042 },
{ .vid = 0x27c6, .pid = 0x5110 },
{ .vid = 0x27c6, .pid = 0x5117 },
{ .vid = 0x27c6, .pid = 0x5201 },
{ .vid = 0x27c6, .pid = 0x521d },
{ .vid = 0x27c6, .pid = 0x5301 },
{ .vid = 0x27c6, .pid = 0x530c },
{ .vid = 0x27c6, .pid = 0x532d },
{ .vid = 0x27c6, .pid = 0x533c },
{ .vid = 0x27c6, .pid = 0x5381 },
{ .vid = 0x27c6, .pid = 0x5385 },
{ .vid = 0x27c6, .pid = 0x538c },
{ .vid = 0x27c6, .pid = 0x538d },
{ .vid = 0x27c6, .pid = 0x5395 },
{ .vid = 0x27c6, .pid = 0x5584 },
{ .vid = 0x27c6, .pid = 0x55a2 },
{ .vid = 0x27c6, .pid = 0x55a4 },
{ .vid = 0x27c6, .pid = 0x55b4 },
{ .vid = 0x27c6, .pid = 0x5740 },
{ .vid = 0x2808, .pid = 0x9338 },
{ .vid = 0x298d, .pid = 0x2033 },
{ .vid = 0x3538, .pid = 0x0930 },
{ .vid = 0 }, { .vid = 0 },
}; };
@@ -96,27 +163,20 @@ print_driver (const FpDeviceClass *cls)
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
g_autoptr(GArray) drivers = g_array_new (FALSE, FALSE, sizeof (GType)); g_autoptr(GArray) drivers = fpi_get_driver_types ();
guint i; guint i;
fpi_get_driver_types (drivers);
printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
for (i = 0; i < drivers->len; i++) for (i = 0; i < drivers->len; i++)
{ {
GType driver = g_array_index (drivers, GType, i); GType driver = g_array_index (drivers, GType, i);
FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver)); g_autoptr(FpDeviceClass) cls = g_type_class_ref (driver);
if (cls->type != FP_DEVICE_TYPE_USB) if (cls->type != FP_DEVICE_TYPE_USB)
{
g_type_class_unref (cls);
continue; continue;
}
print_driver (cls); print_driver (cls);
g_type_class_unref (cls);
} }
print_driver (&whitelist); print_driver (&whitelist);

View File

@@ -1,9 +1,6 @@
LIBFPRINT_2.0.0 { LIBFPRINT_2.0.0 {
global: global:
fp_*; fp_*;
/* Needs to be public for the listing commands. */
fpi_get_driver_types;
local: local:
*; *;
}; };

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