Compare commits

...

538 Commits

Author SHA1 Message Date
Bastien Nocera
823f2c1067 1.0 2019-08-08 14:54:06 +02:00
Bastien Nocera
19732341d6 lib: Fix overwriting action after deactivating callback
If one of the callbacks called from fpi_imgdev_deactivate_complete()
was reactivating the device, then we would be overwriting whichever
"action" got set in the callback, leading to
fpi_imgdev_activate_complete() failing as it doesn't handle the "none"
action.

Reset the action before calling the callbacks.
2019-08-08 12:43:03 +00:00
Bastien Nocera
0e44eb4c1c elan: Better debug when skipping commands 2019-08-08 12:43:03 +00:00
Bastien Nocera
50461b4d7d lib: Make fp_async_*_stop() not throw warning if already in right state
Make it possible to call fp_async_*_stop() multiple times without
penalty.
2019-08-08 12:43:03 +00:00
Bastien Nocera
c11126181e aeslib: Fix use-after-free in aeslib
If a USB transfer is started but not completed in one go, the wdata we
pass to continue_write_regv() will already be freed by the time we try
to use it again.

Only free() the wdata on error, or when the USB transfer is completed.

Closes: #180
2019-08-08 14:18:47 +02:00
Bastien Nocera
658c301e3c lib: Use memmove(), g_memmove() is deprecated 2019-08-05 18:12:06 +00:00
Bastien Nocera
dce52ed081 vfs5011: Use memmove(), g_memmove() is deprecated 2019-08-05 18:12:06 +00:00
Bastien Nocera
f309f586c9 ci: Add ABI check
Last ABI break was when we fixed the return value for fp_get_pollfds()
in commit 056ea54.
2019-08-05 20:05:13 +02:00
Igor Filatov
ae1b10dba8 elan: Fix frame leak in elan_submit_image 2019-08-05 18:43:04 +02:00
Bastien Nocera
860a256f4b HACKING: Clarify the intent of the license 2019-08-05 13:37:28 +02:00
Bastien Nocera
cb2f46ed08 HACKING: Fix a typo 2019-08-05 13:18:18 +02:00
Bastien Nocera
13deaa66fd lib: Fix a typo 2019-08-05 13:18:15 +02:00
Bastien Nocera
3597a5b0ed img: Fix a typo 2019-08-05 13:17:55 +02:00
Bastien Nocera
0352995cb3 data: Fix a number of typos 2019-08-05 13:17:33 +02:00
Bastien Nocera
e9041da7f4 uru4000: Fix a typo 2019-08-05 13:17:30 +02:00
Bastien Nocera
252180e088 upektc: Fix a typo 2019-08-05 13:17:08 +02:00
Bastien Nocera
6361c208bd upeksonly: Fix a number of typos 2019-08-05 13:16:18 +02:00
Bastien Nocera
2ef8ace543 etes603: Fix a typo 2019-08-05 13:16:14 +02:00
Bastien Nocera
0400bcc85e vfs*: Fix a number of typos 2019-08-05 13:13:10 +02:00
Bastien Nocera
76db6a5a16 aes*: Fix a number of typos 2019-08-05 13:12:45 +02:00
Bastien Nocera
5b171f9577 Add code of conduct document 2019-07-25 12:04:39 +02:00
Benjamin Berg
4cec28416e lib: Remove state from fp_img_driver activate handler
The state was always AWAIT_FINGER and it was never used by any driver
(except for error checking). So remove it, in particular as a correct
state change will be done after activation anyway.

The only driver with code that actually did anything based on this was
the URU4000 driver. However, all it did was an explicit state change
execution. This is not necessary, as the state_change handler is called
anyway (i.e. we now only write the AWAIT_FINGER register once rather
than twice).

Manual changes plus:

@ init @
identifier driver_name;
identifier activate_func;
@@
struct fp_img_driver driver_name = {
    ...,
    .activate = activate_func,
    ...,
};
@ remove_arg @
identifier dev;
identifier state;
identifier init.activate_func;
@@
activate_func (
	struct fp_img_dev *dev
-	, enum fp_imgdev_state state
	)
{
	<...
-	if (state != IMGDEV_STATE_AWAIT_FINGER_ON) { ... }
	...>
}
2019-06-18 18:19:38 +02:00
Benjamin Berg
3b32baccf6 fdu2000: Remove driver as it has been defunct for long
The driver was never ported to the new asynchronous model, meaning it
has been defunct since some time in 2008. Remove the driver, as
seemingly no one has complained about this and we have no proper way to
even verify a port is correct.
2019-06-18 15:54:57 +00:00
Benjamin Berg
16875d7776 examples: Port enroll and verify examples to new storage
This ports the enroll and verify examples to the new storage so that
they do not need any deprecated API anymore.
2019-06-13 13:12:15 +00:00
Benjamin Berg
a9600e23a1 examples: Link examples to the new GVariant based storage
For now just compile and link it, we do not yet use the new storage
code.
2019-06-13 13:12:15 +00:00
Benjamin Berg
a4b6813ebf examples: Add simple storage implementation using GVariant
This is useful so that the enroll and verify examples will not use the
deprecated API anymore.
2019-06-13 13:12:15 +00:00
Benjamin Berg
ef90938eb9 build: Bump GLib dependency to 2.50 and add guards
libfprint already uses G_DEBUG_HERE in a lot of places which requires
GLib 2.50. Also add the appropriate defines so that usage of newer API
will result in warnings.
2019-06-13 13:56:35 +02:00
Benjamin Berg
66891274a7 build: Remove header files from nbis_sources
There is no need to list them in the sources.
2019-06-12 16:10:04 +02:00
Benjamin Berg
f52276bd06 build: Remove header files from libfprint_sources
There is no need to list them in the sources.
2019-06-12 16:07:44 +02:00
Benjamin Berg
7dce8dbfaa build: Remove header files from drivers_sources
It is not necessary to list all the headers in the drivers_sources list,
so remove them.
2019-06-12 16:07:08 +02:00
Benjamin Berg
3b757ee738 build: Fix source files of upekts and upketc drivers
The upekts driver needs upek_proto.c while the upektc driver does not.
Move the corresponding source file entries so that both drivers compile
standalone.
2019-06-12 16:07:05 +02:00
Benjamin Berg
0a45ed7af6 data: Deprecate print storage API
The only API user currently seems to be the examples. fprintd has its
own storage and that will be a good idea in general.

So deprecate the API, we'll need to find a different solution for the
examples eventually.
2019-06-11 18:23:56 +02:00
Benjamin Berg
1db2dc3f58 core: Add guards to public API
Add appropriate g_return_val_if_fail macros to all public API functions
to guard against NULL pointers being passed into libfprint.
2019-06-11 18:23:56 +02:00
Benjamin Berg
953c75575b poll: Remove fpi_timeout_cancel_for_dev
The function was committed by accident as part of commit d18e1053
(lib: Add a way to name timeouts). It is not used anywhere and
fpi_timeout_cancel_all_for_dev exists, is exported and used and serves
the same purpose.
2019-06-11 16:00:19 +02:00
Dave
8c7ff259af elan: Add 04f3:0c42 to the supported devices
Now that all the quirks are in place to support it.
2019-06-06 12:19:31 +02:00
Dave
3e666130c2 elan: Skip more final frames to avoid bulging captures
If users put their finger on the sensor between the bulge and
"un-bulge" area first and then swipe, the captured image would
be bad.

Skipping more frames can reduce the impact, so bump
ELAN_SKIP_LAST_FRAMES to 2.
2019-06-06 12:19:31 +02:00
Dave
2babfa0625 elan: Simplify calibration check for ELAN_0C42
Check for the mean calibration being outside of range to know whether we
require a recalibration. Continue with the usual checks if the
calibration value is within range.
2019-06-06 12:19:31 +02:00
Dave
83af40679a elan: ELAN_0C42 always supports calibration
Split off calibration support checks into elan_supports_calibration()
2019-06-06 12:19:31 +02:00
Dave
ce31c1d704 elan: Work-around one-byte responses being two-bytes long
On the ELAN_0C42 device, one-byte responses are 2 bytes long.
Adapt our expected response length.
2019-06-06 12:19:31 +02:00
Dave
b20a74a22c elan: Work-around sensors returning incorrect dimensions
The dimensions some sensors return is the maximum zero-based index
rather than the number of pixels. Assuming every sensor has an
even number of pixels is safe.
2019-06-06 12:19:31 +02:00
Dave
66461e4c81 elan: Add quirk definition for device 04f3:0c42 2019-06-06 12:19:07 +02:00
Peter Maatman
6ba9439bbb examples: Remove sleep call in enroll examples
The call to sleep(1) inside of the enrollment loop caused a crash
on at least the etes603 driver.

Because in fp_enroll_finger_img the function enters an event
handling loop. This loop needs to start before the next libusb
event timeout. Which would not happen in the etes603 driver
because the timeout there was set to 1 second as well.
2019-05-14 11:38:33 +02:00
Peter Maatman
6764ec79ae etes603: Prevent hang during enroll process
This commit fixes a hang in gnome-settings when trying to enroll a finger.

The same issue could be seen in the enroll example. Previously the enroll
example would hang on "deactivating" because at some point dev->is_active
is set to false and m_exit_start is never called.
2019-05-14 11:37:19 +02:00
Diego
6f4c378933 vfs5011: Add support for Lenovo Preferred Pro Keyboard (KUF1256)
Add support for the Lenovo Preferred Pro USB Fingerprint Keyboard KUF1256
by declaring support for USB ID 138a:0015.

Closes: #125
2019-05-06 09:55:40 +02:00
Bastien Nocera
b121fa2cc9 uru4000: Work-around SELinux AVC warnings when driver starts
Work-around SELinux AVC warnings caused by p11-kit (which is an NSS
dependency) trying to load the root user's p11-kit configs. We disable
this feature using the P11_KIT_NO_USER_CONFIG envvar.

See https://bugzilla.redhat.com/show_bug.cgi?id=1688583
2019-03-21 16:54:06 +01:00
Seong-Joong Kim
ca26e85fd4 uru4000: Fix integer overflow in imaging_run_state()
‘img->key_number’ variable is originally from the device through bulk
endpoint of USB. The variable is immediately assigned to ‘buf[0]’ for
sending to control endpoint of the device. Here, integer overflow may
occur when the ‘img->key_number’ attempts to assign a value that is
outside of type range of ‘char’ to the ‘buf[0]’
2019-02-21 19:06:06 +09:00
Bastien Nocera
0714380360 build: Disable GTK+ demo app by default
Given that it's not usable yet.
2019-01-21 14:38:35 +00:00
Bastien Nocera
b9af7952a4 demo: Fix crash when there are no supported devices
Handle the empty list output from fp_discover_devs() when there are no
supported devices.

Closes: #146
2019-01-21 14:38:35 +00:00
Bastien Nocera
ea6820ed51 lib: Better docs for no devices case in fp_discover_devs()
Document the behaviour of fp_discover_devs() when there are no supported
devices, rather than errors listing the devices.
2019-01-21 14:38:35 +00:00
Bastien Nocera
a1e46de462 lib: Fix examples not working
Remove the sanity check added in commit b1afa9d, having a poll setup
isn't necessary to use timeouts, as long as only sync functions are
used.

Closes: #139
2018-12-30 16:35:33 +01:00
Bastien Nocera
1abe213844 0.99.0 2018-12-14 13:18:08 +01:00
Bastien Nocera
0372ae8ba5 mindtct: Check for multiplication overflow in alloc_power_stats()
Assert if any of the multiplications, which are then used to allocate
memory, would overflow.

Closes: #91
2018-12-13 15:48:25 +01:00
Bastien Nocera
a1e69a0e9d mindtct: Check for multiplication overflow in morph_TF_map()
Assert if any of the multiplications, which are then used to allocate
memory and run a loop, would overflow.

Closes: #92
2018-12-13 15:48:25 +01:00
Bastien Nocera
49e1e98914 mindtct: Check for multiplication overflow in pixelize_map()
Assert if any of the multiplications, which are then used to allocate
memory, would overflow.

Closes: #96
2018-12-13 15:48:25 +01:00
Bastien Nocera
34b316d7d5 mindtct: Check for multiplication overflow in allocate_contour()
Assert if any of the multiplications, which are then used to allocate
memory, would overflow.

Closes: #97
2018-12-13 15:48:25 +01:00
Bastien Nocera
9abc6791c7 mindtct: Check for multiplication overflow in gen_initial_maps()
Assert if any of the multiplications, which are then used to allocate
memory, would overflow.

Closes: #99
2018-12-13 15:48:25 +01:00
Bastien Nocera
5459823667 mindtct: Check for multiplication overflow in interpolate_direction_map()
Assert if any of the multiplications, which are then used to allocate
memory, would overflow.

Closes: #100
2018-12-13 15:48:25 +01:00
Bastien Nocera
4fa8c5ec47 mindtct: Check for multiplication overflow in gen_high_curve_map()
Assert if any of the multiplications, which are then used to allocate
memory, would overflow.

Closes: #101
2018-12-13 15:48:25 +01:00
Bastien Nocera
239034714f mindtct: Check for multiplication overflow in gen_quality_map()
Assert if any of the multiplications, which are then used to allocate
memory, would overflow.

Closes: #103
2018-12-13 15:48:25 +01:00
Bastien Nocera
8a3a974ea3 lib: Add helper for NBIS copy-paste library
This will allow us to segragate helper functions that we want to use
within NBIS. The first helper will be a macro checking for overflow in
multiplications.
2018-12-13 15:48:25 +01:00
Bastien Nocera
db607c4a6f mindtct: Fix memory leak in error path in find_neighbors()
libfprint/nbis/mindtct/ridges.c:284:13: warning: Potential leak of memory pointed to by 'nbr_list'
            free(nbr_sqr_dists);
            ^~~~
2018-12-13 11:59:24 +01:00
Bastien Nocera
a53f07a8f6 mindtct: Fix memory leak in error path in gen_image_maps()
Free the maps if we're not returning them.

libfprint/nbis/mindtct/maps.c:176:14: warning: Potential leak of memory pointed to by 'direction_map'
      return(ret);
             ^~~
libfprint/nbis/mindtct/maps.c:195:14: warning: Potential leak of memory pointed to by 'low_contrast_map'
      return(ret);
             ^~~
libfprint/nbis/mindtct/maps.c:195:14: warning: Potential leak of memory pointed to by 'low_flow_map'
      return(ret);
             ^~~
2018-12-13 11:33:39 +01:00
Bastien Nocera
c3e996b96c mindtct: Fix memory leak in error path in shape_from_contour()
libfprint/nbis/mindtct/shape.c:263:13: warning: Potential leak of memory pointed to by 'shape'
            fprintf(stderr,
            ^~~~~~~
2018-12-13 11:27:00 +01:00
Bastien Nocera
a218437cf4 mindtct: Fix uninitialised value usage in count_minutia_ridges()
libfprint/nbis/mindtct/ridges.c:153:7: warning: 1st function call argument is an uninitialized value
      free(nbr_list);
      ^~~~~~~~~~~~~~
2018-12-12 17:24:45 +01:00
Bastien Nocera
0add0ca9b1 bozorth3: Remove unused assignments in bz_match_score()
libfprint/nbis/bozorth3/bozorth3.c:704:1: warning: Value stored to 'kx' is never read
kx  = 0;
^     ~
libfprint/nbis/bozorth3/bozorth3.c:820:4: warning: Value stored to 'l' is never read
                        l = 1;
                        ^   ~
2018-12-12 17:17:35 +01:00
Bastien Nocera
eaa4aa964c mindtct: Fix memory leaks in get_centered_contour()
The first contour wasn't freed if we failed on the second contour.

libfprint/nbis/mindtct/contour.c:521:14: warning: Potential leak of memory pointed to by 'half1_ex'
      return(ret);
             ^~~
libfprint/nbis/mindtct/contour.c:521:14: warning: Potential leak of memory pointed to by 'half1_ey'
      return(ret);
             ^~~
libfprint/nbis/mindtct/contour.c:521:14: warning: Potential leak of memory pointed to by 'half1_x'
      return(ret);
             ^~~
libfprint/nbis/mindtct/contour.c:521:14: warning: Potential leak of memory pointed to by 'half1_y'
      return(ret);
             ^~~
2018-12-12 17:14:21 +01:00
Bastien Nocera
74bb899ce2 mindtct: Fix "garbage value" error in dft_dir_powers()
libfprint/nbis/mindtct/dft.c:212:30: warning: The left operand of '*' is a garbage value
      cospart += (rowsums[i] * wave->cos[i]);
                  ~~~~~~~~~~ ^
2018-12-12 17:06:36 +01:00
Bastien Nocera
69fe7a1b8c elan: Fix "garbage value" errors in elan_cmd_cb()
libfprint/drivers/elan.c:351:4: warning: 2nd function call argument is an uninitialized value
                        dbg_buf(elandev->last_read, transfer->actual_length);
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
libfprint/drivers/elan.c:46:5: note: expanded from macro 'dbg_buf'
    fp_dbg("%02x", buf[0]);                                  \
    ^~~~~~~~~~~~~~~~~~~~~~
../../../../../../Projects/jhbuild/libfprint/libfprint/fpi-log.h:52:16: note: expanded from macro 'fp_dbg'
 #define fp_dbg g_debug
               ^
libfprint/drivers/elan.c:351:4: warning: The left operand of '<<' is a garbage value
                        dbg_buf(elandev->last_read, transfer->actual_length);
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
libfprint/drivers/elan.c:48:27: note: expanded from macro 'dbg_buf'
    fp_dbg("%04x", buf[0] << 8 | buf[1]);                    \
                   ~~~~~~ ^
libfprint/drivers/elan.c:351:4: warning: The left operand of '<<' is a garbage value
                        dbg_buf(elandev->last_read, transfer->actual_length);
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
libfprint/drivers/elan.c:50:41: note: expanded from macro 'dbg_buf'
    fp_dbg("%04x... (%d bytes)", buf[0] << 8 | buf[1], len)
                                 ~~~~~~ ^
2018-12-12 16:56:33 +01:00
Bastien Nocera
ce856efa25 elan: Assert on a possible division by zero in elan_need_calibration()
libfprint/drivers/elan.c:598:10: warning: Division by zero
        bg_mean /= frame_size;
        ~~~~~~~~^~~~~~~~~~~~~
2018-12-12 16:30:26 +01:00
Bastien Nocera
b54514df6e elan: Assert on a possible division by zero in elan_process_frame_linear()
libfprint/drivers/elan.c:249:26: warning: Division by zero
                px = (px - min) * 0xff / (max - min);
                     ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
2018-12-12 16:24:05 +01:00
Bastien Nocera
551616c3ab aesx660: Fix memory leak in process_stripe_data()
libfprint/drivers/aesx660.c:292:10: warning: Potential leak of memory pointed to by 'stripe'
                return 0;
                       ^
2018-12-12 16:19:20 +01:00
Bastien Nocera
cec307ce7f aeslib: Fix memory leak in aes_write_regv()
libfprint/drivers/aeslib.c:156:1: warning: Potential leak of memory pointed to by 'wdata'
}
^
2018-12-12 16:17:32 +01:00
Bastien Nocera
e7bc8e03fc upeksonly: Fix typos in upeksonly_get_deviation2() comment 2018-12-12 16:13:29 +01:00
Bastien Nocera
9a025bde8b upeksonly: Fix possible division by zero in upeksonly_get_deviation2()
By asserting if we have a zero line width.

libfprint/drivers/upeksonly.c:118:7: warning: Division by zero
        mean /= (ctx->line_width / 2);
        ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
2018-12-12 16:13:15 +01:00
Bastien Nocera
6845e400cd uru4000: Fix memory leak on error in dev_init()
libfprint/drivers/uru4000.c:1357:3: warning: Potential leak of memory pointed to by 'urudev'
                fp_err("could not get encryption slot");
                ^~~~~~
2018-12-12 16:06:03 +01:00
Bastien Nocera
25d0fa42e2 uru4000: Fix unused value in sm_read_regs()
We don't need to assign urudev if we only want the size of one of its
members.

libfprint/drivers/uru4000.c:554:20: warning: Value stored to 'urudev' during its initialization is never read
        struct uru4k_dev *urudev = FP_INSTANCE_DATA(FP_DEV(dev));
                          ^~~~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2018-12-12 16:04:35 +01:00
Bastien Nocera
52208a6606 lib: Fix baroque code in fp_discover_prints()
Use GPtrArray to simplify the implementation.

libfprint/fpi-data.c:777:13: warning: Access to field 'data' results in a dereference of a null pointer (loaded from variable 'elem')
                list[i] = elem->data;
                          ^~~~~~~~~~
2018-12-12 15:47:29 +01:00
Bastien Nocera
6725b22fd4 lib: Fix uninitialised value in fp_print_data_load()
libfprint/fpi-data.c:497:7: warning: 2nd function call argument is an uninitialized value
        if (!fp_dev_supports_print_data(dev, fdata)) {
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2018-12-12 15:47:29 +01:00
Bastien Nocera
21bac43015 lib: Fix memory leak on error in fp_print_data_save()
libfprint/fpi-data.c:393:3: warning: Potential leak of memory pointed to by 'buf'
                fp_err("couldn't create storage directory");
                ^~~~~~
libfprint/fpi-log.h:75:16: note: expanded from macro 'fp_err'
 #define fp_err g_warning
               ^~~~~~~~~
2018-12-12 15:47:29 +01:00
Bastien Nocera
0ddd11f81b lib: Fix unintialised variable warning in fp_get_next_timeout()
libfprint/fpi-poll.c:398:11: warning: The left operand of '==' is a garbage value
        else if (timercmp(&fprint_timeout, &libusb_timeout, <))
                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/sys/time.h:162:17: note: expanded from macro 'timercmp'
  (((a)->tv_sec == (b)->tv_sec) ?                                             \
    ~~~~~~~~~~~ ^
2018-12-12 15:47:29 +01:00
Bastien Nocera
6d47c44335 lib: Fix possible dereference in fpi_ssm_next_state()
libfprint/fpi-ssm.c:244:9: warning: Access to field 'completed' results in a dereference of a null pointer (loaded from variable 'machine')
        BUG_ON(machine->completed);
               ^~~~~~~~~~~~~~~~~~
libfprint/fpi-log.h:84:6: note: expanded from macro 'BUG_ON'
        if (condition) {                        \
            ^~~~~~~~~
2018-12-12 15:47:29 +01:00
Bastien Nocera
3cbc908a6e lib: Add better guard against huge malloc
See dda6857fee
and https://bugzilla.redhat.com/show_bug.cgi?id=1656518

When the number of lines to assemble is 1, the median_filter() function
would be passed -1 as its size as it was calculated with:
(num_lines / 2) - 1
so (1 / 2) - 1 = 0 - 1 = -1

Add a guard to stop drivers trying to assemble single lines. This
doesn't however fix the vfs5011 driver that tried to do that.
2018-12-06 10:58:35 +01:00
Bastien Nocera
95b1e75f1b Revert "ci: Disable Flatpak generation for now"
This reverts commit bcfe0ad12c.
2018-12-04 17:43:19 +01:00
Bastien Nocera
bcfe0ad12c ci: Disable Flatpak generation for now
It's currently broken on our CI runners, we'll need to reactivate
it again in the future.
2018-12-04 17:09:00 +01:00
Vasily Khoruzhick
2088a6177f lib: Document assembling functions 2018-12-03 16:06:53 +01:00
Vasily Khoruzhick
c9733d27df lib: Add API docs for fpi_img comparison functions 2018-12-03 14:22:12 +01:00
Bastien Nocera
dd24cf57c6 README: Add links to historical resources 2018-11-30 17:57:40 +01:00
Bastien Nocera
451a4c0969 lib: Remove duplicated structs from fp_internal.h
They are already declared in fpi-core.h. Also move their API docs
to fpi-core.h.
2018-11-30 16:08:42 +01:00
Bastien Nocera
dabcae7b4d lib: Document fpi_imgdev_*() functions 2018-11-29 17:32:37 +01:00
Bastien Nocera
eae5721f8e vfs101: Add FIXME about fpi_imgdev_set_action_result usage
We shouldn't need to use fpi_imgdev_set_action_result(), because
as fpi_imgdev_image_captured() is already there.
2018-11-29 17:32:37 +01:00
Bastien Nocera
ce1a675dd5 lib: Reword "use the functions below" docs
Those usually appear in struct definitions which will be at the bottom
of the docs, so there's nothing but the bottom of the page below.
2018-11-29 17:32:37 +01:00
Bastien Nocera
6ba1016024 lib: Hide struct fp_minutiae from the docs
It's only used internally.
2018-11-29 17:32:37 +01:00
Bastien Nocera
b5f175b78e lib: Move action state functions to correct C file 2018-11-29 17:32:37 +01:00
Bastien Nocera
aff7efd891 lib: Remove upekts used headers from the docs
They're only used by this super quirky driver that stores data
internally.

See #127
2018-11-29 17:32:37 +01:00
Bastien Nocera
1d00c2d4b4 lib: Make 6 async reporting funcs private
As those aren't used in any driver.
2018-11-29 17:32:37 +01:00
Bastien Nocera
61483a4c47 lib: Make the fp_print_data structure private
And add a couple of helpers for the upekts driver to use.
2018-11-29 17:32:37 +01:00
Bastien Nocera
2b21430abe lib: Fix lack of description for fp_dscv_dev_get_driver_id()
We documented the return value, but without writing out the function
description.
2018-11-29 17:32:36 +01:00
Bastien Nocera
1781ad140e lib: Remove unused fp_imgdev_verify_state enum 2018-11-29 17:32:36 +01:00
Bastien Nocera
d2e3ad4dbc lib: Add API docs for 3 enums 2018-11-29 17:32:36 +01:00
Bastien Nocera
7615aaef7a lib: Document fp_img functions
And fix fpi_img_new_for_imgdev()'s argument names to match between the
header and the C file.
2018-11-29 13:15:03 +01:00
Bastien Nocera
92e3168309 lib: Add documentation for fp_driver_type 2018-11-25 12:42:11 +00:00
Bastien Nocera
ca148cbcf1 lib: Add documentation for usb_id 2018-11-25 12:42:11 +00:00
Bastien Nocera
e57f037a10 lib: Document FP_IMG_DEV
...and add "Returns" docs to fpi-dev.c

We might need to do this more, documenting what the function does in
prose, as well as a short explanation of the returned data.
2018-11-25 12:42:11 +00:00
Bastien Nocera
46ad86fab2 doc: Remove empty "poll" section
In commit 231b8f9, when porting from doxygen to gtk-doc, we created a
section for "poll" functions, but we renamed that section to "events"
during the porting process. We ended up with an empty "poll" section,
and a correctly migrated "events" section.
2018-11-25 12:42:11 +00:00
Bastien Nocera
511e164f17 lib: Add @short_description for all the sections
So gtk-doc doesn't warn about them any more.
2018-11-25 12:42:11 +00:00
Bastien Nocera
6f18fc4bf4 lib: Document all the internal symbols in fpi-dev.c 2018-11-25 12:42:11 +00:00
Bastien Nocera
8ca34303f2 lib: Transform fp_img_driver flags into a FpiImgDriverFlags enum
And document it.
2018-11-25 12:42:11 +00:00
Bastien Nocera
882fc8ab54 lib: Fix sloppy grammar in error output 2018-11-25 12:42:11 +00:00
Bastien Nocera
9e5ae25abf lib: Transform fp_img flags into a FpiImgFlags enum
And document it.
2018-11-25 12:42:11 +00:00
Bastien Nocera
9316dfed2e ci: Generate Flatpak and export it 2018-11-23 15:05:00 +01:00
Bastien Nocera
702932c69b demo: Add Flatpak manifest for the demo application 2018-11-23 15:05:00 +01:00
Bastien Nocera
29461fa910 demo: Add GTK+ test application 2018-11-23 15:05:00 +01:00
Bastien Nocera
f7173e6645 ci: Add GTK+ to the CI 2018-11-20 05:20:55 +01:00
Bastien Nocera
11b11a9d71 lib: Fix get_next_timeout_expiry
If get_next_timeout_expiry() fails, and libusb_get_next_timeout()
has no timeouts or failed, fprint_timeout will not be set, and
we cannot compare it to libusb_timeout.

Exit early if both failed or have empty queues.
2018-11-13 11:54:41 +01:00
Bastien Nocera
9da69dfc36 elan: Fix format mismatch warnings in debug output
libfprint/drivers/elan.c:351:12: warning: format specifies type 'unsigned short' but the argument has type 'unsigned char' [-Wformat]
                        dbg_buf(elandev->last_read, transfer->actual_length);
                        ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
libfprint/drivers/elan.c:46:21: note: expanded from macro 'dbg_buf'
    fp_dbg("%02hx", buf[0]);                                  \
    ~~~~~~~~~~~~~~~~^~~~~~~
include/glib-2.0/glib/gmessages.h:345:32: note: expanded from macro 'g_debug'
                               __VA_ARGS__)
                               ^~~~~~~~~~~
libfprint/drivers/elan.c:351:12: warning: format specifies type 'unsigned short' but the argument has type 'int' [-Wformat]
                        dbg_buf(elandev->last_read, transfer->actual_length);
                        ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
libfprint/drivers/elan.c:48:21: note: expanded from macro 'dbg_buf'
    fp_dbg("%04hx", buf[0] << 8 | buf[1]);                    \
    ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
include/glib-2.0/glib/gmessages.h:345:32: note: expanded from macro 'g_debug'
                               __VA_ARGS__)
                               ^~~~~~~~~~~
libfprint/drivers/elan.c:351:12: warning: format specifies type 'unsigned short' but the argument has type 'int' [-Wformat]
                        dbg_buf(elandev->last_read, transfer->actual_length);
                        ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
libfprint/drivers/elan.c:50:35: note: expanded from macro 'dbg_buf'
    fp_dbg("%04hx... (%d bytes)", buf[0] << 8 | buf[1], len)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
include/glib-2.0/glib/gmessages.h:345:32: note: expanded from macro 'g_debug'
                               __VA_ARGS__)
                               ^~~~~~~~~~~
libfprint/drivers/elan.c:413:10: warning: format specifies type 'unsigned short' but the argument has type 'unsigned char' [-Wformat]
        dbg_buf(cmd->cmd, 2);
        ~~~~~~~~^~~~~~~~~~~~
libfprint/drivers/elan.c:46:21: note: expanded from macro 'dbg_buf'
    fp_dbg("%02hx", buf[0]);                                  \
    ~~~~~~~~~~~~~~~~^~~~~~~
include/glib-2.0/glib/gmessages.h:345:32: note: expanded from macro 'g_debug'
                               __VA_ARGS__)
                               ^~~~~~~~~~~
libfprint/drivers/elan.c:413:10: warning: format specifies type 'unsigned short' but the argument has type 'int' [-Wformat]
        dbg_buf(cmd->cmd, 2);
        ~~~~~~~~^~~~~~~~~~~~
libfprint/drivers/elan.c:48:21: note: expanded from macro 'dbg_buf'
    fp_dbg("%04hx", buf[0] << 8 | buf[1]);                    \
    ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
include/glib-2.0/glib/gmessages.h:345:32: note: expanded from macro 'g_debug'
                               __VA_ARGS__)
                               ^~~~~~~~~~~
libfprint/drivers/elan.c:413:10: warning: format specifies type 'unsigned short' but the argument has type 'int' [-Wformat]
        dbg_buf(cmd->cmd, 2);
        ~~~~~~~~^~~~~~~~~~~~
libfprint/drivers/elan.c:50:35: note: expanded from macro 'dbg_buf'
    fp_dbg("%04hx... (%d bytes)", buf[0] << 8 | buf[1], len)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
include/glib-2.0/glib/gmessages.h:345:32: note: expanded from macro 'g_debug'
                               __VA_ARGS__)
                               ^~~~~~~~~~~
2018-11-13 11:54:41 +01:00
Bastien Nocera
e5f4021a4f vfs101: Fix warning about value truncation
libfprint/drivers/vfs101.c:854:6: warning: absolute value function 'abs' given an argument of type 'long' but has parameter of type 'int' which may cause truncation of value [-Wabsolute-value]
        if (abs(count - VFS_IMG_BEST_CONTRAST) < abs(vdev->best_clevel - VFS_IMG_BEST_CONTRAST))
            ^
libfprint/drivers/vfs101.c:854:6: note: use function 'labs' instead
        if (abs(count - VFS_IMG_BEST_CONTRAST) < abs(vdev->best_clevel - VFS_IMG_BEST_CONTRAST))
            ^~~
            labs
2018-11-13 11:54:41 +01:00
Bastien Nocera
4dcbc6a3aa vfs101: Fix typo in "VFS_IMG_BEST_CONRAST" constant
It's contrast, says so above the definition.
2018-11-13 11:54:41 +01:00
Bastien Nocera
7a72d8fd58 lib: Fix uninitialised variable in fpi_imgdev_image_captured()
libfprint/fpi-dev-img.c:255:6: warning: variable 'print' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
        if (imgdev->action != IMG_ACTION_CAPTURE) {
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
libfprint/fpi-dev-img.c:271:25: note: uninitialized use occurs here
        imgdev->acquire_data = print;
                               ^~~~~
libfprint/fpi-dev-img.c:255:2: note: remove the 'if' if its condition is always true
        if (imgdev->action != IMG_ACTION_CAPTURE) {
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
libfprint/fpi-dev-img.c:232:29: note: initialize the variable 'print' to silence this warning
        struct fp_print_data *print;
                                   ^
                                    = NULL
2018-11-13 11:54:41 +01:00
Bastien Nocera
5edfd55e00 doc: Fix typo in chapter title 2018-11-12 12:10:11 +01:00
Bastien Nocera
2951daaa01 doc: Split driver docs into chapters 2018-11-09 15:32:39 +01:00
Bastien Nocera
23f7c4aaf7 doc: Hide G_LOG_DOMAIN from gtk-doc
It's only used internally, and doesn't need to be documented here,
as FP_COMPONENT which uses it already is.
2018-11-09 15:32:39 +01:00
Bastien Nocera
3dc5c4a6de doc: Add LIBFPRINT_DEPRECATED to the docs 2018-11-09 15:32:39 +01:00
Bastien Nocera
a97ae3bc35 doc: Add fpi-async section to the docs 2018-11-09 15:32:39 +01:00
Bastien Nocera
53d2fb3ad2 doc: Add more functions to fpi-dev section
And rename section to match that of fpi-dev-img
2018-11-09 15:32:39 +01:00
Bastien Nocera
4f7e507716 doc: Add fpi-dev-img section to the docs 2018-11-09 15:32:39 +01:00
Bastien Nocera
f2e3a840db doc: Add fpi-core and fpi-core-img sections to the docs 2018-11-09 15:32:39 +01:00
Bastien Nocera
f45c18116a doc: Add fpi-data section to the docs 2018-11-09 15:32:39 +01:00
Bastien Nocera
2365c608f9 lib: Change "unsigned" to "unsigned int"
To appease gtk-doc's struct parser, which chokes on the short name for
the data type.

See https://gitlab.gnome.org/GNOME/gtk-doc/issues/63
2018-11-09 14:20:19 +01:00
Bastien Nocera
6c6daaa619 doc: Add fpi-assembling section to the docs 2018-11-09 14:08:04 +01:00
Bastien Nocera
857a399bfa doc: Fix parameter names in fp_minutia_get_coords() docs
The API docs for fp_minutia_get_coords() as added in commit 1006467
had the parameters in the docs not match the actual names used.
2018-11-09 13:58:21 +01:00
Bastien Nocera
6e230f0a07 doc: Add missing fpi_timeout_set_name() to the docs 2018-11-09 13:58:21 +01:00
Bastien Nocera
7402401057 doc: Add fpi-img section to the docs 2018-11-09 13:58:21 +01:00
Bastien Nocera
fe17dfe01d lib: Rename fpi_im_resize() to fpi_img_resize()
Now that this name is free. This makes all the image manipulation
functions have the same "fpi_img_" prefix.
2018-11-09 13:36:52 +01:00
Bastien Nocera
4885b38be5 lib: Rename fpi_img_resize() to fpi_img_realloc()
To better match what it does. It does not resize an image, but
reallocate its internal data structure's size.
2018-11-09 13:35:40 +01:00
Bastien Nocera
5d4a5981d0 lib: Make fpi_poll_is_setup() private
As it's not used in other parts of the library after the changes in
commit 422f81b.
2018-11-09 13:33:02 +01:00
Bastien Nocera
0fcb4533b5 doc: Add more headers to ignore list
Those are not public headers, so don't need to be documented. This
removes 18 symbols from the undocumented symbols list.
2018-11-09 13:29:56 +01:00
Bastien Nocera
422f81b60c Revert "lib: Async functions cannot be started without a mainloop"
This reverts commit 700c5791f8.

We don't need a poll setup to be able to call sync functions, which then
use async functions for implementation internally.

Closes: #124

Conflicts:
	libfprint/fpi-async.c
2018-11-09 12:22:45 +01:00
Bastien Nocera
e0d2f3ae53 lib: Link to fp_enroll_finger_img() in fp_enroll_stage_cb() doc 2018-10-23 11:24:56 +02:00
Bastien Nocera
531d9048b3 lib: Fix up third-person usage in API docs 2018-10-23 11:24:56 +02:00
Bastien Nocera
18495d122d lib: Fix up @dev argument docs 2018-10-23 11:24:56 +02:00
Bastien Nocera
9bcacd97df lib: Require a callback for a number of async calls
Otherwise the caller won't be able to call the appropriate _finish()
calls when done.

See https://gitlab.freedesktop.org/libfprint/libfprint/issues/119 for
the long-term plan
2018-10-23 11:24:56 +02:00
Bastien Nocera
d91eae26e5 lib: Add API docs for the async functions 2018-10-23 11:24:56 +02:00
Bastien Nocera
d113ed9d83 docs: Update API docs style
No period at the end of parameter docs.
2018-10-23 11:24:56 +02:00
Bastien Nocera
e113754312 docs: Add "struct #fp_dev" to the API docs 2018-10-23 11:24:56 +02:00
Bastien Nocera
b1afa9da5d lib: No mainloop, no timeouts either 2018-10-23 10:59:15 +02:00
Bastien Nocera
700c5791f8 lib: Async functions cannot be started without a mainloop
No mainloop, no async.
2018-10-23 10:59:15 +02:00
Bastien Nocera
97d0a6dfe6 lib: Add fpi_poll_is_setup() sanity check
This checks whether polling was correctly setup for integration with a
mainloop.
2018-10-23 10:59:15 +02:00
Bastien Nocera
a54d020c97 lib: Make fp_get_pollfds() fail when fp_init() wasn't called
So things can't be called out-of-order.
2018-10-23 10:59:15 +02:00
Bastien Nocera
36b696f433 lib: Fix error messages in async capture functions
They mentioned verification instead of capture.
2018-10-23 10:59:15 +02:00
Bastien Nocera
f42cd6eefd lib: Add fp_driver_supports_imaging()
So we don't need to open a device before checking whether it supports
imaging.
2018-10-23 10:59:15 +02:00
Bastien Nocera
46ebb39f65 lib: Throw an error if fp_init() wasn't called
You can't call fp_discover_devs() without calling fp_init() and having
it not fail, otherwise there's nothing to discover...
2018-10-23 10:59:15 +02:00
Bastien Nocera
c88a51cce8 build: Refuse to build without any drivers 2018-10-23 10:59:15 +02:00
Bastien Nocera
3a7b03f022 lib: Fix crash when too many minutiae were detected
struct xyt_struct uses a fixed-sized array to fit MAX_BOZORTH_MINUTIAE (200)
minutiae. MAX_FILE_MINUTIAE is 1000. So if we detected more than
MAX_BOZORTH_MINUTIAE, we would crash copying the data from the capture
to the structure.

We might want to use dynamically allocated arrays in the future (or
bigger ones) so that we don't lose minutiae.

Closes: #116
2018-10-12 12:27:37 +02:00
Bastien Nocera
94450a1d74 lib: Fix fpi_img_is_sane()
The checks weren't:
- checking whether the width or height were negative
- whether img->width * img->height would overflow, or
  was bigger than G_MAXINT
- whether img->width * img->height was bigger than the total
  length of the buffer

The last one looks like a thinko, it checked for:
(img->length * img->height) < img->length
which is equivalent to:
img->height < 1
which we already check for earlier.

Closes: #85
2018-10-12 00:54:39 +00:00
Bastien Nocera
c35ad20249 mindtct: Fix a memory leak in morph_TF_map()
cimage is leaked when mimage fails to allocate.

Spotted by Seth Arnold

Closes: #82
2018-10-12 00:54:39 +00:00
Bastien Nocera
5e8b4a81e9 mindtct: Fix leak in pixelize_map()
pmap was not freed in error cases.

Spotted by Seth Arnold

Closes: #83
2018-10-12 00:54:39 +00:00
Bastien Nocera
21a779235d mindtct: Fix powmax_dirs leak on error
A copy/paste error meant we were trying to free a variable we only just
failed to allocate.

Spotted by Seth Arnold

Closes: #81
2018-10-12 00:54:39 +00:00
Bastien Nocera
1006467f33 lib: Add accessor for minutia coordinates
Add fp_minutia_get_coords() so that debugging applications can show the
detected minutiae along with the captured image.

This will also help fix the positively ancient fprint_demo.

See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=907470
2018-10-08 16:50:59 +02:00
Bastien Nocera
cffe49de5c build: Fix "already configured" meson warning 2018-10-08 16:50:59 +02:00
Bastien Nocera
7962d8cdab build: Fix build with no drivers using imaging_dep
Closes: #109
2018-09-28 15:59:45 +02:00
Bastien Nocera
36dba33808 build: Test builds with a single driver with no deps
So that we can be sure that the build doesn't break when those drivers
are not built.
2018-09-28 15:55:56 +02:00
Bastien Nocera
c273908471 lib: Tidy fp_internal.h
Group global variables, structure definitions, and functions, and
group functions by which C source they're defined in.

This also removes the FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE
definition that's already available in the drivers API-accessible
fpi-core.h
2018-09-28 15:12:39 +02:00
Bastien Nocera
bf8661bf45 lib: Include "fpi-dev.h" in drivers header 2018-09-28 15:12:39 +02:00
Bastien Nocera
499b522183 lib: Remove titles from headers
The purpose of the headers is (or will be) explained in more details
inside each one of the matching C files.
2018-09-28 15:12:39 +02:00
Bastien Nocera
13bfe7a65c lib: Move driver definitions to fpi-core.h 2018-09-28 15:12:39 +02:00
Bastien Nocera
e90ec04441 lib: Move fp_minutiae to fpi-img.h 2018-09-28 15:12:39 +02:00
Bastien Nocera
a59a5caaf3 lib: Add missing includes
Headers for size_t and ssize_t, as used in the header itself.
2018-09-28 15:12:39 +02:00
Bastien Nocera
5b9f81fb46 lib: Move fp_dev helpers to fpi-dev.h from fpi-core.h 2018-09-28 15:12:39 +02:00
Bastien Nocera
d3aaebb352 lib: Add include guards to fpi-dev.h 2018-09-28 15:12:39 +02:00
Bastien Nocera
a99f920fe0 lib: Add include guards to fpi-poll.h 2018-09-28 15:12:39 +02:00
Bastien Nocera
fb5527c58c lib: Split off imgdev functions
They now live in fpi-dev-img.[ch]
2018-09-28 15:12:39 +02:00
Bastien Nocera
fcd3c1dd89 lib: Split off core.c drivers API 2018-09-28 15:12:39 +02:00
Bastien Nocera
3a9500be67 lib: Move fpi-img utils to fpi-img's header 2018-09-28 15:12:39 +02:00
Bastien Nocera
1d93b86569 lib: Rename assembling.[ch]
This is already self-contained.
2018-09-28 15:12:39 +02:00
Bastien Nocera
aca2cd41d8 lib: Rename sync.c to fpi-sync.c
All the functions there are public, so no matching header.
2018-09-28 15:12:39 +02:00
Bastien Nocera
1035f733aa lib: Split off async functions
And don't add the new fpi-async.h to the driver imports, it will only
be used by the upekts driver.
2018-09-28 15:12:39 +02:00
Bastien Nocera
2818d94010 lib: Split off fp_img helpers
And rename img.c to fpi-img.c, as well as pixman.c to fpi-img-pixman.c
2018-09-28 15:12:39 +02:00
Bastien Nocera
1d1c34eb60 lib: Make fpi_print_data_item_free() static 2018-09-28 15:12:39 +02:00
Bastien Nocera
ca06fae22e lib: Split off fpi_print_data helpers
And rename data.c to fpi-data.c
2018-09-28 15:12:39 +02:00
Bastien Nocera
36f527269b Revert "lib: Mark fpi_ssm_get_user_data as deprecated"
This reverts commit 1fd82b5162.

This was causing too many warnings at this stage of porting. We'll add
it again when most of the drivers are ported.
2018-09-28 15:12:39 +02:00
Bastien Nocera
7cfe20e07f lib: Fix gtk-doc warning
warning: no link for: "timeval" -> (<code class="literal">timeval</code>)
2018-09-27 16:08:21 +02:00
Bastien Nocera
1fd82b5162 lib: Mark fpi_ssm_get_user_data as deprecated 2018-09-27 16:07:58 +02:00
Bastien Nocera
0ace5f64f8 elan: Fix use-after-free if USB transfer is cancelled 2018-09-27 15:19:00 +02:00
Bastien Nocera
e532524c7e elan: Name our timeouts 2018-09-27 15:17:15 +02:00
Bastien Nocera
ebd96f892e lib: Make fpi_timeout_add() never fail
It will now assert if the monotonic clock isn't available. If that's the
case, we'll have bigger problems anyway.
2018-09-27 15:14:05 +02:00
Bastien Nocera
4b9b34fa4d elan: Use USB helpers 2018-09-27 15:09:59 +02:00
Bastien Nocera
bcc1e7ae73 aes2501: Use USB helpers
Nice little cleanups.
2018-09-27 14:49:50 +02:00
Bastien Nocera
27accf42f2 uru4000: Port URU4000 driver to USB helpers
Doesn't look very useful.
2018-09-27 14:49:50 +02:00
Bastien Nocera
96f2e07cdd lib: Add USB transfer helpers
Those helpers will provide more arguments in their callbacks, and handle
the libusb_transfer lifecycle, making it easier to use for drivers.
2018-09-27 14:49:50 +02:00
Bastien Nocera
192c4f3cfc aesx660: Use constants for buffer sizes 2018-09-27 14:49:50 +02:00
Bastien Nocera
cb274032da aes2501: Use constants for buffer sizes 2018-09-27 14:49:50 +02:00
Bastien Nocera
b890fa56d8 aes1610: Use constants for buffer sizes 2018-09-27 14:49:50 +02:00
Bastien Nocera
b817b46494 lib: Remove home-grown min() implementation
And use GLib's.
2018-09-27 14:49:50 +02:00
Bastien Nocera
49ba59369a aesx660: Remove home-grown min() implementation
And use GLib's.
2018-09-27 14:49:50 +02:00
Bastien Nocera
d09cb88e9a uru4000: Fix typos in comments 2018-09-27 14:49:50 +02:00
Bastien Nocera
44c3f4f772 lib: Cancel pending timeouts on close
So that the drivers don't call back into themselves once freed.
2018-09-27 14:49:50 +02:00
Bastien Nocera
948a67a51f lib: Add internal fpi_timeout_cancel_all_for_dev()
This will be used to disable all timeouts for a device that's about
to be closed, and freed.
2018-09-27 14:49:50 +02:00
Bastien Nocera
de79609550 lib: Use timeout name in debug messages when available 2018-09-27 14:49:41 +02:00
Bastien Nocera
d18e10535e lib: Add a way to name timeouts
To make it easier to print debug information about where timeouts are
coming from.
2018-09-27 14:49:41 +02:00
Bastien Nocera
2954583373 lib: Fix memory leak in fpi_poll_exit()
The active_timers list was freed, but not the elements themselves.
2018-09-27 14:36:06 +02:00
Bastien Nocera
ccdecdea11 lib: Document fpi_ssm_jump_to_state() 2018-09-26 15:57:25 +02:00
Bastien Nocera
946388d1e9 lib: Fix incorrect guard in FP_IMG_DEV()
The check for the DRIVER_IMAGING type was reversed.
2018-09-26 15:53:52 +02:00
Bastien Nocera
10ae8ffb55 drivers: Simplify libusb_alloc_transfer(0) calls
By using our new, never-failing, USB allocation wrapper. As it can never
fail, we can also remove all the error paths for the allocations
failing.
2018-09-19 17:06:17 +02:00
Bastien Nocera
bdba9990fb lib: Add libusb allocation helper 2018-09-19 17:04:55 +02:00
Bastien Nocera
50166e88c0 lib: Add fpi_ssm_next_state_timeout_cb() helper
To be used in 5 drivers.
2018-09-19 15:46:29 +02:00
Bastien Nocera
f2bc826a20 lib: Move aeslib.[ch] to the drivers directory
As it's used by drivers, and isn't a set of generic helpers.
2018-09-19 15:16:40 +02:00
Bastien Nocera
ac48d66203 lib: Mention that fpi_timeout gets freed 2018-09-18 20:02:14 +02:00
Bastien Nocera
f68e7fcb9f lib: Add fp_dev argument to timeout callback
To cut down on the fpi_ssm_get_user_data() usage again.
2018-09-18 20:00:59 +02:00
Bastien Nocera
0c3a22758b drivers: Simplify FP_INSTANCE_DATA(FP_DEV()) calls
When fp_dev is already available, don't use the fp_img_dev struct to get
to fp_dev with FP_DEV(), use the fp_dev directly.
2018-09-18 18:30:42 +02:00
Bastien Nocera
9cc859a318 drivers: Diminish fpi_ssm_get_user_data() usage
Can't remove it yet, as libusb transfers and timeouts don't pass the
fp_dev or fp_img_dev separately.
2018-09-18 18:30:42 +02:00
Bastien Nocera
7dfc8f3364 lib: Remove fpi_ssm_get_dev()
Usually by passing the dev it from an fpi_ssm callback, or simply
using that callback argument.
2018-09-18 14:02:23 +02:00
Bastien Nocera
e397571f83 lib: Add more args to fpi_ssm callbacks
Pass the struct fp_dev and user_data to fpi_ssm callbacks, so that we
might be able to get rid of the fpi_ssm_get_user_data(), and
fpi_ssm_get_dev() as most drivers just get those from the ssm anyway
in their callbacks.
2018-09-18 13:46:39 +02:00
Bastien Nocera
d34d7c26de lib: Remove fpi_ssm_set_user_data()
And pass the user_data in fpi_ssm_new() instead.
2018-09-18 13:45:33 +02:00
Bastien Nocera
77defa1d8b lib: Remove fpi_dev_get_nr_enroll_stages()
It's the same as fp_dev_get_nr_enroll_stages()
2018-09-15 01:23:02 +02:00
Bastien Nocera
5d01497b8b lib: Remove dev_to_img_dev() helper
We can access the img_dev struct member directly, and we don't have
to double check that the img_dev member is actually an img_dev type,
as it always is.
2018-09-15 01:20:59 +02:00
Bastien Nocera
cb8732b255 lib: Remove priv struct member in fp_driver
It's unused.
2018-09-15 01:18:14 +02:00
Bastien Nocera
3f64b2ea5a lib: Remove fpi_imgdev_get_usb_dev()
We now use fpi_get_usb_dev(FP_DEV()) instead.
2018-09-15 01:12:18 +02:00
Bastien Nocera
57b38a2876 lib: Remove fpi_imgdev_get_dev()
This is now handled by FP_DEV()
2018-09-15 01:12:18 +02:00
Bastien Nocera
058f91909d lib: Remove fp_img_dev->priv
And use the new instance data helper.
2018-09-15 01:12:18 +02:00
Bastien Nocera
4f75e63e25 lib: Remove fp_dev->priv
Not precise enough, and it was only used in upekts to get/set the
instance private struct and keep it around, so add a new, more precisely
defined field to fp_dev for that.
2018-09-15 01:12:18 +02:00
Bastien Nocera
2e50ff5e7c lib: Add some TODO items to fp_dev struct 2018-09-15 01:09:50 +02:00
Bastien Nocera
0b7477e72c lib: Use fp_dev to access USB device
Instead of having own own pointer in fp_img_dev
2018-09-15 01:09:50 +02:00
Bastien Nocera
4dc8aa6dfa docs: Add fpi-dev helpers to API docs 2018-09-15 01:09:50 +02:00
Bastien Nocera
161c3ccf1c lib: Link fp_dev to fp_img_dev and back
So it's easy to switch between the 2 structs, and remove fp_img_dev->dev
direct access, as well as fp_dev->priv "user data" usage.
2018-09-15 01:09:39 +02:00
Bastien Nocera
170da7fec1 lib: Move fp_dev definition and helpers to separate header 2018-09-15 01:09:39 +02:00
Bastien Nocera
d7af9a4498 Merge branch 'wip/hadess/fpi-log-docs' into 'master'
Logging API docs

See merge request libfprint/libfprint!16
2018-09-07 10:59:56 +00:00
Bastien Nocera
3cb3b1d63a lib: Add Logging API documentation 2018-09-06 14:44:45 +02:00
Bastien Nocera
fc66919e1f lib: Make BUG_ON() use fp_err()
So that when we change fp_err() to an assertion, BUG_ON() is changed as
well.
2018-09-06 14:42:33 +02:00
Bastien Nocera
de5b4e7dcb lib: Split off logging helpers 2018-09-06 13:24:41 +02:00
Bastien Nocera
561576961c Merge branch 'wip/hadess/fpi-timeout-docs' into 'master'
fpi_timeout API docs

See merge request libfprint/libfprint!15
2018-09-05 15:53:15 +00:00
Bastien Nocera
2946dabd24 lib: Typedef fpi_timeout 2018-09-05 17:49:02 +02:00
Bastien Nocera
969eefc81f lib: Add fpi_timeout API documentation 2018-09-05 17:48:43 +02:00
Bastien Nocera
efee7262b6 lib: Split off timeout helpers
And rename poll.c to fpi-poll.c
2018-09-05 17:15:20 +02:00
Bastien Nocera
1102d6e478 Merge branch 'wip/hadess/fpi-ssm-docs' into 'master'
Add fpi_ssm API docs

See merge request libfprint/libfprint!14
2018-09-05 14:37:09 +00:00
Bastien Nocera
603aab0a81 lib: Rename fpi_ssm_mark_aborted to fpi_ssm_mark_failed 2018-09-05 16:29:26 +02:00
Bastien Nocera
10e934e234 lib: Add fpi_ssm API documentation 2018-09-05 16:25:34 +02:00
Bastien Nocera
48b9e6c517 lib: Typedef fpi_ssm 2018-09-05 15:40:34 +02:00
Bastien Nocera
5ae2ef5407 lib: Split off fpi_ssm functions
Rename drv.c to something more fitting to its contents, and move state
machine code to its own header.
2018-09-05 15:40:01 +02:00
Bastien Nocera
cafa05dab5 doc: Add include to each section rather than globally
So that we can document internal driver APIs and mention the correct
include.
2018-09-05 15:39:05 +02:00
Bastien Nocera
6345835b02 doc: %NULL changes 2018-09-04 15:58:23 +02:00
Bastien Nocera
1d453b5732 doc: Fix warning about fp_capture_result
The enum values need to be documented before the enum itself, otherwise
this warning is thrown:
warning: Value descriptions for fp_capture_result are missing in source code comment block.
2018-09-04 15:57:46 +02:00
Bastien Nocera
d3a1b86ea4 doc: Fix warnings about user_data
Such as:
warning: Parameter description for *::user_data is missing in source code comment block.
2018-09-04 15:56:32 +02:00
Bastien Nocera
bb27ca5720 lib: Work-around gtk-doc's handling of the short type
gtk-doc thinks "short" is only a modifier, not a type of its own (short
== short int). This work-around does not change the ABI.

See https://gitlab.gnome.org/GNOME/gtk-doc/issues/44
2018-09-04 15:45:18 +02:00
Bastien Nocera
065c0b6202 fdu2000: This comment is not API docs
So format it in a way that gtk-doc doesn't think it's API docs.
2018-09-04 15:44:26 +02:00
Bastien Nocera
b4819701b4 doc: Fix warning about fp_print_data
It was listed twice in the sections declaration.
2018-09-04 15:42:57 +02:00
Bastien Nocera
daed05755a lib: Remove unused fp_imgdev_verify_state from drivers API 2018-09-04 14:46:25 +02:00
Bastien Nocera
fb192f706c lib: Remove unused fp_dev_state from drivers API 2018-09-04 14:45:49 +02:00
Bastien Nocera
350b0a38e2 drivers: De-duplicate udf_crc() helper
Was in both the upektc_img and upekts drivers
2018-09-04 14:05:37 +02:00
Bastien Nocera
9ccbda4a40 Merge branch 'wip/hadess/update-nbis-5-0-0' into 'master'
Update to NBIS 5.0.0

See merge request libfprint/libfprint!12
2018-08-28 09:35:46 +00:00
Bastien Nocera
821cbae187 loop: Remove unused variable
mindtct/loop.c: In function ‘process_loop_V2’:
mindtct/loop.c:713:8: warning: variable ‘halfway’ set but not used [-Wunused-but-set-variable]
    int halfway;
        ^~~~~~~
2018-08-27 20:01:00 +02:00
Bastien Nocera
528f3556ea vfs301: Use vfs301_proto_deinit()
Even though it does nothing, make sure to use it so it's useful once it
does something.
2018-08-27 20:01:00 +02:00
Bastien Nocera
489afea605 lib: Remove unused fpi_dev_get_dev_state()
This internal-only function was never used.
2018-08-27 20:01:00 +02:00
Bastien Nocera
688a133f3f morph: Fix misleading indentation
mindtct/morph.c: In function ‘get_south8_2’:
mindtct/morph.c:173:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
    if (row >= ih-1) /* catch case where image is undefined southwards   */
    ^~
mindtct/morph.c:176:7: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
       return *(ptr+iw);
       ^~~~~~
mindtct/morph.c: In function ‘get_north8_2’:
mindtct/morph.c:197:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
    if (row < 1)     /* catch case where image is undefined northwards   */
    ^~
mindtct/morph.c:200:7: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
       return *(ptr-iw);
       ^~~~~~
mindtct/morph.c: In function ‘get_east8_2’:
mindtct/morph.c:221:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
    if (col >= iw-1) /* catch case where image is undefined eastwards    */
    ^~
mindtct/morph.c:224:7: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
       return *(ptr+ 1);
       ^~~~~~
mindtct/morph.c: In function ‘get_west8_2’:
mindtct/morph.c:243:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
    if (col < 1)     /* catch case where image is undefined westwards     */
    ^~
mindtct/morph.c:246:7: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
       return *(ptr- 1);
       ^~~~~~
2018-08-27 20:01:00 +02:00
Bastien Nocera
0cd37b0142 block: Remove unused variable
mindtct/block.c: In function ‘block_offsets’:
mindtct/block.c:108:18: warning: variable ‘ph’ set but not used [-Wunused-but-set-variable]
    int pad2, pw, ph;
                  ^~
2018-08-27 20:01:00 +02:00
Bastien Nocera
9fb789dc78 nbis: Update to NBIS 5.0.0
This updates NBIS to its latest 5.0.0 version, dated 04/03/2015, from a
1.x version, dated 2007.

Original sources are available at:
https://www.nist.gov/itl/iad/image-group/products-and-services/image-group-open-source-server-nigos#Releases

And full change log at:
https://www.nist.gov/sites/default/files/documents/2016/12/14/changelog.txt
2018-08-27 20:01:00 +02:00
Bastien Nocera
770444af55 nbis: Add script to update NBIS and apply changes
NBIS is pretty complicated to update, seeing as we've made quite a few
changes to get it to compile as a library. With those scripts, we can
easily trim headers to remove functions we don't use, rename global
variables, and do any sort of fixups that are necessary right now.

In the future, removing unused NBIS functions might be as easy as
updating that script, re-running it, and pushing the changes.

Note that remove-function.lua is a very crude parser that only supports
NBIS' style of declaration, with the return type on the same line as the
function name. I wouldn't recommend trying to use it in another project.

Callcatcher (https://github.com/caolanm/callcatcher) was also used to
remove additional unused functions.
2018-08-27 20:01:00 +02:00
Bastien Nocera
703d9b2a07 img: Remove remove_perimeter_pts setting
It's gone in NBIS 5.0.0, and will need to be replaced after the update.
2018-08-24 16:11:07 +02:00
Bastien Nocera
a96509f286 Merge branch 'iafilatov/libfprint-elan-prepare-merge' into 'master'
elan: Send stop cmd and recalibrate after each capture

See merge request libfprint/libfprint!11
2018-08-10 12:36:01 +00:00
Igor Filatov
8553f2e41c elan: Send stop cmd and recalibrate after each capture
This seems to fix the lock-up issue with 0903
2018-08-10 14:21:36 +02:00
Bastien Nocera
56c96dde8b Merge branch 'wip/hadess/elan-update' into 'master'
Update Elan driver

See merge request libfprint/libfprint!10
2018-08-10 12:06:35 +00:00
Bastien Nocera
9793d60c5a elan: Remove unused string.h include 2018-08-10 13:45:29 +02:00
Igor Filatov
4ff97e7cbd elan: Increase bz3 threshold to 24
Based on experience. Values more than 24 seem to work just after
enrollment, but it becomes very hard to verify in a day or so.
2018-08-10 13:45:29 +02:00
Igor Filatov
75fe328f64 elan: Don't rotate frames on 0x0c03
There has been a report that a 0x0c03 was installed straight (other
readers so far have produced images that are 90 degrees rotated). There
is no way for the dirver to know how a device is installed, so for now just
make an exception.
2018-08-10 13:45:29 +02:00
Igor Filatov
b28b006d61 elan: Use IMGDEV_STATE_* events instead of pre-set capture path 2018-08-10 13:45:29 +02:00
Igor Filatov
c3b1c982bc elan: Set max frame height to 50, add note about performance
Frame height is a loosely experience-backed guesstimation. 50 seems to be
a good middle ground between swipe speed and quality.
2018-08-10 13:45:29 +02:00
Igor Filatov
b098399bbc elan: Support 0x0c01~0x0c33 2018-08-10 13:45:29 +02:00
Igor Filatov
65bbdff3fc elan: Stop doing sensor reset, change calibration and frame logic
Sensor reset code has been removed because it is not needed during normal
operation.

Calibration and frame processing logic has been improved according to
recommendations from Elantech.
2018-08-10 13:45:29 +02:00
Igor Filatov
fba3e682ea elan: Remove supported devices from udev whitelist 2018-08-09 14:45:15 +02:00
Igor Filatov
bccff4ffef elan: Change command structs, support more devices
Make each command a separate struct to get finer control over which
commands are called on which devices. Update ssm's accordingly. Add
sensor_reset and fuse_load commands.

Support 0x0903, 0x0c03, 0x0c16, 0x0c1a, 0x0c26
2018-08-09 14:42:25 +02:00
Bastien Nocera
9843c2d764 Merge branch 'wip/hadess/gtk-doc-meson-regression' into 'master'
build: Fix build with newer meson

See merge request libfprint/libfprint!9
2018-07-20 10:36:28 +00:00
Bastien Nocera
c9abbec48a build: Fix build with newer meson
The gtk-doc meson helper regressed in that it didn't expect the
libfprint.types file to not exist, as earlier versions did. Remove
the --rebuild-types call so that it is not generated, and meson carries
on compiling the rest of the library.

See https://gitlab.gnome.org/GNOME/gtk-doc/issues/48
See https://github.com/mesonbuild/meson/issues/3892
See https://bugzilla.redhat.com/show_bug.cgi?id=1604585
2018-07-20 12:22:50 +02:00
Bastien Nocera
bc79f9504b Merge branch 'wip/hadess/more-string-h' into 'master'
lib: Include more standard headers for drivers to use

See merge request libfprint/libfprint!8
2018-07-18 09:08:41 +00:00
Bastien Nocera
626b0f1a8f lib: Include more standard headers for drivers to use
Such as string.h and stdlib.h, to be able to compile on older
toolchains like that of Fedora 27.
2018-07-17 16:14:09 +02:00
Bastien Nocera
da95af0f48 0.8.2 2018-07-17 13:48:13 +02:00
Bastien Nocera
28b6f643d8 Merge branch 'wip/hadess/downgrade-assert-2' into 'master'
Another assert downgrade

See merge request libfprint/libfprint!7
2018-07-17 11:47:10 +00:00
Bastien Nocera
22277c7277 build: Downgrade meson in CI script
It failed to finish the build otherwise:
https://github.com/mesonbuild/meson/issues/3892
2018-07-17 13:43:00 +02:00
Bastien Nocera
19e7b217c1 lib: Downgrade fp_err() to be non-fatal
Similarly to b1ac865abd, downgrade
fp_err() to be non-fatal. A number of drivers would spit out an error
when encountering this call, but not crash, carry on and most of the
time recover.

Make sure we don't assert in those cases.
2018-07-16 16:08:54 +02:00
Bastien Nocera
29d3541b74 lib: Downgrade BUG* assertions to work-around crashes #2
Same as b1ac865abd but for the drivers
API.
2018-07-16 16:07:25 +02:00
Bastien Nocera
b9e5b3a55c Merge branch 'wip/hadess/downgrade-assert' into 'master'
lib: Downgrade BUG* assertions to work-around crashes

Closes #77

See merge request libfprint/libfprint!6
2018-07-03 09:44:56 +00:00
Bastien Nocera
b1ac865abd lib: Downgrade BUG* assertions to work-around crashes
BUG() and BUG_ON() didn't use to assert, but only print an error if
debugging was enabled. This was hiding a lot of state bugs in drivers,
and transforming those into assertions causes crashes.

Downgrade the assertion to only print a warning, and hope that those
eventually get fixed in the drivers so we can re-enable them.

Closes: #77
2018-07-03 11:40:25 +02:00
Bastien Nocera
21504c0621 Merge branch 'wip/hadess/poll-retval' into 'master'
poll: Fix fp_get_pollfds retval type

See merge request libfprint/libfprint!5
2018-06-26 13:18:40 +00:00
Bastien Nocera
056ea541dd poll: Fix fp_get_pollfds retval type
fp_get_pollfds() is supposed to return a negative value on failure, but
size_t is an unsigned integer. Use ssize_t instead.
2018-06-26 14:53:53 +02:00
Bastien Nocera
871fddf5fb Merge branch 'wip/hadess/more-upekts-statuses' into 'master'
upekts: Fix enrollment never finishing on some upekts devices

See merge request libfprint/libfprint!3
2018-06-20 17:16:08 +00:00
Bastien Nocera
c284858d06 upekts: Fix enrollment never finishing on some upekts devices
Add support for more device status codes, brought back from the
now-removed UPEKE2 driver.

See 3bf55a3e07/libfprint/drivers/upeke2.c (L1013)
2018-06-19 11:11:04 +02:00
Bastien Nocera
bb4d888661 elan: Fix typo in comment 2018-06-18 18:07:24 +02:00
Bastien Nocera
ff77cfc9b5 doc: Update more website links 2018-06-18 14:56:40 +02:00
Bastien Nocera
80f91ecb7b Merge branch 'master' into 'master'
elan: Add USB ID for TNP Nano USB Fingerprint Reader (04f3:0c26)

See merge request libfprint/libfprint!1
2018-06-13 20:09:47 +00:00
Corentin Noël
6cb77465ab elan: Add USB ID for TNP Nano USB Fingerprint Reader (04f3:0c26)
The device works without any additional changes to the driver.

https://www.amazon.co.uk/gp/product/B075955HY7/
2018-06-13 20:22:29 +01:00
Bastien Nocera
6f6127cbb6 0.8.1 2018-06-12 16:05:45 +02:00
Bastien Nocera
e1d85fb636 build: Install udev rules in correct directory
The udevdir variable in udev.pc might not have a trailing slash.
2018-06-12 16:03:34 +02:00
Bastien Nocera
74b5c92787 0.8.0 2018-06-12 15:42:22 +02:00
Bastien Nocera
ac1f97e2eb lib: Supported devices list is for master, not stable 2018-06-12 12:46:19 +02:00
Bastien Nocera
40f486b108 HACKING: Update hacking file 2018-06-12 11:06:26 +02:00
Bastien Nocera
b62e67401c lib: Fix supported devices page title again
We need to call setlocale() so we run in UTF-8 mode, and can print that
sweet sweet em dash.
2018-06-08 16:23:35 +02:00
Bastien Nocera
dd0a0134a6 lib: Fix supported devices page title 2018-06-07 17:46:08 +02:00
Bastien Nocera
5e24000799 build: Remove unused meson rule
We're generating the supported-devices.md file in the website CI now.
2018-06-07 17:39:36 +02:00
Bastien Nocera
c5cdfcb120 lib: Sort supported devices list 2018-06-07 17:39:04 +02:00
Bastien Nocera
549a6694d2 build: Disable supported-devices.md generation for now
As added in e5393bf46a

It fails currently.
2018-06-04 16:13:08 +02:00
Bastien Nocera
14e34e1d15 build: Remove ported Makefile.am snippet 2018-06-03 14:41:02 +02:00
Bastien Nocera
e5393bf46a lib: Add script to print MarkDown page of supported devices 2018-06-03 14:40:34 +02:00
Bastien Nocera
878a201bb1 README: Update links to new website 2018-06-03 00:31:05 +02:00
Bastien Nocera
b0e4619e0a docs: Update links to new website 2018-06-03 00:31:05 +02:00
Bastien Nocera
aec65777a7 nbis: Update links to new website 2018-06-03 00:30:10 +02:00
Bastien Nocera
31bad8ddd2 build: Fix fprint.h install destination
Since the port to meson, fprint.h was installing to $includedir instead
of $includedir/libfprint/
2018-05-31 14:35:16 +02:00
Bastien Nocera
79d65c907f build: Add missing X11 deps for the examples 2018-05-31 12:30:48 +02:00
Bastien Nocera
92231d984f build: Add some missing build essentials 2018-05-31 12:23:30 +02:00
Bastien Nocera
2fbc77955e build: Add CI
Barebones, just compiles libfprint.
2018-05-31 12:18:17 +02:00
Bastien Nocera
c91819f551 lib: Remove drv->close absence support in fp_async_dev_close()
The driver will at least need to close its hardware resources, and
free memory, so it must have had one.

This case was never actually used as can be seen from the fact that
we would assert in fpi_drvcb_close_complete() if the state was wrong
but never set it to the expected value.
2018-05-31 11:06:31 +02:00
Bastien Nocera
b3f6ff5a36 lib: Add guard to async functions
To avoid having NULL devices being passed dereferenced.
2018-05-31 11:06:31 +02:00
Bastien Nocera
52f84bee3c vfs5011: Error out when no lines were captured
Which avoids passing zero lines to fpi_assemble_lines()

"gmem.c:130: failed to allocate 18446744073709551612 bytes"

 #3  0x00007fe4f6ef428f in g_log (log_domain=log_domain@entry=0x7fe4f6f3506e "GLib", log_level=log_level@entry=G_LOG_LEVEL_ERROR, format=format@entry=0x7fe4f6f3e610 "%s: failed to allocate %lu bytes") at gmessages.c:1398
 #4  0x00007fe4f6ef2ac4 in g_malloc0 (n_bytes=n_bytes@entry=18446744073709551612) at gmem.c:129
 #5  0x00007fe4f8052020 in median_filter (filtersize=25, size=-1, data=0x0) at assembling.c:309
 #6  fpi_assemble_lines (ctx=ctx@entry=0x7fe4f82ac3c0 <assembling_ctx>, lines=0x0, lines_len=0) at assembling.c:389
 #7  0x00007fe4f805f3db in submit_image (ssm=ssm@entry=0x16c3cba360, data=data@entry=0x16c3cb9cc0) at drivers/vfs5011.c:412

See https://bugzilla.redhat.com/show_bug.cgi?id=1484812

Closes: #42
2018-05-31 11:08:41 +02:00
Bastien Nocera
dda6857fee assembling: Add guards to fpi_assemble_lines()
With the goal of not crashing when we try to malloc MAXINT bytes of RAM.

See https://bugzilla.redhat.com/show_bug.cgi?id=1484812

Closes: #42
2018-05-31 11:06:31 +02:00
Timur Celik
0215483fb3 assembling: Fix assembling of last frame in reverse mode
The last image is always misplaced because the sign of the delta
vector isn't corrected.

This could result in false positives and verification failing.

https://bugs.freedesktop.org/show_bug.cgi?id=105027
2018-05-30 11:17:08 +02:00
Timur Celik
37bb59df13 assembling: Fix assembling of frames for non-reverse stripes
Every frame stores the delta from the previous frame, in reverse mode
it stores the delta to the next frame. This causes images to use the
wrong delta while assembling in forward mode.

The broken assembling in forward mode will create a small error for
linear motion, because the delta of all frames is approximately the
same in this case. But if you move your finger, stop and then continue
moving in a single scan, the misplaced frames should be visible in
the assembled output.

This could result in false positives and verification failing.

https://bugs.freedesktop.org/show_bug.cgi?id=105027
2018-05-30 11:15:17 +02:00
Bastien Nocera
db34837d2d lib: Simplify device discovery
Use GPtrArray instead of open-coding a NULL terminated array.

https://bugs.freedesktop.org/show_bug.cgi?id=106279
2018-05-29 13:45:27 +02:00
Bastien Nocera
ba49677794 vfs0050: Rename "udev" to "usb_dev"
To reduce confusion with possible future udev usage.
2018-05-29 13:37:19 +02:00
Bastien Nocera
1a376c1bfa lib: Remove 2 more functions from the drivers API 2018-05-29 13:34:18 +02:00
Bastien Nocera
ef807d9d0e lib: s/array_n_elements/G_N_ELEMENTS/ 2018-05-29 13:34:18 +02:00
Bastien Nocera
2a4893d946 lib: fp_imgdev_*() functions are only used in drivers 2018-05-29 13:34:18 +02:00
Bastien Nocera
cee061b363 lib: fpi_ssm_start_subsm() is only used in drivers 2018-05-29 13:34:18 +02:00
Bastien Nocera
1bbdf304ab lib: Remove never defined fpi_ssm_has_completed() 2018-05-29 13:34:18 +02:00
Bastien Nocera
6155068f9e lib: Split off some fp_img related functions
Some are internal, some are for drivers.
2018-05-29 13:34:18 +02:00
Bastien Nocera
0c4e3bb1c4 lib: Remove drivers definitions from drivers API 2018-05-29 13:34:18 +02:00
Bastien Nocera
4547ff0c19 lib: Remove a number of data types from drivers API
They're unused in drivers.
2018-05-29 13:34:18 +02:00
Bastien Nocera
b7cce4d91d lib: Make fp_minutiae opaque
And fp_minutia private.
2018-05-29 13:34:18 +02:00
Bastien Nocera
475250ce71 lib: Split off some polling related functions 2018-05-29 13:34:18 +02:00
Bastien Nocera
f40f231a63 lib: Make fp_img_dev structure opaque 2018-05-29 13:34:18 +02:00
Bastien Nocera
d83d92adf2 lib: Make fp_dev structure opaque 2018-05-29 13:34:18 +02:00
Bastien Nocera
d15282bff1 lib: Remove minutiae utils from internal header
It's only used by drivers.
2018-05-29 13:34:18 +02:00
Bastien Nocera
00637c4f0b lib: Remove fprint_get_drivers() from drivers API
Drivers don't need to get a list of drivers.
2018-05-29 13:34:18 +02:00
Bastien Nocera
2e035a7f45 lib: Make fpi_ssm opaque for drivers
We shouldn't access fpi_ssm struct fields directly in drivers, so add
accessor and setter functions for the common uses.
2018-05-29 13:34:18 +02:00
Bastien Nocera
17577175f8 lib: Remove fpi_driver_to_img_driver() from drivers API
It's only used internally to the library.
2018-05-29 13:34:18 +02:00
Bastien Nocera
933fab6ed4 lib: Remove array_n_elements() from internal header
It's unused.
2018-05-29 13:34:18 +02:00
Bastien Nocera
7e5661caff lib: Don't include assembling.h in internal header
It's not needed.
2018-05-29 13:34:18 +02:00
Bastien Nocera
19dfb138a6 drivers: Use new drivers_api.h in drivers 2018-05-29 13:34:18 +02:00
Bastien Nocera
0930f2614c lib: Use public header in helper files
aeslib.h and assembling.h don't need access to internal data structures,
so make them include the public fprint.h rather than the internal header
fp_internal.h.
2018-05-29 13:34:18 +02:00
Bastien Nocera
1b20521e5c lib: Add drivers_api.h file
This will hide library internals, for the purpose of making the drivers
API documentable.
2018-05-29 13:34:18 +02:00
Bastien Nocera
0e351db91a lib: Fix new BUG_ON() implementation again
This time, the macro didn't wrap the condition, resulting in calls
like BUG_ON(size > 100) being expanded to g_assert(!size > 100), when
what we wanted was BUG_ON(!(size > 100)).

See 9cca501650,
ff09456cf5, and egg on my face.
2018-05-25 17:38:40 +02:00
Bastien Nocera
9cca501650 lib: Fix new BUG_ON() implementation
A thinko reversed the meaning of the BUG_ON() condition. This fixes
the inverted implementation in commit
ff09456cf5.
2018-05-25 16:12:29 +02:00
Bastien Nocera
2481cbe4ab poll: Add some details about how mainloop integration works 2018-05-25 15:58:29 +02:00
Bastien Nocera
bc3959d1e0 lib: Fix internal deprecation warning
Remove deprecation warning that comes from an internal function, it
serves no purpose, and the function itself is deprecated.

Follow-up from commit 1f0079a274.
2018-05-25 13:45:13 +02:00
Bastien Nocera
1ac815e457 examples: Fix integer overflow warnings 2018-05-25 13:40:33 +02:00
Bastien Nocera
55b83062d0 img: Fix fd leaks
Spotted by maddin200@aol.com

https://bugs.freedesktop.org/show_bug.cgi?id=90197
2018-05-25 13:30:35 +02:00
Mark Harfouche
391f77ce5e aes1610: Fix compilation warning in aes1610
Fixes:
drivers/aes1610.c:736:34: warning: ‘stop_reader’ defined but not used [-Wunused-const-variable=]

https://bugs.freedesktop.org/show_bug.cgi?id=105429
2018-05-25 13:21:17 +02:00
Bastien Nocera
bc30a3d2e5 aes2501: Fix state machine never using "init_3" state
This fixes this warning by the same token:
drivers/aes2501.c:671:34: warning: ‘init_3’ defined but not used [-Wunused-const-variable=]

https://bugs.freedesktop.org/show_bug.cgi?id=105429
2018-05-25 13:21:17 +02:00
Bastien Nocera
ff5de4ff03 lib: Replace open-coded array_n_elements()
Replace it with glib's version, which already exists.

https://bugs.freedesktop.org/show_bug.cgi?id=106280
2018-05-25 13:20:34 +02:00
Bastien Nocera
f433a4d67c upekts: Fix compilation warning
libfprint/drivers/upekts.c: In function ‘alloc_send_cmd_transfer’:
libfprint/drivers/upekts.c:161:2: warning: ‘strncpy’ output truncated before terminating nul copying 4 bytes from a string of the same length [-Wstringop-truncation]
  strncpy(buf, "Ciao", 4);
  ^~~~~~~~~~~~~~~~~~~~~~~

Replace with memcpy() to only copy the 4 bytes we need.

https://bugs.freedesktop.org/show_bug.cgi?id=106281
2018-05-25 13:19:31 +02:00
Bastien Nocera
994061af44 drivers: Simplify headers
Remove all the headers already included through "fp_internal.h" such as
<libusb.h> and <errno.h>, include "assembling.h" and "driver_ids.h" there
as well to avoid doing it in (almost) every driver.
2018-05-24 17:02:15 +02:00
Bastien Nocera
501020921e vfs301_proto: Use GLib helpers
No need to roll out own min() implementation, or use the bare assert().
2018-05-24 17:00:17 +02:00
Bastien Nocera
53c09405b2 etes603: Use GLib's g_assert()
Instead of requiring assert.h to be included.
2018-05-24 16:57:14 +02:00
Bastien Nocera
1f0079a274 data: Mark all the fp_dscv_print functions as deprecated
As this is pretty much copy/pasted in fprintd, and should instead be
implemented by whatever system actually stores the data, rather than
in a generic but not quite "fits-all-purpose" way.

https://bugs.freedesktop.org/show_bug.cgi?id=106550
2018-05-24 13:21:40 +02:00
Bastien Nocera
15afe43cf0 examples: Replace deprecated fp_set_debug() 2018-05-24 12:43:53 +02:00
Bastien Nocera
eb8f7ba3b2 lib: Mark fp_set_debug() as deprecated
As per the documentation.
2018-05-24 12:42:18 +02:00
Bastien Nocera
901a6f7fed lib: Add macro for deprecated functions 2018-05-24 12:41:35 +02:00
Bastien Nocera
fc92f62136 build: Remove the need to modify sources for new drivers
Instead of having to modify both fp_internal.h to list each driver
definition structure, and core.c to add those drivers to arrays we
can loop over, generate both of those using meson.
2018-05-24 12:23:39 +02:00
Bastien Nocera
ff09456cf5 lib: Use g_assert* to implement BUG() and BUG_ON() assertions 2018-05-23 19:14:33 +02:00
Bastien Nocera
32fcfde86b lib: Use GLib and libusb directly for debug output
Use GLib internally to output debug information, and tell about
libusb's LIBUSB_DEBUG envvar for libusb debug.

fp_set_debug() is now a no-op.

https://bugs.freedesktop.org/show_bug.cgi?id=106552
2018-05-23 19:13:08 +02:00
Bastien Nocera
363a1b3371 lib: Replace empty fp_dbg() calls
GLib won't like them, so use G_DEBUG_HERE() instead.
2018-05-23 19:11:07 +02:00
Bastien Nocera
c376c6fb02 lib: Fix type mismatch warnings in debug output 2018-05-23 19:11:05 +02:00
Bastien Nocera
8e6e23b8d0 build: Always enable debugging logging
We shouldn't need to specifically enable debug logging to get useful
data out of a compiled libfprint either, so always enable debugging
output. It will still be switched off at runtime, by default.
2018-05-23 19:11:02 +02:00
Bastien Nocera
63e5d56441 build: Always allow switching log level at runtime
There are no parts of libfprint that are so resource intensive that we'd
want to disable logging. This avoids (hopefully rare) cases where
compiled versions of libfprint are distributed with logging completely
disabled, and thus can't be debugged.
2018-05-23 19:10:58 +02:00
Bastien Nocera
e9bfd943fc docs: Mark a few FP_VERIFY_MATCH as constant
Add "%" prefix to mark it as a constant in the docs.
2018-05-23 15:18:34 +02:00
Bastien Nocera
614e2286c2 docs: Fix typo in fp_enroll_finger_img() API docs
Missing ":" after argument name.
2018-05-23 15:18:34 +02:00
Bastien Nocera
768a74c4bf docs: Remove transfer information from fp_dev_open()
There's no GObject usage in the public API.
2018-05-23 15:18:34 +02:00
Bastien Nocera
612e9e11de docs: Document async function callbacks 2018-05-23 15:18:34 +02:00
Bastien Nocera
06c72d54be poll: Add missing API docs for polling functions 2018-05-23 15:18:34 +02:00
Bastien Nocera
be68bacc94 lib: Merge two other async callback types
Merge fp_capture_cb and fp_verify_cb.

https://bugs.freedesktop.org/show_bug.cgi?id=106551
2018-05-23 15:18:34 +02:00
Bastien Nocera
317d7bc988 lib: Simplify fp_*_stop_cb callback definitions
They're all the same, so merge them into a single fp_operation_stop_cb.

https://bugs.freedesktop.org/show_bug.cgi?id=106551
2018-05-23 13:39:10 +02:00
Bastien Nocera
b44e417bca docs: Fix '\sa' doxygen references 2018-05-23 13:39:10 +02:00
Bastien Nocera
9d67ce484d lib: Make inline functions real functions
Otherwise they will not be parsed by gtk-doc and documented.
2018-05-23 13:39:10 +02:00
Bastien Nocera
78b8602cf6 lib: Make fp_minutia an opaque structure
Nothing uses the elements of the structure, so make it opaque.
2018-05-23 13:39:10 +02:00
Bastien Nocera
83a0a7681b img: Make fpi_img_detect_minutiae() static
It's unused outside img.c, so mark it as static.
2018-05-23 13:39:10 +02:00
Bastien Nocera
c5e0e41ce7 docs: Add documentation for opaque structures 2018-05-23 13:39:00 +02:00
Bastien Nocera
b3fe4a1e91 docs: Update API documentation
Fixes to layout, dead links, typography, and more.

Thanks to Benjamin Berg <bberg@redhat.com> for the thorough review
2018-05-18 05:51:58 +02:00
Bastien Nocera
f59bf389d9 INSTALL: Update for Meson 2018-05-18 01:16:30 +02:00
Bastien Nocera
231b8f9f92 doc: Port from Doxygen to gtk-doc
Split the introduction into separate chapters, add filler documentation
for async functions, fix mismatched function arguments.
2018-05-17 17:39:51 +02:00
Bastien Nocera
dac153d24a build: Port to meson
And remove the autotools. Faster, cleaner.

https://bugs.freedesktop.org/show_bug.cgi?id=106514
2018-05-15 10:46:54 +02:00
Bastien Nocera
3661d146a7 drivers: Remove UPEKE2 driver
The device is already handled by upektc_img driver, and its sources
still exist in git if needed.

https://bugs.freedesktop.org/show_bug.cgi?id=106514
2018-05-15 10:46:54 +02:00
Bastien Nocera
3bf55a3e07 examples: Fix compile-time warning
Fix "function declaration isn’t a prototype" warning
2018-05-14 17:35:51 +02:00
Bastien Nocera
5226467fc2 build: Make NSS (and URU4000) driver optional
In case NSS isn't available.

https://bugs.freedesktop.org/show_bug.cgi?id=106278
2018-05-11 11:13:25 +02:00
Mark Harfouche
54deaa1b24 mindtct: Fix compilation warnings
nbis/mindtct/morph.c:152:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
nbis/mindtct/morph.c:176:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
nbis/mindtct/morph.c:200:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
nbis/mindtct/morph.c:222:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]

https://bugs.freedesktop.org/show_bug.cgi?id=105429
2018-03-11 22:43:04 +01:00
Mark Harfouche
7ff667f58d examples: Fix memory leaked by device discovery
https://bugs.freedesktop.org/show_bug.cgi?id=105427
2018-03-11 13:46:00 +01:00
Mark Harfouche
58ba9b02ed lib: Fix memory leak patch in device discovery
libusb_free_device_list() needs to be called on the list of USB devices
obtained through libusb_get_device_list() or the list and its elements
will be leaked.

https://bugs.freedesktop.org/show_bug.cgi?id=105427
2018-03-11 13:43:58 +01:00
Hans de Goede
d35da0ce99 fprint-list-udev-rules: Add some unsupported Validity Sensors readers to the whitelist 2017-12-12 10:51:43 +01:00
Hans de Goede
041e6a1078 fprint-list-udev-rules: Add some unsupported Elantech readers to the whitelist 2017-12-12 10:51:43 +01:00
Hans de Goede
1c73c36ed7 fprint-list-udev-rules: Remove white-list entries which have a driver now
Remove 2 entries from the whitelist for finger-print models for which
we have a driver now, so they no longer need to be on the whitelist.
2017-12-12 10:51:43 +01:00
Anton Eliasson
69de32f700 vfs5011: Don't submit an image if there was an error capturing it 2017-12-03 13:12:53 -08:00
Alan Davidson
4bfee76ead Fix security hole: zero out memory when allocated 2017-12-03 13:11:48 -08:00
Vasily Khoruzhick
4d7afd9b4f uru4k: increase threshold to detect encryption
Otherwise we get false positives on devices without encryption
2017-12-03 13:10:13 -08:00
Igor Filatov
cfe60c0640 Add Elan driver 2017-12-03 21:17:26 +02:00
Vasily Khoruzhick
d82847a6b4 0.7.0 2017-05-14 09:26:30 -07:00
Vasily Khoruzhick
5a7e6e07ff etes603: fix compilation error caused by a0bbbd7d3 2016-11-07 08:10:44 -08:00
Vasily Khoruzhick
2162aa9f48 uru4k: decrypt image if necessary
Add naive detection of image encryption: if stddev between two
adjacent rows is higher than threshold assume that image is encrypted
and decrypt it.

Fixes fd.o bug 88945
2016-11-05 20:58:18 -07:00
Vasily Khoruzhick
a1f36c71c9 upeksonly: decrease bz3_threshold for 147e:1001
Its width is only 216 pixels, and it appears not to be enough for matching at
default threshold.
2016-11-05 20:58:18 -07:00
Vasily Khoruzhick
e4eedef27e upeksonly: use fpi_std_sq_dev() and fpi_mean_sq_diff_norm()
Use fpi_mean_sq_diff_norm() for blank line detection and
fpi_mean_sq_diff_norm() for duplicate line detection.
Fixes finger presence and removal detection.
2016-11-05 20:58:18 -07:00
Vasily Khoruzhick
5e29695969 lib: move some functions from vfs5011.c into img.c 2016-11-05 20:58:18 -07:00
Konstantin Semenov
49a46668ad lib: Add VFS0050 driver
New driver for 138a:0050 device

https://bugs.freedesktop.org/show_bug.cgi?id=91616
2016-11-05 20:58:18 -07:00
Bastien Nocera
76269decdd aes4000: Fix possible crash if USB init fails
The code was indented, but only the first call was actually in the
conditional, which meant that initialisation carried on as normal on top
of a failed USB device. Exit early and remove the conditional to fix
this.

https://bugzilla.gnome.org/show_bug.cgi?id=98594
2016-11-05 00:13:51 +01:00
Bastien Nocera
8454a25ecf aes3500: Fix possible crash if USB init fails
The code was indented, but only the first call was actually in the
conditional, which meant that initialisation carried on as normal on top
of a failed USB device. Exit early and remove the conditional to fix
this.
2016-11-05 00:11:30 +01:00
Bastien Nocera
a0bbbd7d32 drivers: Print USB error when libusb_claim_interface() fails
https://bugs.freedesktop.org/show_bug.cgi?id=98594
2016-11-05 00:10:17 +01:00
Bastien Nocera
12f6dae8cd build: Fix 9570c36 on Debian
Debian's ash doesn't implement pushd/popd.
2016-11-03 14:05:46 +01:00
Bastien Nocera
9570c36fd4 build: Fix running autogen.sh out-of-tree 2016-07-19 11:18:58 +02:00
Bastien Nocera
487dae0d2f Work-around kernel's lack of USB PM
The device is already supported by the vfs5011 driver and non-
blacklisted, so will show up in the udev rules, but for ease of
backporting to older versions, add it to the whitelist anyway.

https://bugzilla.redhat.com/show_bug.cgi?id=1173367
2016-04-12 16:39:39 +02:00
Vasily Khoruzhick
d71018bd8f vfs5011: add USB IDs of device found in some Toshiba laptops 2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
61fa57b05d aes1610: decrease bz3_threshold to 20 2015-12-16 13:24:39 -08:00
Volkau Siarhei
fc1781e317 vfs101: fix broken enrolling by prev commit to vfs101.c 2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
ffef6c2bcc vfs5011: add proper dev_deactivate() support
Driver is not able to cancel imaging process without this change
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
67d29f7936 aes1610: improve gain settings 2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
9437c98d54 lib: frame assembling: flip image for non-reverse direction
It was here for aes2501 and aes1610 before moving assembling routines
into common code.
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
f7d00a828d lib: use normalized error to make decision about movement direction
Height is not a good determinant in movement direction, normalized
error is much better. Should fix aes1610 and aes2501 driver issues.
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
0f0a4b2da6 aes2660: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
c9cdbaf880 aes2550: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
83f29dad9f aes2501: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
c2a11c5918 aes1610: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
3746b2ad5c upeksonly: use line assembling routines to account variable swiping speed 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
b51fa446e3 lib: move line assembling routines out of vfs5011 into common code 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
6fc5293e83 upeksonly: add support for 147e:1001 device 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
aab3daa28b upeksonly: move regwrite sequences into a header 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
e40f7bd1f7 lib: move frame assembling routines into a separate file and make them usable by non-aes drivers
Frame assembling routines are not aes-specific, so move them into a separate file
and add an accessor for peeking a pixel.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
6664f87d8f aes1610: fix memory corruption introduced by a457658f1b 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
6e8d5cd6a1 vfs5011: do duplicate line search for 30 lines
It seems that on faster devices, the driver can get more duplicate lines.
Without this change scan looks too stretched.

Suggested by: Thomas Rinsma <thomasrinsma@gmail.com>
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
9f7e1ecf40 vfs5011: make '5 scans per enroll' work with this driver
Restart scan if core did not ask to terminate it explicitly.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
d9567002e4 imgdev: reset action_result after reporting it
Otherwise we end up in reporting the same result on next iteration.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
a656a4a9f3 upektc_img: Handle scan status reported by sensor properly
Sensor can report "short scan" or "finger not centered" instead of image,
so it's necessary to handle them gracefully.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
2944a35e74 imgdev: Add fpi_imgdev_abort_scan() to abort scan gracefully from within the driver.
Smart sensors like Upek TouchChip Coprocessor can provide scan status instead
of image, as result we need to report such status as "short scan" or "finger not centered"
from within the driver, since it's not a session error.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
391373fb0c upektc_img: Fix copy/paste error
Replace upekts_img with upektc_img
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
985e8c4577 aes1660: set partial flag on an image
Sensor width seems to be only 128 pixels and that's not enough
for scanning whole finger surface. Lower bz3_threshold to 25,
since for wrong finger score never goes above 10, but sometimes
for right finger score is below 40.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
bd0d4258e4 upektc_img: set partial flag on an image
Sensor width seems to be only 128 pixels and that's not enough
for scanning whole finger surface. Lower bz3_threshold to 20,
since for wrong fingerprint score never goes above 10, but sometimes
for right finger score is below 40.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
9bbd9b208a lib: add partial image flag
And activate perimeter points removal if this flag is set.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
59fe0fb699 nbis: add one more step of false minutia removal
Remove minutiae that are located on scan perimeter
2015-12-16 13:24:38 -08:00
Bastien Nocera
f1fdd71613 rules: Add driver name to the USB properties
Makes it easier to see which driver is being used.
2015-03-15 23:13:08 +01:00
Bastien Nocera
bb66780cb5 build: Document why some drivers are disabled
This avoids confusion when looking at the build logs
2015-03-15 23:10:03 +01:00
Carlos Olmedo Escobar
45fb6d7908 vfs101: Logical conjunction always evaluates to false. 2015-02-09 12:47:22 +01:00
Timo Teräs
9f408bf51b imgdev: fix enum mismatch for dev_change_state() call
This bug has existed long time, but it was uncovered by commit
e215b05094 which enabled multiple enrollment rounds.

In practice this broke (at least) URU4000 driver state machine
causing it to enter indefinite loop - due to the invalid state
change callback.

Patch originally posted at:
http://lists.freedesktop.org/archives/fprint/2014-June/000603.html

Test and verification results:
http://lists.freedesktop.org/archives/fprint/2014-June/000607.html

Signed-off-by: Timo Teräs <timo.teras@iki.fi>
2015-02-03 18:26:33 +01:00
Bastien Nocera
ee32166267 0.6.0 2015-02-03 17:36:32 +01:00
Vasily Khoruzhick
a3c90f2b24 upektc_img: handle 0x28 message better 2015-01-31 13:19:24 +03:00
Arseniy Lartsev
dc537ef2c9 lib: Add VFS5011 driver
New driver for VFS5011 138a:0011 and 138a:0018

https://bugs.freedesktop.org/show_bug.cgi?id=61692

[vasilykh]:
	- use g_get_real_time() instead of non-portable time()
	- use g_free() instead of free()
	- comment out "RECV(VFS5011_IN_ENDPOINT_CTRL2, 8)"
2015-01-31 13:19:11 +03:00
Vasily Khoruzhick
061a457658 aeslib: improve frame-assembling routines
Some scanners provide hardware assistance in frame assemling, i.e.
horizontal and vertical offset to previous frame is provided. This
commit improves code to utilise that assistance. Sensors without
hardware assistance will use software algorithm, which was also
improved to do search in horizontal direction.
2015-01-31 13:18:23 +03:00
Vasily Khoruzhick
82ae7c1c09 aesx660: fix spelling of define name 2015-01-31 13:18:23 +03:00
Rex Dieter
79d79c3c87 lib: Test before applying power saving udev rules
add TEST=="power/control"... to test for the presence of the file
before setting it.

See https://bugzilla.redhat.com/show_bug.cgi?id=950205

https://bugs.freedesktop.org/show_bug.cgi?id=87414
2015-01-28 16:28:57 +01:00
Vasily Khoruzhick
35e356f625 build: add subdir-objects option to make automake happy
Without subdir-object automake throws warnings like this one:

Makefile.am warning: source file 'drivers/aes2550.c' is in a subdirectory,
Makefile.am but option 'subdir-objects' is disabled
2013-08-19 11:13:50 +03:00
Vasily Khoruzhick
948ab02d1a lib: use pixman for imaging utils
pixman is very lightweight library for pixel manipulation, and it
has no dependencies except glibc, so using it instead of gdkpixbuf/imagemagick
makes list for libfprint dependencies a bit shorter.
2013-08-19 11:13:50 +03:00
Patrick Marlier
a6101026d2 Add EgisTec ES603 driver
This driver handles EgisTec ES603 device, ID 1c7a:0603
2013-08-19 11:13:50 +03:00
Vasily Khoruzhick
e0966cb20f vfs101: drop nr_enroll_stages override
Imaging drivers aren't allowed to change number of enroll stages anymore,
all that imaging driver does is providing image to the upper layer.
Upper layer decides if it needs more scans.
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
e278e8321c vfs301: drop nr_enroll_stages override
Imaging drivers aren't allowed to change number of enroll stages anymore,
all that imaging driver does is providing image to the upper layer.
Upper layer decides if it needs more scans.
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
e1728e7c25 aes1660: remove unneeded lowering of bz3_threshold
Since 5 scans for enroll was introduced it's not necessary to lower
bz3_threshold anymore, there's a good probability that scan to verify matches
with at least one enrolled sample.
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
e215b05094 imgdev: perform 5 scans for enrollment
This feature dramatically improves matching rate on devices with small
sensors.
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
fe3fdd1f50 upeke2: disable by default, device is now handled by upektc_img driver 2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
5ff45658c0 upektc_img: add support for Upek Eikon 2 devices 2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
d12b294783 Add upektc_img driver
Imaging driver, handles UPEK 147e:2020 device
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
2bba4fb073 Implement image capture (both synchronous and asynchronous variants)
fp_dev_img_capture() is not implemented and returns -ENOTSUPP for all devices
since migration to asynchronous model. This commit implement missing functionality
2013-08-12 11:18:18 +03:00
Juvenn Woo
aeca32fc12 aes3k: add aes3500 driver
This driver supports AES3500 device (08ff:5731) and utilizes common routines from
AES4000.
2013-08-12 11:18:18 +03:00
Juvenn Woo
12c1088777 aes3k: extract common routines aes3k from aes4000
AES3500 and AES4000 are pretty similar devices, have same
command send, the only difference is in image size and init sequence.
Extract common routines from AES4K to be used later in AES3500 driver

https://bugs.freedesktop.org/show_bug.cgi?id=64351
2013-08-12 11:18:18 +03:00
Bastien Nocera
9e2f8b5e75 0.5.1 2013-08-11 17:53:21 +02:00
Bastien Nocera
c14ebd8b63 Add MS keyboard to the suspend blacklist
045e:00bb isn't a stand-alone fingerprint reader, but a keyboard
with a reader integrated.

https://bugs.freedesktop.org/show_bug.cgi?id=66659
2013-07-25 12:29:53 +02:00
Bastien Nocera
a6339a30ef Fix udev rules printing
The blacklisted devices weren't correctly checked for past the first
item, as we weren't using the right index to get the product ID
from the ID table.
2013-07-25 12:29:29 +02:00
Bastien Nocera
f3dd55815e lib: Use g_malloc0 instead of g_malloc+memset 2013-06-26 14:21:33 +02:00
Patrick Marlier
6d65bfcf80 lib: g_malloc never fails
Or rather, it never returns errors and aborts instead if
memory cannot be allocated, so remove code that handled
failures.

https://bugs.freedesktop.org/show_bug.cgi?id=57869
2013-06-26 14:18:40 +02:00
Jeremy Bicha
1acd647b29 build: Fix underlinking against glib
https://bugs.freedesktop.org/show_bug.cgi?id=63755
2013-06-26 14:14:49 +02:00
Vasily Khoruzhick
0e843ad6b3 build: fix autoreconf warnings
Replace AC_LANG_PROGRAM with AC_LANG_SOURCE and INCLUDES with AM_CFLAGS to fix
autoreconf warnings

https://bugs.freedesktop.org/show_bug.cgi?id=62748
2013-06-26 14:13:21 +02:00
Vasily Khoruzhick
7eafca7bab build: glib-2.28 or newer is a requirement
We're using g_slist_free_full which was introduced in glib-2.28

https://bugs.freedesktop.org/show_bug.cgi?id=59790
2013-01-30 17:52:54 +01:00
Vasily Khoruzhick
3b3679c900 upeke2: Add support for 147e:2020 ID
https://bugs.freedesktop.org/show_bug.cgi?id=59320
2013-01-13 19:02:52 +01:00
Vasily Khoruzhick
43eca622cd configure.ac: Use AC_CONFIG_HEADERS
AM_CONFIG_HEADER is obsolete, use AC_CONFIG_HEADERS instead of it.

https://bugs.freedesktop.org/show_bug.cgi?id=59320
2013-01-13 19:02:48 +01:00
Bastien Nocera
02509e1073 build: udev rules must be created when building from git
https://bugs.freedesktop.org/show_bug.cgi?id=59076
2013-01-07 12:36:45 +01:00
Timo Teräs
0b2d33c712 imgdev: fix cancelling of enrollment from stage_completed callback
Re-check device state after fpi_drvcb_enroll_stage_completed().
If enrollment was cancelled after non-completing stage, we must
not restart acquire as it would confuse the internal state machine.

https://bugs.freedesktop.org/show_bug.cgi?id=57829
2012-12-14 13:17:30 +01:00
Timo Teräs
7751fcb375 AUTHORS: Update
https://bugs.freedesktop.org/show_bug.cgi?id=57829
2012-12-14 13:17:24 +01:00
Timo Teräs
8a87ba448c uru4000: fix cancelling of imaging from error callback
Call error callback before resetting img_transfer to NULL. This
variable is internally used to detect if we are still in imaging
loop and the call to execute_state_change() needs to be postponed.
Since this is the final thing imaging_complete() we can't reset
img_transfer until just before this call.

https://bugs.freedesktop.org/show_bug.cgi?id=57829
2012-12-14 13:16:31 +01:00
Timo Teräs
7e1646c382 uru4000: fix race condition on waiting power up irq
It can come before we finish reading the status register on some
cases. Arm the irq handler early, and fix the state machine to
handle early irq properly.

https://bugs.freedesktop.org/show_bug.cgi?id=57834
2012-12-04 11:55:10 +01:00
Bastien Nocera
a5ec0b30e1 0.5.0 2012-12-03 16:28:00 +01:00
Bastien Nocera
29cf86a02e build: Fix configure-time warning
Due to missing quotes
2012-12-03 16:28:00 +01:00
Bastien Nocera
7892c943e6 build: Create .tar.xz distribution by default 2012-12-03 16:28:00 +01:00
Vasily Khoruzhick
a7d6b7c30a nbis: Fix crash in gen_initial_maps()
If the image is too small some coordinates can become negative.
Handle this to avoid memory corruption.

https://bugs.freedesktop.org/show_bug.cgi?id=57730
2012-12-03 15:36:25 +01:00
Bastien Nocera
258ac2d4da build: Build all the drivers by default 2012-12-01 15:10:13 +01:00
Vasily Khoruzhick
31cf7a9383 build: Add common routines to the dist
Fixes dist when aesX660 drivers are disabled from the build.

https://bugs.freedesktop.org/show_bug.cgi?id=57724
2012-12-01 15:04:47 +01:00
Vasily Khoruzhick
f335256cbe build: Don't build unneeded aesX660 routines
When neither of the AES drivers are built, no need to build the common
routines.

https://bugs.freedesktop.org/show_bug.cgi?id=57724
2012-12-01 15:02:59 +01:00
Vasily Khoruzhick
c02cb3083d AES-drivers: drop redudant type cast in g_slist_free_full call
https://bugs.freedesktop.org/show_bug.cgi?id=57688
2012-11-29 15:32:14 +01:00
Vasily Khoruzhick
2084724115 upektc: add support for Eikon Touch 300
https://bugs.freedesktop.org/show_bug.cgi?id=45197
2012-11-29 12:35:05 +01:00
Vasily Khoruzhick
59925d2027 Update AUTHORS file
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:52 +01:00
Vasily Khoruzhick
118c610e29 lib: add AES2660 driver
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:52 +01:00
Vasily Khoruzhick
313bfede77 lib: add AES1660 driver
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:52 +01:00
Vasily Khoruzhick
c1da647aed lib: add AES1660/AES2660 common routines
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:52 +01:00
Vasily Khoruzhick
22d204cc68 lib: Split fpi_im_resize factor into width/height
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:27 +01:00
Vasily Khoruzhick
bc497f1b26 aeslib: prevent integer overflow
AuthenTec devices send 4bpp images, but current code assumes 3bpp for
some reason.

https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:27 +01:00
Vasily Khoruzhick
8c5f2e6434 aes: unify image processing code for AuthenTec devices
Move overlap detection and assembling code into aeslib to prevent
code duplication

https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:22 +01:00
Vasily Khoruzhick
5b20892dd4 Thanks to Greg and Martin from AuthenTec
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:51:55 +01:00
Colin Walters
b2a53a459c autogen.sh: Honor NOCONFIGURE=1
See http://people.gnome.org/~walters/docs/build-api.txt
2012-11-16 19:31:30 +01:00
Timo Teräs
45ac0eefb0 uru4000: Fix image capture error on ARM
http://www.mail-archive.com/fprint@lists.freedesktop.org/msg00294.html
2012-11-16 18:38:52 +01:00
Vasily Khoruzhick
ea6d5ba6d6 lib: Fix mess with driver IDs
ID is just a some magic number to make fingerprint from one scanner model
incompatible with another scanner model. Get rid of "magic", declare enum
and use it.

https://bugs.freedesktop.org/show_bug.cgi?id=56956
2012-11-16 17:57:47 +01:00
Vasily Khoruzhick
ed2c75842a upektc: Port to asynchronous model
https://bugs.freedesktop.org/show_bug.cgi?id=56955
2012-11-16 17:56:51 +01:00
Vasily Khoruzhick
b307dd1a6a aes2550: fix bug introduced by last commit
ssm->priv is not aesdev, but fp_img_dev

https://bugs.freedesktop.org/show_bug.cgi?id=56782
2012-11-16 17:46:27 +01:00
Vasily Khoruzhick
39902374ce aes2550: Harden against low finger pressure
Stop scan only after 3rd heartbeat message

https://bugs.freedesktop.org/show_bug.cgi?id=56782
2012-11-06 09:32:07 +01:00
Bastien Nocera
9e92d4cf2c examples: Fix compile-time warnings
A few signedness problems.
2012-11-06 09:30:40 +01:00
Vasily Khoruzhick
6b84c6664f AES1610: driver cleanup, part #2
Remove dead code

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-06 09:26:17 +01:00
Vasily Khoruzhick
f569d0bf44 AES1610: driver cleanup, part #1
Fix warnings reported by gcc

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-06 09:26:17 +01:00
Vasily Khoruzhick
1cfd14b7fe nbis: fix rest of warnings reported by gcc
https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-06 09:26:17 +01:00
Vasily Khoruzhick
5d32102efe nbis: prefix global variables with "g_"
It fixes a lot of warnings about shadowing global variable

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-06 09:26:17 +01:00
Bastien Nocera
9e10edd422 aes2501: Add year 2008 to the copyright.
https://bugs.freedesktop.org/show_bug.cgi?id=56715#c7
2012-11-05 19:52:46 +01:00
Vasily Khoruzhick
fecf6d6fe5 aes2501: Update copyright line
https://bugs.freedesktop.org/show_bug.cgi?id=56715
2012-11-05 19:51:19 +01:00
Vasily Khoruzhick
e32fa8cc38 aes2501: Improve image contrast
When scanning, check the histogram sum, and increase the
ADREFHI register value if the sum is too low, or decrease
it if it's too high.

https://bugs.freedesktop.org/show_bug.cgi?id=56715
2012-11-05 19:49:54 +01:00
Vasily Khoruzhick
d8aae30a67 aes2501: Harden against low finger pressure
Wait for 3 empty frames before stopping the scan

The driver used to stop the scan immediately after an empty frame
(by checking for hist sum == 0), but it is possible to get empty
frames in the middle of the scan due to low finger pressure.
Waiting for 3 empty frames stop the driver wrongly aborting the scan
too early.

https://bugs.freedesktop.org/show_bug.cgi?id=56715
2012-11-05 19:47:32 +01:00
Vasily Khoruzhick
84b97ea15b lib: Fix warning when debug logs are disabled
Fix warning in sync.c when debug logs are not enabled

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-05 19:24:55 +01:00
Vasily Khoruzhick
8f98743857 upeksonly: Fix build-time warnings
Fix warnings reported by gcc

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-05 19:24:44 +01:00
Vasily Khoruzhick
be29f27e25 upeke2: Fix build-time warnings
Fix warnings reported by gcc

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-05 19:24:23 +01:00
Timo Teräs
d003f08855 lib: Add support for DigitalPersona URU4500
By adding native encryption support, rather than poking at
the firmware to disable it.

This also makes the URU4000B use native encryption.

https://bugs.freedesktop.org/show_bug.cgi?id=55351
2012-11-05 18:39:16 +01:00
Bastien Nocera
0f7ad00fc4 HACKING: Update with bugzilla link 2012-10-15 11:46:03 +02:00
Vasily Khoruzhick
83333bce3f lib: Add AES2550/AES2810 driver
Initial implementation of AES2550/AES2810 driver.
Does not support AES2810 crypto engine and external flash.

https://bugs.freedesktop.org/show_bug.cgi?id=50859
2012-10-11 15:24:14 +02:00
Bastien Nocera
132b178304 lib: Add driver names to the udev rules 2012-08-28 20:02:17 +01:00
Andrej Krutak
c3689665db lib: Add VFS300/VFS301 driver
https://bugs.freedesktop.org/show_bug.cgi?id=37574
2012-08-28 19:51:59 +01:00
Bastien Nocera
3d222ddda7 lib: Fix whitespace errors 2012-08-28 19:45:08 +01:00
Bastien Nocera
080c414ce9 build: Whitespace fixes in configure.ac 2012-08-28 19:45:08 +01:00
Vasily Khoruzhick
ff02115b0f lib: Fix global variables collision with libusb 2012-08-14 15:25:44 +01:00
Bastien Nocera
3b409c767c build: Allow disabling the update of udev rules
We can only create the udev rules file when we're not cross-compiling,
so allow people to disable updating the file when building from
tarballs.

If you run with --enable-udev-rules=no and the
60-fprint-autosuspend.rules doesn't exist, you're on your own.
2012-08-14 15:19:35 +01:00
Bastien Nocera
d05c69698c build: Fix distcheck'ing not working
We need to be able to override the udev rules directory for
distchecking to work.
2012-08-14 15:12:20 +01:00
Bastien Nocera
2d09b10a27 build: Include udev rules in tarball 2012-08-14 14:52:58 +01:00
Bastien Nocera
bc03d56186 build: Add list of drivers to include when distchecking 2012-08-14 14:51:23 +01:00
Bastien Nocera
96e7224a23 build: Fix distchecking of examples directory 2012-08-14 14:48:43 +01:00
Benedikt Morbach
0f4a75ff61 update udev rules dir location again
Instead of hardcoding whatever the new fashion is every few months, which might
even differ between distributions, just put the rules where udev says we should.

https://bugs.freedesktop.org/show_bug.cgi?id=50426
2012-05-28 14:12:55 +01:00
Bastien Nocera
dfff16f3e3 Don't print duplicate udev rules
https://bugs.freedesktop.org/show_bug.cgi?id=45513
2012-02-02 05:28:17 +00:00
Patrick Marlier
3d2e545264 Fix possible crash without a deactivate callback
We were checking for ->activate existing instead of ->deactivate.
2012-01-20 12:57:27 +00:00
Benedikt Morbach
c96fa32da4 Move udev rules to libdir
This moves the dir from /etc/udev/rules.d to /lib/udev/rules.d,
which is encouraged by upstream, as /etc is for user overrides only

https://bugs.freedesktop.org/show_bug.cgi?id=44507
2012-01-06 12:13:29 +00:00
Matthew Garrett
48ec64f683 Fix udev control path
Newer kernels uses power/control for USB suspend setup, not power/level.
Fix the udev rules generation.
2011-10-04 15:11:08 +01:00
Bastien Nocera
f8aa82a554 0.4.0 2011-04-18 18:02:37 +01:00
Bastien Nocera
1451a2dde2 build: Quiet by default 2011-04-08 15:38:33 +01:00
Sergio Cerlesi
d2e957683f Added support for Validity VFS101
USB ID 138a:0001
2011-04-08 15:37:03 +01:00
Sergio Cerlesi
a3ae96c214 Fix function fpi_im_resize on create new image
The function fpi_im_resize copy the new resized image into old fp_img
instead of newimg so, new image is empty and often it generated a
segmentation fault error.
2011-02-22 19:14:02 +00:00
Sergio Cerlesi
3dd905d4e9 Fix return timeout of fp_get_next_timeout
On function fp_get_next_timeout if exist fprint timeout or libusb
timeout the function return the smaller of they.

But if one of that not exist and have a smaller value the function
return a timeout that not exist.
2011-02-18 13:59:28 +00:00
Bastien Nocera
aab031ce37 0.3.0 2010-09-08 11:11:31 +01:00
Bastien Nocera
6bfc516553 Add test program for C++ support
So that we don't regress
2010-09-08 11:03:58 +01:00
Kunal Gangakhedkar
ff842125c5 Add C++ guards to take care of C++ name-mangling.
The libfprint library is generally built with C calling conventions.
Which makes it difficult to link to C++ programs as it is.

This patch adds the support for linking with C++ code - by telling the C++
compiler to use C calling conventions for libfprint functions.

Reported-by: Guus Ellenkamp <guus@activediscovery.net>
Signed-off-by: Kunal Gangakhedkar <kunal.gangakhedkar@gmail.com>
2010-09-08 11:02:25 +01:00
Hugo Grostabussiat
712ebb6d44 Namespace functions and structs for 147e:2016
Inserted "_2016" in structures, enums and functions names
that are related with device 147e:2016.
2010-08-31 17:51:21 +01:00
Hugo Grostabussiat
a18f318446 Added support for UPEK TCS4C (USB ID 147e:1000)
https://bugs.freedesktop.org/show_bug.cgi?id=29719
2010-08-31 17:49:45 +01:00
Bastien Nocera
15b8e8f376 Remove HAL fdi generation
HAL is deprecated, and the rules are broken when multiple
drivers use the same USB IDs.
2010-08-25 11:20:55 +01:00
Bastien Nocera
9f8dd29a55 Make sure all sources are dist'ed
So that you can compile against ImageMagick even if the
distter doesn't.
2010-08-25 11:17:42 +01:00
Bastien Nocera
f28c579779 Print out summary of build options
At the end of the configure run.
2010-08-25 11:17:19 +01:00
Bastien Nocera
375575b701 Port OpenSSL using code to NSS
As OpenSSL cannot really be used in an LGPLv2+ piece of software
without an exception.

Adapted from the example code at:
http://www.mozilla.org/projects/security/pki/nss/sample-code/sample2.html
and:
http://github.com/bagder/curl/commit/f3b77e5
2010-08-25 10:34:30 +01:00
Alexia Death
25161286f5 Only complete scan with MIN_ROWS in print
Don't consider the scan complete unless theres at least
MIN_ROWS recorded or very long blank read occurred.

Typical problem spot: one brief touch before starting the
actual scan. Happens most commonly if scan is started from
before the first joint resulting in a gap after the inital touch.

http://lists.reactivated.net/pipermail/fprint/2009-December/001406.html
2010-08-19 20:36:16 +01:00
Alexia Death
c575afba9a Make the +2 right shift happen on image handoff.
Rather than when reading the rows. Doing it there seems a lot
more sensible and does not get in the way of minimizing the
impact of lost USB packets.

http://lists.reactivated.net/pipermail/fprint/2009-December/001404.html
2010-08-19 20:29:45 +01:00
Alexia Death
8cd31f6ad8 Try to correct missing packets in image stream
If a packet goes missing, use the data from previous row
to minimize distortion, or use some neutral dummy data.

http://lists.reactivated.net/pipermail/fprint/2009-December/001404.html
2010-08-19 20:27:09 +01:00
Alexia Death
4b518bd230 Fix a segfault if a scan was shorter than 8 lines.
This segfault was caused by proceeding to free the conversion result
without checking for an error condition in image to print
data conversion.

http://lists.reactivated.net/pipermail/fprint/2009-December/001405.html
2010-08-19 20:21:51 +01:00
169 changed files with 40451 additions and 13177 deletions

113
.ci/check-abi Executable file
View File

@@ -0,0 +1,113 @@
#!/usr/bin/python3
import argparse
import contextlib
import os
import shutil
import subprocess
import sys
def format_title(title):
box = {
'tl': '╔', 'tr': '╗', 'bl': '╚', 'br': '╝', 'h': '═', 'v': '║',
}
hline = box['h'] * (len(title) + 2)
return '\n'.join([
f"{box['tl']}{hline}{box['tr']}",
f"{box['v']} {title} {box['v']}",
f"{box['bl']}{hline}{box['br']}",
])
def rm_rf(path):
try:
shutil.rmtree(path)
except FileNotFoundError:
pass
def sanitize_path(name):
return name.replace('/', '-')
def get_current_revision():
revision = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
encoding='utf-8').strip()
if revision == 'HEAD':
# This is a detached HEAD, get the commit hash
revision = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip().decode('utf-8')
return revision
@contextlib.contextmanager
def checkout_git_revision(revision):
current_revision = get_current_revision()
subprocess.check_call(['git', 'checkout', '-q', revision])
try:
yield
finally:
subprocess.check_call(['git', 'checkout', '-q', current_revision])
def build_install(revision):
build_dir = '_build'
dest_dir = os.path.abspath(sanitize_path(revision))
print(format_title(f'# Building and installing {revision} in {dest_dir}'),
end='\n\n', flush=True)
with checkout_git_revision(revision):
rm_rf(build_dir)
rm_rf(revision)
subprocess.check_call(['meson', build_dir,
'--prefix=/usr', '--libdir=lib',
'-Dx11-examples=false', '-Ddoc=false', '-Dgtk-examples=false'])
subprocess.check_call(['ninja', '-v', '-C', build_dir])
subprocess.check_call(['ninja', '-v', '-C', build_dir, 'install'],
env={'DESTDIR': dest_dir})
return dest_dir
def compare(old_tree, new_tree):
print(format_title(f'# Comparing the two ABIs'), end='\n\n', flush=True)
old_headers = os.path.join(old_tree, 'usr', 'include')
old_lib = os.path.join(old_tree, 'usr', 'lib', 'libfprint.so')
new_headers = os.path.join(new_tree, 'usr', 'include')
new_lib = os.path.join(new_tree, 'usr', 'lib', 'libfprint.so')
subprocess.check_call([
'abidiff', '--headers-dir1', old_headers, '--headers-dir2', new_headers,
'--drop-private-types', '--fail-no-debug-info', '--no-added-syms', old_lib, new_lib])
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('old', help='the previous revision, considered the reference')
parser.add_argument('new', help='the new revision, to compare to the reference')
args = parser.parse_args()
if args.old == args.new:
print("Let's not waste time comparing something to itself")
sys.exit(0)
old_tree = build_install(args.old)
new_tree = build_install(args.new)
try:
compare(old_tree, new_tree)
except Exception:
sys.exit(1)
print(f'Hurray! {args.old} and {args.new} are ABI-compatible!')

71
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,71 @@
image: fedora:rawhide
stages:
- build
- test
variables:
DEPENDENCIES: libusb1-devel glib2-devel nss-devel pixman-devel systemd meson gtk-doc
gcc gcc-c++ glibc-devel libX11-devel libXv-devel gtk3-devel flatpak-builder
BUNDLE: "org.freedesktop.libfprint.Demo.flatpak"
LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546"
.build_one_driver_template: &build_one_driver
script:
- dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES
# Build with a driver that doesn't need imaging, or nss
- meson -Ddrivers=elan . _build
- ninja -C _build
- rm -rf _build/
.build_template: &build
script:
- dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES
# And build with everything
- meson -Ddrivers=all . _build
- ninja -C _build
- ninja -C _build install
.build_template: &check_abi
script:
- dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES doxygen libabigail git
- ./.ci/check-abi ${LAST_ABI_BREAK} $(git rev-parse HEAD)
build:
stage: build
<<: *build_one_driver
<<: *build
<<: *check_abi
.flatpak_script_template: &flatpak_script
script:
- flatpak-builder --stop-at=${FLATPAK_MODULE} app ${MANIFEST_PATH}
# 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:
paths:
- ${BUNDLE}
when: always
expire_in: 30 days
.flatpak_template: &flatpak
<<: *flatpak_script
<<: *flatpak_artifacts
flatpak master:
image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master
stage: test
variables:
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"
DBUS_ID: "org.freedesktop.libfprint.Demo"
<<: *flatpak

View File

@@ -4,6 +4,8 @@ Copyright (C) 2006 Pavel Machek <pavel@suse.cz>
Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
Copyright (C) 2007 Cyrille Bagard
Copyright (C) 2007 Vasily Khoruzhick
Copyright (C) 2007-2008,2012 Vasily Khoruzhick <anarsoul@gmail.com>
Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
Copyright (C) 2007 Anthony Bretaudeau <wxcover@users.sourceforge.net>
Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
Copyright (C) 2012 Timo Teräs <timo.teras@iki.fi>

96
HACKING
View File

@@ -1,96 +0,0 @@
Copyright notices
=================
If you make a contribution substantial enough to add or update a copyright
notice on a file, such notice must be mirrored in the AUTHORS file. This is
to make it easy for people to comply to section 6 of the LGPL, which states
that a "work that uses the Library" must include copyright notices from
this library. By providing them all in one place, hopefully we save such
users some time.
USB
===
At the time of development, there are no known consumer fingerprint readers
which do not operate over the USB bus. Therefore the library is designed around
the fact that each driver drivers USB devices, and each device is a USB device.
If we were to ever support a non-USB device, some rearchitecting would be
needed, but this would not be a substantial task.
GLib
====
Although the library uses GLib internally, libfprint is designed to provide
a completely neutral interface to it's application users. So, the public
APIs should never return GLib data types or anything like that.
Two-faced-ness
==============
Like any decent library, this one is designed to provide a stable and
documented API to it's users: applications. Clear distinction is made between
data available internally in the library, and data/functions available to
the applications.
This library is confused a little by the fact that there is another 'interface'
at hand: the internal interface provided to drivers. So, we effectively end
up with 2 APIs:
1. The external-facing API for applications
2. The internal API for fingerprint drivers
Non-static functions which are intended for internal use only are prepended
with the "fpi_" prefix.
API stability
=============
No API stability has been promised to anyone: go wild, there's no issue with
breaking APIs at this point in time.
Portability
===========
libfprint is primarily written for Linux. However, I'm interested in
supporting efforts to port this to other operating systems too.
You should ensure code is portable wherever possible. Try and use GLib rather
than OS-specific features.
Endianness must be considered in all code. libfprint must support both big-
and little-endian systems.
Coding Style
============
This project follows Linux kernel coding style but with a tab width of 4.
Documentation
=============
All additions of public API functions must be accompanied with doxygen
comments.
All changes which potentially change the behaviour of the public API must
be reflected by updating the appropriate doxygen comments.
Contributing
============
Patches should be sent to the fprint mailing list detailed on the website.
A subscription is required.
Information about libfprint development repositories can be found here:
http://www.reactivated.net/fprint/Libfprint_development
If you're looking for ideas for things to work on, look at the TODO file or
grep the source code for FIXMEs.

73
HACKING.md Normal file
View File

@@ -0,0 +1,73 @@
# Contributing to libfprint
## GLib
Although the library uses GLib internally, libfprint is designed to provide
a completely neutral interface to its application users. So, the public
APIs should never return GLib data types.
## License clarification
Although this library's license could allow for shims that hook up into
proprietary blobs to add driver support for some unsupported devices, the
intent of the original authors, and of current maintainers of the library,
was for this license to allow _integration into_ proprietary stacks, not
_integration of_ proprietary code in the library.
As such, no code to integrate proprietary drivers will be accepted in libfprint
upstream. Proprietary drivers would make it impossible to debug problems in
libfprint, as we wouldn't know what the proprietary driver does behind the
library's back. The closed source nature of drivers is usually used to hide
parts of the hardware setup, such as encryption keys, or protocols, in order
to protect the hardware's integrity. Unfortunately, this is only [security through
obscurity](https://en.wikipedia.org/wiki/Security_through_obscurity).
We however encourage potential contributors to take advantage of libfprint's
source availability to create such shims to make it easier to reverse-engineer
proprietary drivers in order to create new free software drivers, to the extent
permitted by local laws.
## Two-faced-ness
Like any decent library, this one is designed to provide a stable and
documented API to its users: applications. Clear distinction is made between
data available internally in the library, and data/functions available to
the applications.
This library is confused a little by the fact that there is another 'interface'
at hand: the internal interface provided to drivers. So, we effectively end
up with 2 APIs:
1. The [external-facing API for applications](libfprint/fprint.h)
2. The [internal API for fingerprint drivers](libfprint/drivers_api.h)
Non-static functions which are intended for internal use only are prepended
with the "fpi_" prefix.
## Documentation
All additions of public API functions must be accompanied with gtk-doc
comments.
All changes which potentially change the behaviour of the public API must
be reflected by updating the appropriate gtk-doc comments.
## Contributing
Patches should be sent as merge requests to the gitlab page:
https://gitlab.freedesktop.org/libfprint/libfprint/merge_requests
Drivers are not usually written by libfprint developers, but when they
are, we require:
- 3 stand-alone devices. Not in a laptop or another embedded device, as
space is scarce, unless the device has special integration with that
hardware.
- specifications of the protocol.
If you are an end-user, you can file a feature request with the "Driver Request"
tag on [libfprint's issue page](https://gitlab.freedesktop.org/libfprint/libfprint/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=Driver%20Request),
or subscribe to an existing feature request there.
If you are an enterprising hacker, please file a new merge request with
the driver patches integrated.

238
INSTALL
View File

@@ -1,234 +1,6 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.
libfprint uses the Meson build system. See
http://mesonbuild.com/Quick-guide.html for details on how to
compile libfprint.
The "meson configure" command will give you a list of available
command-line options.

View File

@@ -1,22 +0,0 @@
AUTOMAKE_OPTIONS = dist-bzip2
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = THANKS TODO HACKING libfprint.pc.in
DISTCLEANFILES = ChangeLog libfprint.pc
SUBDIRS = libfprint doc
if BUILD_EXAMPLES
SUBDIRS += examples
endif
pkgconfigdir=$(libdir)/pkgconfig
pkgconfig_DATA=libfprint.pc
.PHONY: ChangeLog dist-up
ChangeLog:
git --git-dir $(top_srcdir)/.git log > ChangeLog || touch ChangeLog
dist-hook: ChangeLog
dist-up: dist
rsync $(distdir).tar.bz2 frs.sourceforge.net:uploads/

174
NEWS
View File

@@ -1,6 +1,180 @@
This file lists notable changes in each release. For the full history of all
changes, see ChangeLog.
2019-08-08: v1.0 release
* Library:
- Add guards to the public API and require GLib 2.50
- Deprecate print storage API
- Better documentation for fp_discover_devs()
- Remove unused internal fpi_timeout_cancel_for_dev()
- Remove state from fp_img_driver activate handler
- Bug fixes related to restarting a failed verification immediately
* Drivers:
- The Elan driver received a lot of bug fixes including a fix for a
hang when verifying prints with fprintd, quirks for some devices,
a memory leak fix and support for 04f3:0c42
- Fix a probable crash in all the AES drivers
- Add support for Lenovo Preferred Pro Keyboard (KUF1256) to vfs5011
- Prevent hang during enroll process in etes603 driver
- Fix possible integer overflow in uru4000
- Work-around SELinux AVC warnings when uru4000 driver starts
- Remove long-unmaintained and broken fdu2000 driver
* Tools/Examples:
- Fix examples not working due to an overly strict check
- Fix crash in GTK demo when there's no supported devices
- Disable GTK demo until we have a USB Flatpak portal
- Remove sleep() in enroll example which caused a crash in some drivers
- Add a simple storage implementation example
2018-12-14: v0.99.0 release
* Library:
- All the internal API for device driver writers is now covered by the
documentation and has been enhanced to make it easier to write drivers
- Update internal NBIS fingerprint data processing library to one that's
nearly 10 years newer
- Re-add accessor for minutia coordinates which was used in the very
old fprint_demo program, but also by our new GTK+ test program (see below)
- Fix a crash when too many minutiae were detected in a capture
* Drivers:
- Support more devices in the Elan driver, stability improvements
* Tools:
- Add a test GTK+ application that will eventually be used for testing
drivers without modifying the OS installed version. Note that this
application currently requires manually changing permissions of USB
devices, this will be fixed when the infrastructure exists to access
those devices without additional permissions, as a normal user.
2018-07-15: v0.8.2 release
* Drivers:
- Add USB ID for TNP Nano USB Fingerprint Reader
- Fix UPEKTS enrollment never finishing on some devices
* Library:
- Fix fp_get_pollfds() retval type, a small ABI change
- Downgrade fatal errors to run-time warnings, as a number of drivers
used to throw silent errors and we made them fatal. Those will now
be visible warnings, hopefully helping with fixing them.
2018-06-12: v0.8.1 release
- Brown paperbag release to install the udev rules file in the correct
directory if the udev pkg-config file doesn't have a trailing slash
2018-06-12: v0.8.0 release
- Port to meson as the build system
- Port documentation to gtk-doc
* Drivers:
- Add Elan driver
- Increase threshold to detect encryption on URU4000 devices
- Remove already replaced UPEKE2 driver
- Fix possible crash caused by vfs5011 when no lines were captured
* Library:
- Fix a number of memory and file descriptor leaks and warnings
- Make NSS (and URU4000) driver optional
- Fix assembling of frames for non-reverse and non reverse stripes
- Split internal private header to clarify drivers API
- Simplify logging system, now all the builds can be used to output
debug information
- Mark fp_dscv_print functions as deprecated
* Udev rules:
- Add some unsupported devices to the whitelist
2017-05-14: v0.7.0 release
* Drivers:
- Add VFS0050 driver
- Fix possible crash in AES3500 and AES4000
- Fix broken enrollment in VFS101
- Better verification with small sensor scanners
- Plenty of fixes in VFS5011
- Fix memory corruption in AES1610
- Improve calibration settings for AES1610
- Improve image assembling in upeksonly driver
- Autodetect whether image is encrypted in uru4k
* Library:
- NBIS: Remove false minutia at the edge of partial image
- Introduce routines to assemble image from lines (used in VFS5011 and upeksonly)
- Fix a bug that can cause driver state machine to enter into endless loop.
* Udev rules:
- Add driver name to the USB properties
* Plenty of build fixes
2015-02-03: v0.6.0 release
* Drivers:
- Reduce duplication between AES3500 and AES4000 drivers and
add support for AES3500 device
- Add support for UPEK 147e:2020 and Upek Eikon 2 devices
- Add EgisTec ES603 driver
- Add VFS5011 driver
- Always perform 5 scans for image enrollment
- Better verification with AES1660 driver
- Better verification for a number of AES drivers
* Library:
- Always use Pixman for image manipulation, gdk-pixbuf and ImageMagick
are not supported any more.
* Udev rules:
- Fix warning when USB hub or system does not support power management
2013-08-11: v0.5.1 release
* Drivers
- Add support for 147e:2020 to upeke2 driver
- Fix possible race condition, and cancellation in uru4000 driver
* Udev rules:
- Add Microsoft keyboard to the suspend blacklist
* Plenty of build fixes
2012-12-03: v0.5.0 release
* Drivers:
- New VFS300/VFS301 driver
- New AES2550/AES2810 drivers
- New AES1660 driver
- New AES2660 driver
- New DigitalPersona URU4500 driver
- Avoid empty capture and improve image contrast in the
AES2501 and AES2550 drivers
- Update upektc driver, add support for Eikon Touch 300
- Fix UrU4000 image capture on ARM
* Library:
- Fix global variable collisions with libusb and other system headers
- Fix possible crash in NBIS image processing with some fingerprints
* Udev rules:
- Fix power control path for newer kernels
- Move udev rules to the correct directory
- Don't print duplicated udev rules
- Include udev rules in the tarball
- Allow disabling of udev rules for cross-compiling
- Add driver names in the generated rules
2011-04-18: v0.4.0 release
* Add support for Validity VFS101 (USB ID 138a:0001)
* Fix crasher when resizing a fingerprint image
* Fix wrong timeout being returned when either of
libusb or libfprint doesn't have a timeout
2010-09-08: v0.3.0 release
* Add support for UPEK TCS4C (USB ID 147e:1000)
* Use NSS instead of OpenSSL for GPL compliance
* upeksonly driver bug fixes
* Fix a crash if a scan was shorter than 8 lines
* Fix compilation with C++ compiler
2010-08-19: v0.2.0 release (since 0.1.0-pre2)
* Add gdk-pixbuf support for image manipulation
* Add udev rules to allow devices to autosuspend

16
README
View File

@@ -2,7 +2,7 @@ libfprint
=========
libfprint is part of the fprint project:
http://www.reactivated.net/fprint
https://fprint.freedesktop.org/
libfprint was originally developed as part of an academic project at the
University of Manchester with the aim of hiding differences between different
@@ -22,7 +22,7 @@ university computers and the project is not hosted at the university either.
For more information on libfprint, supported devices, API documentation, etc.,
see the homepage:
http://www.reactivated.net/fprint/Libfprint
https://fprint.freedesktop.org/
libfprint is licensed under the GNU LGPL version 2.1. See the COPYING file
for the license text.
@@ -37,5 +37,15 @@ libfprint includes code from NIST's NBIS software distribution:
http://fingerprint.nist.gov/NBIS/index.html
We include bozorth3 from the US export controlled distribution. We have
determined that it is fine to ship bozorth3 in an open source project,
see http://reactivated.net/fprint/wiki/US_export_control
see https://fprint.freedesktop.org/us-export-control.html
## Historical links
Older versions of libfprint are available at:
https://sourceforge.net/projects/fprint/files/
Historical mailing-list archives:
http://www.reactivated.net/fprint_list_archives/
Historical website:
http://web.archive.org/web/*/https://www.freedesktop.org/wiki/Software/fprint/

1
THANKS
View File

@@ -8,3 +8,4 @@ Toby Howard (University of Manchester)
Seemant Kulleen
Pavel Herrman
Bastien Nocera
Greg Kerr and Martin Konecny from AuthenTec Inc - hardware donations (AES2550 device), datasheets for AES2550 and AES2810

View File

@@ -1,8 +0,0 @@
#!/bin/sh
libtoolize --copy --force || exit 1
aclocal || exit 1
autoheader || exit 1
autoconf || exit 1
automake -a -c || exit 1
./configure --enable-maintainer-mode --enable-examples-build \
--enable-x11-examples-build --enable-debug-log $*

3
code-of-conduct.md Normal file
View File

@@ -0,0 +1,3 @@
This project and its community follow the [Freedesktop.org code of conduct]
[Freedesktop.org code of conduct]: https://www.freedesktop.org/wiki/CodeOfConduct/

View File

@@ -1,205 +0,0 @@
AC_INIT([libfprint], [0.2.0])
AM_INIT_AUTOMAKE([1.11 dist-bzip2 no-dist-gzip check-news])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([libfprint/core.c])
AM_CONFIG_HEADER([config.h])
AC_PREREQ([2.50])
AC_PROG_CC
AC_PROG_LIBTOOL
AC_C_INLINE
AM_PROG_CC_C_O
AC_DEFINE([_GNU_SOURCE], [], [Use GNU extensions])
# Library versioning
lt_major="0"
lt_revision="0"
lt_age="0"
AC_SUBST(lt_major)
AC_SUBST(lt_revision)
AC_SUBST(lt_age)
all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes2501 aes4000"
require_imaging='no'
require_aeslib='no'
enable_upeke2='no'
enable_upekts='no'
enable_upektc='no'
enable_upeksonly='no'
enable_vcom5s='no'
enable_uru4000='no'
enable_fdu2000='no'
enable_aes1610='no'
enable_aes2501='no'
enable_aes4000='no'
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
[List of drivers to enable])],
[drivers="$withval"],
[drivers="$all_drivers"])
for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
case ${driver} in
upekts)
AC_DEFINE([ENABLE_UPEKTS], [], [Build UPEK TouchStrip driver])
enable_upekts="yes"
;;
upeke2)
AC_DEFINE([ENABLE_UPEKE2], [], [Build UPEK Eikon 2])
enable_upeke2="yes"
;;
upektc)
AC_DEFINE([ENABLE_UPEKTC], [], [Build UPEK TouchChip driver])
enable_upektc="yes"
;;
upeksonly)
AC_DEFINE([ENABLE_UPEKSONLY], [], [Build UPEK TouchStrip sensor-only driver])
enable_upeksonly="yes"
;;
uru4000)
AC_DEFINE([ENABLE_URU4000], [], [Build Digital Persona U.are.U 4000 driver])
enable_uru4000="yes"
;;
fdu2000)
AC_DEFINE([ENABLE_FDU2000], [], [Build Secugen FDU 2000 driver])
enable_fdu2000="yes"
;;
vcom5s)
AC_DEFINE([ENABLE_VCOM5S], [], [Build Veridicom 5thSense driver])
enable_vcom5s="yes"
;;
aes2501)
AC_DEFINE([ENABLE_AES2501], [], [Build AuthenTec AES2501 driver])
require_aeslib="yes"
enable_aes2501="yes"
;;
aes1610)
AC_DEFINE([ENABLE_AES1610], [], [Build AuthenTec AES1610 driver])
require_aeslib="yes"
enable_aes1610="yes"
;;
aes4000)
AC_DEFINE([ENABLE_AES4000], [], [Build AuthenTec AES4000 driver])
require_aeslib="yes"
require_imaging="yes"
enable_aes4000="yes"
;;
esac
done
AM_CONDITIONAL([ENABLE_UPEKTS], [test "$enable_upekts" != "no"])
AM_CONDITIONAL([ENABLE_UPEKE2], [test "$enable_upeke2" != "no"])
#AM_CONDITIONAL([ENABLE_UPEKTC], [test "$enable_upektc" != "no"])
AM_CONDITIONAL([ENABLE_UPEKSONLY], [test "$enable_upeksonly" != "no"])
AM_CONDITIONAL([ENABLE_VCOM5S], [test "$enable_vcom5s" != "no"])
AM_CONDITIONAL([ENABLE_URU4000], [test "$enable_uru4000" != "no"])
#AM_CONDITIONAL([ENABLE_FDU2000], [test "$enable_fdu2000" != "no"])
AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" != "no"])
AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" != "no"])
AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" != "no"])
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" != "no"])
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
AC_SUBST(LIBUSB_CFLAGS)
AC_SUBST(LIBUSB_LIBS)
# check for OpenSSL's libcrypto
PKG_CHECK_MODULES(CRYPTO, "libcrypto")
AC_SUBST(CRYPTO_CFLAGS)
AC_SUBST(CRYPTO_LIBS)
PKG_CHECK_MODULES(GLIB, "glib-2.0")
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
imagemagick_found=no
gdkpixbuf_found=no
if test "$require_imaging" != "no"; then
PKG_CHECK_MODULES(IMAGING, gthread-2.0 gdk-pixbuf-2.0, [gdkpixbuf_found=yes], [gdkpixbuf_found=no])
if test "$gdkpixbuf_found" != "yes"; then
PKG_CHECK_MODULES(IMAGING, ImageMagick, [imagemagick_found=yes], [imagemagick_found=no])
fi
fi
if test "$require_imaging" != "no"; then
if test "$gdkpixbuf_found" != "yes" && test "$imagemagick_found" != "yes"; then
AC_MSG_ERROR([gdk-pixbuf or ImageMagick is required for imaging support])
fi
fi
AM_CONDITIONAL([REQUIRE_GDKPIXBUF], [test "$gdkpixbuf_found" != "no"])
AM_CONDITIONAL([REQUIRE_IMAGEMAGICK], [test "$imagemagick_found" != "no"])
AC_SUBST(IMAGING_CFLAGS)
AC_SUBST(IMAGING_LIBS)
# Examples build
AC_ARG_ENABLE([examples-build], [AS_HELP_STRING([--enable-examples-build],
[build example applications (default n)])],
[build_examples=$enableval],
[build_examples='no'])
AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != "xno"])
# Examples build
AC_ARG_ENABLE([x11-examples-build], [AS_HELP_STRING([--enable-x11-examples-build],
[build X11 example applications (default n)])],
[build_x11_examples=$enableval],
[build_x11_examples='no'])
AM_CONDITIONAL([BUILD_X11_EXAMPLES], [test "x$build_x11_examples" != "xno"])
if test "x$build_x11_examples" != "xno"; then
# check for Xv extensions
# imported from Coriander
AC_DEFUN([AC_CHECK_XV],[
AC_SUBST(XV_CFLAGS)
AC_SUBST(XV_LIBS)
AC_MSG_CHECKING(for Xv extensions)
AC_TRY_COMPILE([
#include <X11/Xlib.h>
#include <X11/extensions/Xvlib.h>],[
int main(void) { (void) XvGetPortAttribute(0, 0, 0, 0); return 0; }
],xv=yes,xv=no);
AC_MSG_RESULT($xv)
if test x$xv = xyes; then
XV_LIBS="-lXv -lXext"
XV_CFLAGS=""
AC_DEFINE(HAVE_XV,1,[defined if XV video overlay is available])
else
AC_MSG_ERROR([XV is required for X11 examples])
fi
])
AC_CHECK_XV
fi
# Message logging
AC_ARG_ENABLE([log], [AS_HELP_STRING([--disable-log], [disable all logging])],
[log_enabled=$enableval],
[log_enabled='yes'])
if test "x$log_enabled" != "xno"; then
AC_DEFINE([ENABLE_LOGGING], 1, [Message logging])
fi
AC_ARG_ENABLE([debug-log], [AS_HELP_STRING([--enable-debug-log],
[enable debug logging (default n)])],
[debug_log_enabled=$enableval],
[debug_log_enabled='no'])
if test "x$debug_log_enabled" != "xno"; then
AC_DEFINE([ENABLE_DEBUG_LOGGING], 1, [Debug message logging])
fi
# Restore gnu89 inline semantics on gcc 4.3 and newer
saved_cflags="$CFLAGS"
CFLAGS="$CFLAGS -fgnu89-inline"
AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]), inline_cflags="-fgnu89-inline", inline_cflags="")
CFLAGS="$saved_cflags"
AC_DEFINE([API_EXPORTED], [__attribute__((visibility("default")))], [Default visibility])
AM_CFLAGS="-std=gnu99 $inline_cflags -Wall -Wundef -Wunused -Wstrict-prototypes -Werror-implicit-function-declaration -Wno-pointer-sign -Wshadow"
AC_SUBST(AM_CFLAGS)
AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] [examples/Makefile] [doc/Makefile])
AC_OUTPUT

523
demo/gtk-libfprint-test.c Normal file
View File

@@ -0,0 +1,523 @@
/*
* Example libfprint GTK+ image capture program
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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 "config.h"
#include <gtk/gtk.h>
#include <libfprint/fprint.h>
#include "loop.h"
typedef GtkApplication LibfprintDemo;
typedef GtkApplicationClass LibfprintDemoClass;
G_DEFINE_TYPE (LibfprintDemo, libfprint_demo, GTK_TYPE_APPLICATION)
typedef enum {
IMAGE_DISPLAY_NONE = 0,
IMAGE_DISPLAY_MINUTIAE = 1 << 0,
IMAGE_DISPLAY_BINARY = 1 << 1
} ImageDisplayFlags;
typedef struct {
GtkApplicationWindow parent_instance;
GtkWidget *header_bar;
GtkWidget *mode_stack;
GtkWidget *capture_button;
GtkWidget *capture_image;
GtkWidget *spinner;
GtkWidget *instructions;
struct fp_dscv_dev *ddev;
struct fp_dev *dev;
struct fp_img *img;
ImageDisplayFlags img_flags;
} LibfprintDemoWindow;
typedef GtkApplicationWindowClass LibfprintDemoWindowClass;
G_DEFINE_TYPE (LibfprintDemoWindow, libfprint_demo_window, GTK_TYPE_APPLICATION_WINDOW)
typedef enum {
EMPTY_MODE,
NOIMAGING_MODE,
CAPTURE_MODE,
SPINNER_MODE,
ERROR_MODE
} LibfprintDemoMode;
static void libfprint_demo_set_mode (LibfprintDemoWindow *win,
LibfprintDemoMode mode);
static void
pixbuf_destroy (guchar *pixels, gpointer data)
{
if (pixels == NULL)
return;
g_free (pixels);
}
static unsigned char *
img_to_rgbdata (struct fp_img *img,
int width,
int height)
{
int size = width * height;
unsigned char *imgdata = fp_img_get_data (img);
unsigned char *rgbdata = g_malloc (size * 3);
size_t i;
size_t rgb_offset = 0;
for (i = 0; i < size; i++) {
unsigned char pixel = imgdata[i];
rgbdata[rgb_offset++] = pixel;
rgbdata[rgb_offset++] = pixel;
rgbdata[rgb_offset++] = pixel;
}
return rgbdata;
}
static void
plot_minutiae (unsigned char *rgbdata,
int width,
int height,
struct fp_minutia **minlist,
int nr_minutiae)
{
int i;
#define write_pixel(num) do { \
rgbdata[((num) * 3)] = 0xff; \
rgbdata[((num) * 3) + 1] = 0; \
rgbdata[((num) * 3) + 2] = 0; \
} while(0)
for (i = 0; i < nr_minutiae; i++) {
struct fp_minutia *min = minlist[i];
int x, y;
size_t pixel_offset;
fp_minutia_get_coords(min, &x, &y);
pixel_offset = (y * width) + x;
write_pixel(pixel_offset - 2);
write_pixel(pixel_offset - 1);
write_pixel(pixel_offset);
write_pixel(pixel_offset + 1);
write_pixel(pixel_offset + 2);
write_pixel(pixel_offset - (width * 2));
write_pixel(pixel_offset - (width * 1) - 1);
write_pixel(pixel_offset - (width * 1));
write_pixel(pixel_offset - (width * 1) + 1);
write_pixel(pixel_offset + (width * 1) - 1);
write_pixel(pixel_offset + (width * 1));
write_pixel(pixel_offset + (width * 1) + 1);
write_pixel(pixel_offset + (width * 2));
}
}
static GdkPixbuf *
img_to_pixbuf (struct fp_img *img,
ImageDisplayFlags flags)
{
int width;
int height;
unsigned char *rgbdata;
width = fp_img_get_width (img);
height = fp_img_get_height (img);
if (flags & IMAGE_DISPLAY_BINARY) {
struct fp_img *binary;
binary = fp_img_binarize (img);
rgbdata = img_to_rgbdata (binary, width, height);
fp_img_free (binary);
} else {
rgbdata = img_to_rgbdata (img, width, height);
}
if (flags & IMAGE_DISPLAY_MINUTIAE) {
struct fp_minutia **minlist;
int nr_minutiae;
minlist = fp_img_get_minutiae (img, &nr_minutiae);
plot_minutiae (rgbdata, width, height, minlist, nr_minutiae);
}
return gdk_pixbuf_new_from_data (rgbdata, GDK_COLORSPACE_RGB,
FALSE, 8, width, height,
width * 3, pixbuf_destroy,
NULL);
}
static void
update_image (LibfprintDemoWindow *win)
{
GdkPixbuf *pixbuf;
if (win->img == NULL) {
gtk_image_clear (GTK_IMAGE (win->capture_image));
return;
}
g_debug ("Updating image, minutiae %s, binary mode %s",
win->img_flags & IMAGE_DISPLAY_MINUTIAE ? "shown" : "hidden",
win->img_flags & IMAGE_DISPLAY_BINARY ? "on" : "off");
pixbuf = img_to_pixbuf (win->img, win->img_flags);
gtk_image_set_from_pixbuf (GTK_IMAGE (win->capture_image), pixbuf);
g_object_unref (pixbuf);
}
static void
libfprint_demo_set_spinner_label (LibfprintDemoWindow *win,
const char *message)
{
char *label;
label = g_strdup_printf ("<b><span size=\"large\">%s</span></b>", message);
gtk_label_set_markup (GTK_LABEL (win->instructions), label);
g_free (label);
}
static void
libfprint_demo_set_capture_label (LibfprintDemoWindow *win)
{
struct fp_driver *drv;
enum fp_scan_type scan_type;
const char *message;
drv = fp_dscv_dev_get_driver (win->ddev);
scan_type = fp_driver_get_scan_type(drv);
switch (scan_type) {
case FP_SCAN_TYPE_PRESS:
message = "Place your finger on the fingerprint reader";
break;
case FP_SCAN_TYPE_SWIPE:
message = "Swipe your finger across the fingerprint reader";
break;
default:
g_assert_not_reached ();
}
libfprint_demo_set_spinner_label (win, message);
}
static void
dev_capture_start_cb (struct fp_dev *dev,
int result,
struct fp_img *img,
void *user_data)
{
LibfprintDemoWindow *win = user_data;
if (result < 0) {
libfprint_demo_set_mode (win, ERROR_MODE);
return;
}
fp_async_capture_stop (dev, NULL, NULL);
win->img = img;
update_image (win);
libfprint_demo_set_mode (win, CAPTURE_MODE);
}
static void
dev_open_cb (struct fp_dev *dev, int status, void *user_data)
{
LibfprintDemoWindow *win = user_data;
int r;
if (status < 0) {
libfprint_demo_set_mode (win, ERROR_MODE);
return;
}
libfprint_demo_set_capture_label (win);
win->dev = dev;
r = fp_async_capture_start (win->dev, FALSE, dev_capture_start_cb, user_data);
if (r < 0) {
g_warning ("fp_async_capture_start failed: %d", r);
libfprint_demo_set_mode (win, ERROR_MODE);
return;
}
}
static void
activate_capture (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
LibfprintDemoWindow *win = user_data;
int r;
libfprint_demo_set_mode (win, SPINNER_MODE);
g_clear_pointer (&win->img, fp_img_free);
if (win->dev != NULL) {
dev_open_cb (win->dev, 0, user_data);
return;
}
libfprint_demo_set_spinner_label (win, "Opening fingerprint reader");
r = fp_async_dev_open (win->ddev, dev_open_cb, user_data);
if (r < 0) {
g_warning ("fp_async_dev_open failed: %d", r);
libfprint_demo_set_mode (win, ERROR_MODE);
return;
}
}
static void
activate_quit (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkApplication *app = user_data;
GtkWidget *win;
GList *list, *next;
list = gtk_application_get_windows (app);
while (list)
{
win = list->data;
next = list->next;
gtk_widget_destroy (GTK_WIDGET (win));
list = next;
}
}
static void
activate_show_minutiae (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
LibfprintDemoWindow *win = user_data;
GVariant *state;
gboolean new_state;
state = g_action_get_state (G_ACTION (action));
new_state = !g_variant_get_boolean (state);
g_action_change_state (G_ACTION (action), g_variant_new_boolean (new_state));
g_variant_unref (state);
if (new_state)
win->img_flags |= IMAGE_DISPLAY_MINUTIAE;
else
win->img_flags &= ~IMAGE_DISPLAY_MINUTIAE;
update_image (win);
}
static void
activate_show_binary (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
LibfprintDemoWindow *win = user_data;
GVariant *state;
gboolean new_state;
state = g_action_get_state (G_ACTION (action));
new_state = !g_variant_get_boolean (state);
g_action_change_state (G_ACTION (action), g_variant_new_boolean (new_state));
g_variant_unref (state);
if (new_state)
win->img_flags |= IMAGE_DISPLAY_BINARY;
else
win->img_flags &= ~IMAGE_DISPLAY_BINARY;
update_image (win);
}
static void
change_show_minutiae_state (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
g_simple_action_set_state (action, state);
}
static void
change_show_binary_state (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
g_simple_action_set_state (action, state);
}
static GActionEntry app_entries[] = {
{ "quit", activate_quit, NULL, NULL, NULL },
};
static GActionEntry win_entries[] = {
{ "show-minutiae", activate_show_minutiae, NULL, "false", change_show_minutiae_state },
{ "show-binary", activate_show_binary, NULL, "false", change_show_binary_state },
{ "capture", activate_capture, NULL, NULL, NULL }
};
static void
activate (GApplication *app)
{
LibfprintDemoWindow *window;
window = g_object_new (libfprint_demo_window_get_type (),
"application", app,
NULL);
gtk_widget_show (GTK_WIDGET (window));
}
static void
libfprint_demo_set_mode (LibfprintDemoWindow *win,
LibfprintDemoMode mode)
{
struct fp_driver *drv;
char *title;
switch (mode) {
case EMPTY_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "empty-mode");
gtk_widget_set_sensitive (win->capture_button, FALSE);
gtk_spinner_stop (GTK_SPINNER (win->spinner));
break;
case NOIMAGING_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "noimaging-mode");
gtk_widget_set_sensitive (win->capture_button, FALSE);
gtk_spinner_stop (GTK_SPINNER (win->spinner));
break;
case CAPTURE_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "capture-mode");
gtk_widget_set_sensitive (win->capture_button, TRUE);
drv = fp_dscv_dev_get_driver (win->ddev);
title = g_strdup_printf ("%s Test", fp_driver_get_full_name (drv));
gtk_header_bar_set_title (GTK_HEADER_BAR (win->header_bar), title);
g_free (title);
gtk_spinner_stop (GTK_SPINNER (win->spinner));
break;
case SPINNER_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "spinner-mode");
gtk_widget_set_sensitive (win->capture_button, FALSE);
gtk_spinner_start (GTK_SPINNER (win->spinner));
break;
case ERROR_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "error-mode");
gtk_widget_set_sensitive (win->capture_button, FALSE);
gtk_spinner_stop (GTK_SPINNER (win->spinner));
break;
default:
g_assert_not_reached ();
}
}
static void
libfprint_demo_init (LibfprintDemo *app)
{
g_action_map_add_action_entries (G_ACTION_MAP (app),
app_entries, G_N_ELEMENTS (app_entries),
app);
}
static void
libfprint_demo_class_init (LibfprintDemoClass *class)
{
GApplicationClass *app_class = G_APPLICATION_CLASS (class);
app_class->activate = activate;
}
static void
libfprint_demo_window_init (LibfprintDemoWindow *window)
{
struct fp_dscv_dev **discovered_devs;
gtk_widget_init_template (GTK_WIDGET (window));
gtk_window_set_default_size (GTK_WINDOW (window), 700, 500);
g_action_map_add_action_entries (G_ACTION_MAP (window),
win_entries, G_N_ELEMENTS (win_entries),
window);
if (fp_init () < 0) {
libfprint_demo_set_mode (window, ERROR_MODE);
return;
}
setup_pollfds ();
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
libfprint_demo_set_mode (window, ERROR_MODE);
return;
}
/* Empty list? */
if (discovered_devs[0] == NULL) {
fp_dscv_devs_free (discovered_devs);
libfprint_demo_set_mode (window, EMPTY_MODE);
return;
}
if (!fp_driver_supports_imaging(fp_dscv_dev_get_driver(discovered_devs[0]))) {
libfprint_demo_set_mode (window, NOIMAGING_MODE);
return;
}
window->ddev = discovered_devs[0];
libfprint_demo_set_mode (window, CAPTURE_MODE);
}
static void
libfprint_demo_window_class_init (LibfprintDemoWindowClass *class)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
gtk_widget_class_set_template_from_resource (widget_class, "/libfprint_demo/gtk-libfprint-test.ui");
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, header_bar);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, mode_stack);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, capture_button);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, capture_image);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, spinner);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, instructions);
//FIXME setup dispose
}
int main (int argc, char **argv)
{
GtkApplication *app;
app = GTK_APPLICATION (g_object_new (libfprint_demo_get_type (),
"application-id", "org.freedesktop.libfprint.Demo",
"flags", G_APPLICATION_FLAGS_NONE,
NULL));
return g_application_run (G_APPLICATION (app), 0, NULL);
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/libfprint_demo">
<file>gtk-libfprint-test.ui</file>
</gresource>
</gresources>

351
demo/gtk-libfprint-test.ui Normal file
View File

@@ -0,0 +1,351 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="LibfprintDemoWindow" parent="GtkApplicationWindow">
<property name="can_focus">False</property>
<property name="default_width">500</property>
<property name="default_height">400</property>
<property name="show_menubar">False</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="header_bar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Fingerprint Reader Test</property>
<property name="subtitle">Capture test</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkMenuButton" id="option-menu-button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="valign">center</property>
<property name="menu-model">options-menu</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
<child>
<object class="GtkButton" id="capture_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="valign">center</property>
<property name="action_name">win.capture</property>
<child>
<object class="GtkLabel" id="capture_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Capture</property>
<property name="use_underline">True</property>
</object>
</child>
<style>
<class name="text-button"/>
</style>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkStack" id="mode_stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkGrid" id="spinner-mode">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<property name="row_spacing">12</property>
<child>
<object class="GtkSpinner" id="spinner">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">9</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="instructions">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">9</property>
<property name="label" translatable="yes">&lt;b&gt;&lt;span size="large"&gt;Please press finger on reader&lt;/span&gt;&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="name">spinner-mode</property>
<property name="title">spinner-mode</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="empty-mode">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<property name="row_spacing">12</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">9</property>
<property name="pixel_size">192</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">9</property>
<property name="label" translatable="yes">&lt;b&gt;&lt;span size="large"&gt;No fingerprint readers found&lt;/span&gt;&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Please connect a supported fingerprint reader and start the application again</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="name">empty-mode</property>
<property name="title">empty-mode</property>
</packing>
</child>
<child>
<object class="GtkAspectFrame" id="capture-mode">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<property name="ratio">1.2999999523162842</property>
<child>
<object class="GtkImage" id="capture_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
</object>
<packing>
<property name="name">capture-mode</property>
<property name="title">capture-mode</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="error-mode">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<property name="row_spacing">12</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">9</property>
<property name="pixel_size">192</property>
<property name="icon_name">dialog-warning-symbolic</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">9</property>
<property name="label" translatable="yes">&lt;b&gt;&lt;span size="large"&gt;An error occurred trying to access the fingerprint reader&lt;/span&gt;&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Please consult the debugging output before restarting the application</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="name">error-mode</property>
<property name="title">error-mode</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="noimaging-mode">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<property name="row_spacing">12</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">9</property>
<property name="pixel_size">192</property>
<property name="icon_name">scanner-symbolic</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">9</property>
<property name="label" translatable="yes">&lt;b&gt;&lt;span size="large"&gt;Fingerprint reader does not support capturing images&lt;/span&gt;&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Please connect a supported fingerprint reader and start the application again</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="name">noimaging-mode</property>
<property name="title">noimaging-mode</property>
<property name="position">3</property>
</packing>
</child>
</object>
</child>
</template>
<menu id="options-menu">
<section>
<item>
<attribute name="label" translatable="yes">Show Minutiae</attribute>
<attribute name="action">win.show-minutiae</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Show Binary</attribute>
<attribute name="action">win.show-binary</attribute>
</item>
</section>
</menu>
</interface>

196
demo/loop.c Normal file
View File

@@ -0,0 +1,196 @@
/*
* fprint D-Bus daemon
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <glib.h>
#include <libfprint/fprint.h>
#include <poll.h>
#include <stdlib.h>
#include "loop.h"
struct fdsource {
GSource source;
GSList *pollfds;
};
static gboolean source_prepare(GSource *source, gint *timeout)
{
int r;
struct timeval tv;
r = fp_get_next_timeout(&tv);
if (r == 0) {
*timeout = -1;
return FALSE;
}
if (!timerisset(&tv))
return TRUE;
*timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
return FALSE;
}
static gboolean source_check(GSource *source)
{
struct fdsource *_fdsource = (struct fdsource *) source;
GSList *l;
struct timeval tv;
int r;
if (!_fdsource->pollfds)
return FALSE;
for (l = _fdsource->pollfds; l != NULL; l = l->next) {
GPollFD *pollfd = l->data;
if (pollfd->revents)
return TRUE;
}
r = fp_get_next_timeout(&tv);
if (r == 1 && !timerisset(&tv))
return TRUE;
return FALSE;
}
static gboolean source_dispatch(GSource *source, GSourceFunc callback,
gpointer data)
{
struct timeval zerotimeout = {
.tv_sec = 0,
.tv_usec = 0,
};
/* FIXME error handling */
fp_handle_events_timeout(&zerotimeout);
/* FIXME whats the return value used for? */
return TRUE;
}
static void source_finalize(GSource *source)
{
struct fdsource *_fdsource = (struct fdsource *) source;
GSList *l;
if (!_fdsource->pollfds)
return;
for (l = _fdsource->pollfds; l != NULL; l = l->next) {
GPollFD *pollfd = l->data;
g_source_remove_poll((GSource *) _fdsource, pollfd);
g_slice_free(GPollFD, pollfd);
_fdsource->pollfds = g_slist_delete_link(_fdsource->pollfds, l);
}
g_slist_free(_fdsource->pollfds);
}
static GSourceFuncs sourcefuncs = {
.prepare = source_prepare,
.check = source_check,
.dispatch = source_dispatch,
.finalize = source_finalize,
};
static struct fdsource *fdsource = NULL;
static void pollfd_add(int fd, short events)
{
GPollFD *pollfd;
pollfd = g_slice_new(GPollFD);
pollfd->fd = fd;
pollfd->events = 0;
pollfd->revents = 0;
if (events & POLLIN)
pollfd->events |= G_IO_IN;
if (events & POLLOUT)
pollfd->events |= G_IO_OUT;
fdsource->pollfds = g_slist_prepend(fdsource->pollfds, pollfd);
g_source_add_poll((GSource *) fdsource, pollfd);
}
static void pollfd_added_cb(int fd, short events)
{
g_debug("now monitoring fd %d", fd);
pollfd_add(fd, events);
}
static void pollfd_removed_cb(int fd)
{
GSList *l;
g_debug("no longer monitoring fd %d", fd);
if (!fdsource->pollfds) {
g_debug("cannot remove from list as list is empty?");
return;
}
for (l = fdsource->pollfds; l != NULL; l = l->next) {
GPollFD *pollfd = l->data;
if (pollfd->fd != fd)
continue;
g_source_remove_poll((GSource *) fdsource, pollfd);
g_slice_free(GPollFD, pollfd);
fdsource->pollfds = g_slist_delete_link(fdsource->pollfds, l);
return;
}
g_error("couldn't find fd %d in list\n", fd);
}
int setup_pollfds(void)
{
ssize_t numfds;
size_t i;
struct fp_pollfd *fpfds;
GSource *gsource;
gsource = g_source_new(&sourcefuncs, sizeof(struct fdsource));
fdsource = (struct fdsource *) gsource;
fdsource->pollfds = NULL;
numfds = fp_get_pollfds(&fpfds);
if (numfds < 0) {
if (fpfds)
free(fpfds);
return (int) numfds;
} else if (numfds > 0) {
for (i = 0; i < numfds; i++) {
struct fp_pollfd *fpfd = &fpfds[i];
pollfd_add(fpfd->fd, fpfd->events);
}
}
free(fpfds);
fp_set_pollfd_notifiers(pollfd_added_cb, pollfd_removed_cb);
g_source_attach(gsource, NULL);
return 0;
}

27
demo/loop.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef POLL_H
#define POLL_H
int setup_pollfds(void);
#endif

30
demo/meson.build Normal file
View File

@@ -0,0 +1,30 @@
gtk_test_resources = gnome.compile_resources('gtk-test-resources', 'gtk-libfprint-test.gresource.xml',
source_dir : '.',
c_name : 'gtk_test')
prefix = get_option('prefix')
bindir = join_paths(prefix, get_option('bindir'))
datadir = join_paths(prefix, get_option('datadir'))
executable('gtk-libfprint-test',
[ 'gtk-libfprint-test.c', 'loop.c', 'loop.h', gtk_test_resources ],
dependencies: [ libfprint_dep, gtk_dep ],
include_directories: [
root_inc,
],
c_args: [ common_cflags,
'-DPACKAGE_VERSION="' + meson.project_version() + '"' ],
install: true,
install_dir: bindir)
appdata = 'org.freedesktop.libfprint.Demo.appdata.xml'
install_data(appdata,
install_dir: join_paths(datadir, 'metainfo'))
desktop = 'org.freedesktop.libfprint.Demo.desktop'
install_data(desktop,
install_dir: join_paths(datadir, 'applications'))
icon = 'org.freedesktop.libfprint.Demo.png'
install_data(icon,
install_dir: join_paths(datadir, 'icons'))

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>org.freedesktop.libfprint.Demo.desktop</id>
<name>Fingerprint Reader Demo</name>
<summary>Test fingerprint readers</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>LGPL-2.1+</project_license>
<description>
<p>
Fingerprint Reader Demo is a test application for the libfprint
fingerprint reader library. Its purpose is to test drivers, new and old,
in a sandbox, to make sure that the drivers and associated functions
work correctly.
</p>
<p>
Fingerprint Reader Demo does not modify the system, or replace integration
in desktop environments.
</p>
</description>
<url type="homepage">https://fprint.freedesktop.org</url>
<screenshots>
<screenshot type="default" width="1024" height="576">https://git.gnome.org/browse/totem/plain/data/appdata/ss-main.png</screenshot>
<screenshot width="1024" height="576">https://git.gnome.org/browse/totem/plain/data/appdata/ss-music-playlist.png</screenshot>
</screenshots>
<updatecontact>hadess@hadess.net</updatecontact>
<!-- Incorrect, but appstream-util won't validate without it -->
<translation type="gettext">libfprint</translation>
</component>

View File

@@ -0,0 +1,10 @@
[Desktop Entry]
Name=Fingerprint Reader Demo
Comment=Test fingerprint readers
Keywords=finger;print;fingerprint;fprint;demo;driver;reader;
Exec=gtk-libfprint-test
Icon=org.freedesktop.libfprint.Demo
Terminal=false
Type=Application
Categories=GTK;GNOME;Development;System;
StartupNotify=true

View File

@@ -0,0 +1,51 @@
{
"app-id": "org.freedesktop.libfprint.Demo",
"runtime": "org.gnome.Platform",
"runtime-version": "master",
"sdk": "org.gnome.Sdk",
"command": "gtk-libfprint-test",
"finish-args": [
/* X11 + XShm access */
"--share=ipc", "--socket=x11",
/* Wayland access */
"--socket=wayland",
/* OpenGL access */
"--device=dri",
/* USB access */
"--device=all"
],
"cleanup": [ "/include", "/lib/pkgconfig/" ],
"modules": [
{
"name": "libusb",
"config-opts": [ "--disable-static", "--disable-udev" ],
"cleanup": [
"/lib/*.la",
"/lib/pkgconfig",
"/include"
],
"sources": [
{
"type": "archive",
"url": "https://github.com/libusb/libusb/archive/v1.0.22.tar.gz",
"sha256": "3500f7b182750cd9ccf9be8b1df998f83df56a39ab264976bdb3307773e16f48"
}
],
"post-install": [
"install -Dm644 COPYING /app/share/licenses/libusb/COPYING"
]
},
{
"name": "libfprint",
"buildsystem": "meson",
"config-opts": [ "-Dudev_rules=false", "-Dx11-examples=false", "-Dgtk-examples=true" ],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/libfprint/libfprint.git",
"branch": "wip/hadess/gtk-example"
}
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,10 +0,0 @@
EXTRA_DIST = doxygen.cfg
docs: doxygen.cfg
doxygen $^
docs-upload: docs
ln -s html api
ncftpput -f ~/.ncftp/reactivated -m -R httpdocs/fprint api/
rm -f api

115
doc/advanced-topics.xml Normal file
View File

@@ -0,0 +1,115 @@
<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
]>
<chapter id="advanced-topics" xmlns:xi="http://www.w3.org/2003/XInclude">
<title>Advanced Topics</title>
<refsect2 id="compatibility">
<title>Device and print compatibility</title>
<para>
Moving off generic conceptual ideas and onto libfprint-specific
implementation details, here are some introductory notes regarding how
libfprint copes with compatibility of fingerprints.
</para>
<para>
libfprint deals with a whole variety of different fingerprint readers and
the design includes considerations of compatibility and interoperability
between multiple devices. Your application should also be prepared to
work with more than one type of fingerprint reader and should consider that
enrolled fingerprint X may not be compatible with the device the user has
plugged in today.
</para>
<para>
libfprint implements the principle that fingerprints from different devices
are not necessarily compatible. For example, different devices may see
significantly different areas of fingerprint surface, and comparing images
between the devices would be unreliable. Also, devices can stretch and
distort images in different ways.
</para>
<para>
libfprint also implements the principle that in some cases, fingerprints
<emphasis>are</emphasis> compatible between different devices. If you go and buy two
identical fingerprint readers, it seems logical that you should be able
to enroll on one and verify on another without problems.
</para>
<para>
libfprint takes a fairly simplistic approach to these issues. Internally,
fingerprint hardware is driven by individual drivers. libfprint enforces
that a fingerprint that came from a device backed by driver X is never
compared to a fingerprint that came from a device backed by driver Y.
</para>
<para>
Additionally, libfprint is designed for the situation where a single driver
may support a range of devices which differ in imaging or scanning
properties. For example, a driver may support two ranges of devices which
even though are programmed over the same interface, one device sees
substantially less of the finger flesh, therefore images from the two
device types should be incompatible despite being from the same driver. To
implement this, each driver assigns a <emphasis>device type</emphasis> to each device
that it detects based on its imaging characteristics. libfprint ensures that
two prints being compared have the same device type.
</para>
<para>
In summary, libfprint represents fingerprints in several internal structures
and each representation will offer you a way of determining the
<ulink url="#driver_id">driver ID</ulink> and <ulink url="#device-types">devtype</ulink> of the print in
question. Prints are only compatible if the driver ID <emphasis role="strong">and</emphasis> devtypes
match. libfprint does offer you some "is this print compatible?" helper
functions, so you don't have to worry about these details too much.
</para>
</refsect2>
<refsect2 id="driver_id">
<title>Driver IDs</title>
<para>
Each driver is assigned a unique ID by the project maintainer. These
assignments are
<ulink url="https://gitlab.freedesktop.org/libfprint/libfprint/blob/master/libfprint/drivers/driver_ids.h">
documented in the sources</ulink> and will never change.
</para>
<para>
The only reason you may be interested in retrieving the driver ID for a
driver is for the purpose of checking if some print data is compatible
with a device. libfprint uses the driver ID as one way of checking that
the print you are trying to verify is compatible with the device in
question - it ensures that enrollment data from one driver is never fed to
another. Note that libfprint does provide you with helper functions to
determine whether a print is compatible with a device, so under most
circumstances, you don't have to worry about driver IDs at all.
</para>
</refsect2>
<refsect2 id="device-types">
<title>Device types</title>
<para>
Internally, the <ulink url="libfprint-Driver-operations.html#libfprint-Driver-operations.description">driver</ulink> behind a device assigns a 32-bit
<emphasis>devtype</emphasis> identifier to the device. This cannot be used as a unique
ID for a specific device as many devices under the same range may share
the same devtype. The devtype may even be 0 in all cases.
</para>
<para>
The only reason you may be interested in retrieving the devtype for a
device is for the purpose of checking if some print data is compatible
with a device. libfprint uses the devtype as one way of checking that the
print you are verifying is compatible with the device in question - the
devtypes must be equal. This effectively allows drivers to support more
than one type of device where the data from each one is not compatible with
the other. Note that libfprint does provide you with helper functions to
determine whether a print is compatible with a device, so under most
circumstances, you don't have to worry about devtypes at all.
</para>
</refsect2>
</chapter>

File diff suppressed because it is too large Load Diff

30
doc/getting-started.xml Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
]>
<chapter id="getting-started" xmlns:xi="http://www.w3.org/2003/XInclude">
<title>Getting Started</title>
<para>
libfprint includes several simple functional examples under the <computeroutput>examples/</computeroutput>
directory in the libfprint source distribution. Those are good starting
points.
</para>
<para>
Usually the first thing you want to do is determine which fingerprint
devices are present. This is done through <ulink url="libfprint-Device-discovery.html">device discovery</ulink>.
</para>
<para>
Once you have found a device you would like to operate, you should open it.
Refer to <ulink url="libfprint-Devices-operations.html">device operations</ulink>. This section also details enrollment,
image capture, and verification.
</para>
<para>
That should be enough to get you started, but do remember there are
documentation pages on other aspects of libfprint's API (see the modules
page).
</para>
</chapter>

106
doc/intro.xml Normal file
View File

@@ -0,0 +1,106 @@
<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
]>
<chapter id="intro" xmlns:xi="http://www.w3.org/2003/XInclude">
<title>Introduction</title>
<para>
libfprint is an open source library to provide access to fingerprint
scanning devices. For more info, see the
<ulink url="https://fprint.freedesktop.org/">libfprint project homepage</ulink>.
</para>
<para>
This documentation is aimed at application developers who wish to integrate
fingerprint-related functionality into their software. libfprint has been
designed so that you only have to do this once by integrating your
software with libfprint, you'll be supporting all the fingerprint readers
that we have got our hands on. As such, the API is rather general (and
therefore hopefully easy to comprehend!), and does its best to hide the
technical details that required to operate the hardware.
</para>
<para>
This documentation is not aimed at developers wishing to develop and
contribute fingerprint device drivers to libfprint.
</para>
<para>
Feedback on this API and its associated documentation is appreciated. Was
anything unclear? Does anything seem unreasonably complicated? Is anything
missing? Let us know on the
<ulink url="https://lists.freedesktop.org/mailman/listinfo/fprint">mailing list</ulink>.
</para>
<refsect2 id="enrollment">
<title>Enrollment</title>
<para>
Before you dive into the API, it's worth introducing a couple of concepts.
</para>
<para>
The process of enrolling a finger is where you effectively scan your
finger for the purposes of teaching the system what your finger looks like.
This means that you scan your fingerprint, then the system processes it and
stores some data about your fingerprint to refer to later.
</para>
</refsect2>
<refsect2 id="verification">
<title>Verification</title>
<para>
Verification is what most people think of when they think about fingerprint
scanning. The process of verification is effectively performing a fresh
fingerprint scan, and then comparing that scan to a finger that was
previously enrolled.
</para>
<para>
As an example scenario, verification can be used to implement what people
would picture as fingerprint login (i.e. fingerprint replaces password).
For example:
</para>
<itemizedlist mark='dot'>
<listitem>
I enroll my fingerprint through some software that trusts I am who I say
I am. This is a prerequisite before I can perform fingerprint-based
login for my account.
</listitem>
<listitem>
Some time later, I want to login to my computer. I enter my username,
but instead of prompting me for a password, it asks me to scan my finger.
I scan my finger.
</listitem>
<listitem>
The system compares the finger I just scanned to the one that was
enrolled earlier. If the system decides that the fingerprints match,
I am successfully logged in. Otherwise, the system informs me that I am
not authorised to login as that user.
</listitem>
</itemizedlist>
</refsect2>
<refsect2 id="identification">
<title>Identification</title>
<para>
Identification is the process of comparing a freshly scanned fingerprint
to a <emphasis>collection</emphasis> of previously enrolled fingerprints. For example,
imagine there are 100 people in an organisation, and they all have enrolled
their fingerprints. One user walks up to a fingerprint scanner and scans
their finger. With <emphasis>no other knowledge</emphasis> of who that user might be,
the system examines their fingerprint, looks in the database, and determines
that the user is user number #61.
</para>
<para>
In other words, verification might be seen as a one-to-one fingerprint
comparison where you know the identity of the user that you wish to
authenticate, whereas identification is a one-to-many comparison where you
do not know the identity of the user that you wish to authenticate.
</para>
</refsect2>
</chapter>

73
doc/libfprint-docs.xml Normal file
View File

@@ -0,0 +1,73 @@
<?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
[
<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
]>
<book id="index">
<bookinfo>
<title>libfprint Reference Manual</title>
<releaseinfo>
<para>This document is the API reference for the libfprint library.</para>
<para>
The latest version of libfprint, as well as the latest version of
this API reference, is <ulink role="online-location" url="https://fprint.freedesktop.org/libfprint-dev/">available online</ulink>.
</para>
</releaseinfo>
</bookinfo>
<part>
<title>Library Overview</title>
<xi:include href="intro.xml"/>
<xi:include href="advanced-topics.xml"/>
<xi:include href="getting-started.xml"/>
</part>
<part>
<title>Library API Documentation</title>
<xi:include href="xml/events.xml"/>
<xi:include href="xml/discovery.xml"/>
<xi:include href="xml/drv.xml"/>
<xi:include href="xml/dev.xml"/>
<xi:include href="xml/print_data.xml"/>
<!-- FIXME https://bugs.freedesktop.org/show_bug.cgi?id=106550 -->
<xi:include href="xml/dscv_print.xml"/>
<xi:include href="xml/img.xml"/>
</part>
<part>
<title>Writing Drivers</title>
<chapter id="driver-helpers">
<title>Logging, and async machinery</title>
<xi:include href="xml/fpi-log.xml"/>
<xi:include href="xml/fpi-ssm.xml"/>
<xi:include href="xml/fpi-poll.xml"/>
</chapter>
<chapter id="driver-dev">
<title>Device and driver structures</title>
<xi:include href="xml/fpi-dev.xml"/>
<xi:include href="xml/fpi-dev-img.xml"/>
<xi:include href="xml/fpi-core.xml"/>
<xi:include href="xml/fpi-core-img.xml"/>
</chapter>
<chapter id="driver-img">
<title>Image manipulation</title>
<xi:include href="xml/fpi-img.xml"/>
<xi:include href="xml/fpi-assembling.xml"/>
</chapter>
<xi:include href="xml/fpi-usb.xml"/>
</part>
<index id="api-index">
<title>API Index</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
</index>
<index id="deprecated-api-index" role="deprecated">
<title>Index of deprecated API</title>
<xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
</index>
</book>

271
doc/libfprint-sections.txt Normal file
View File

@@ -0,0 +1,271 @@
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>events</FILE>
<TITLE>Initialisation and events handling</TITLE>
LIBFPRINT_DEPRECATED
fp_set_debug
fp_init
fp_exit
fp_pollfd
fp_handle_events_timeout
fp_handle_events
fp_get_next_timeout
fp_get_pollfds
fp_pollfd_added_cb
fp_pollfd_removed_cb
fp_set_pollfd_notifiers
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>discovery</FILE>
<TITLE>Device discovery</TITLE>
fp_dscv_dev
fp_discover_devs
fp_dscv_devs_free
fp_dscv_dev_get_driver
fp_dscv_dev_get_devtype
fp_dscv_dev_get_driver_id
fp_dscv_dev_supports_print_data
fp_dscv_dev_supports_dscv_print
fp_dscv_dev_for_print_data
fp_dscv_dev_for_dscv_print
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>drv</FILE>
fp_driver
fp_driver_get_name
fp_driver_get_full_name
fp_driver_get_driver_id
fp_driver_get_scan_type
fp_driver_supports_imaging
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>dev</FILE>
fp_dev
fp_scan_type
fp_capture_result
fp_enroll_result
fp_verify_result
fp_dev_get_driver
fp_dev_get_nr_enroll_stages
fp_dev_get_devtype
fp_dev_supports_print_data
fp_dev_supports_imaging
fp_dev_supports_identification
fp_dev_supports_dscv_print
fp_dev_get_img_width
fp_dev_get_img_height
fp_operation_stop_cb
fp_img_operation_cb
fp_dev_open_cb
fp_enroll_stage_cb
fp_identify_cb
fp_dev_open
fp_async_dev_open
fp_dev_close
fp_async_dev_close
fp_enroll_finger
fp_enroll_finger_img
fp_async_enroll_start
fp_async_enroll_stop
fp_verify_finger
fp_verify_finger_img
fp_async_verify_start
fp_async_verify_stop
fp_identify_finger
fp_identify_finger_img
fp_async_identify_start
fp_async_identify_stop
fp_dev_img_capture
fp_async_capture_start
fp_async_capture_stop
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>print_data</FILE>
fp_finger
fp_print_data
fp_print_data_get_data
fp_print_data_from_data
fp_print_data_save
fp_print_data_load
fp_print_data_delete
fp_print_data_from_dscv_print
fp_print_data_free
fp_print_data_get_driver_id
fp_print_data_get_devtype
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>dscv_print</FILE>
fp_dscv_print
fp_discover_prints
fp_dscv_prints_free
fp_dscv_print_get_driver_id
fp_dscv_print_get_devtype
fp_dscv_print_get_finger
fp_dscv_print_delete
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>img</FILE>
fp_img
fp_minutia
fp_img_free
fp_img_get_height
fp_img_get_width
fp_img_get_data
fp_img_save_to_file
fp_img_standardize
fp_img_binarize
fp_img_get_minutiae
fp_minutia_get_coords
</SECTION>
<SECTION>
<INCLUDE>fpi-log.h</INCLUDE>
<FILE>fpi-log</FILE>
fp_dbg
fp_info
fp_warn
fp_err
BUG_ON
BUG
</SECTION>
<SECTION>
<INCLUDE>fpi-ssm.h</INCLUDE>
<FILE>fpi-ssm</FILE>
fpi_ssm
ssm_completed_fn
ssm_handler_fn
fpi_ssm_new
fpi_ssm_free
fpi_ssm_start
fpi_ssm_start_subsm
fpi_ssm_next_state
fpi_ssm_next_state_timeout_cb
fpi_ssm_jump_to_state
fpi_ssm_mark_completed
fpi_ssm_mark_failed
fpi_ssm_get_user_data
fpi_ssm_get_error
fpi_ssm_get_cur_state
</SECTION>
<SECTION>
<INCLUDE>fpi-poll.h</INCLUDE>
<FILE>fpi-poll</FILE>
fpi_timeout
fpi_timeout_fn
fpi_timeout_add
fpi_timeout_set_name
fpi_timeout_cancel
</SECTION>
<SECTION>
<INCLUDE>fpi-dev.h</INCLUDE>
<FILE>fpi-dev</FILE>
fp_img_dev
FP_DEV
FP_IMG_DEV
fp_dev_set_instance_data
FP_INSTANCE_DATA
fpi_dev_get_usb_dev
fpi_dev_get_verify_data
fpi_dev_set_nr_enroll_stages
</SECTION>
<SECTION>
<INCLUDE>fpi-dev-img.h</INCLUDE>
<FILE>fpi-dev-img</FILE>
fp_imgdev_action
fp_imgdev_state
fp_imgdev_enroll_state
fpi_imgdev_abort_scan
fpi_imgdev_activate_complete
fpi_imgdev_close_complete
fpi_imgdev_deactivate_complete
fpi_imgdev_get_action
fpi_imgdev_get_action_result
fpi_imgdev_get_action_state
fpi_imgdev_image_captured
fpi_imgdev_open_complete
fpi_imgdev_report_finger_status
fpi_imgdev_session_error
fpi_imgdev_set_action_result
</SECTION>
<SECTION>
<INCLUDE>fpi-core.h</INCLUDE>
<FILE>fpi-core</FILE>
usb_id
fp_driver_type
</SECTION>
<SECTION>
<INCLUDE>fpi-core.h</INCLUDE>
<FILE>fpi-core-img</FILE>
FpiImgDriverFlags
fp_img_driver
</SECTION>
<SECTION>
<INCLUDE>fpi-img.h</INCLUDE>
<FILE>fpi-img</FILE>
FpiImgFlags
fpi_img_new
fpi_img_new_for_imgdev
fpi_img_realloc
fpi_img_resize
fpi_std_sq_dev
fpi_mean_sq_diff_norm
</SECTION>
<SECTION>
<INCLUDE>fpi-assembling.h</INCLUDE>
<FILE>fpi-assembling</FILE>
fpi_frame
fpi_frame_asmbl_ctx
fpi_line_asmbl_ctx
fpi_do_movement_estimation
fpi_assemble_frames
fpi_assemble_lines
</SECTION>
<SECTION>
<INCLUDE>fpi-usb.h</INCLUDE>
<FILE>fpi-usb</FILE>
fpi_usb_transfer
fpi_usb_transfer_cb_fn
fpi_usb_alloc
fpi_usb_fill_bulk_transfer
fpi_usb_submit_transfer
fpi_usb_cancel_transfer
</SECTION>

71
doc/meson.build Normal file
View File

@@ -0,0 +1,71 @@
subdir('xml')
private_headers = [
'config.h',
'aeslib.h',
'assembling.h',
'fp_internal.h',
'nbis-helpers.h',
'fpi-async.h',
'fpi-data.h',
# Drivers
'aes1660.h',
'aes2501.h',
'aes2550.h',
'aes2660.h',
'aes3k.h',
'aesx660.h',
'driver_ids.h',
'elan.h',
'upek_proto.h',
'upeksonly.h',
'upektc.h',
'upektc_img.h',
'vfs0050.h',
'vfs301_proto_fragments.h',
'vfs301_proto.h',
'vfs5011_proto.h',
# NBIS
'morph.h',
'sunrast.h',
'bozorth.h',
'defs.h',
'log.h',
'bz_array.h',
'lfs.h',
'mytime.h',
]
html_images = [
]
content_files = [
'intro.xml'
]
expand_content_files = content_files
glib_prefix = dependency('glib-2.0').get_pkgconfig_variable('prefix')
glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html')
docpath = join_paths(get_option('datadir'), 'gtk-doc', 'html')
gnome.gtkdoc('libfprint',
main_xml: 'libfprint-docs.xml',
src_dir: join_paths(meson.source_root(), 'libfprint'),
dependencies: libfprint_dep,
content_files: content_files,
expand_content_files: expand_content_files,
scan_args: [
'--ignore-decorators=API_EXPORTED',
'--ignore-headers=' + ' '.join(private_headers),
],
fixxref_args: [
'--html-dir=@0@'.format(docpath),
'--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')),
'--extra-dir=@0@'.format(join_paths(glib_docpath, 'gobject')),
],
html_assets: html_images,
install: true)

View File

@@ -0,0 +1,8 @@
<!ENTITY package "@PACKAGE@">
<!ENTITY package_bugreport "@PACKAGE_BUGREPORT@">
<!ENTITY package_name "@PACKAGE_NAME@">
<!ENTITY package_string "@PACKAGE_STRING@">
<!ENTITY package_tarname "@PACKAGE_TARNAME@">
<!ENTITY package_url "@PACKAGE_URL@">
<!ENTITY package_version "@PACKAGE_VERSION@">
<!ENTITY package_api_version "@PACKAGE_API_VERSION@">

10
doc/xml/meson.build Normal file
View File

@@ -0,0 +1,10 @@
ent_conf = configuration_data()
ent_conf.set('PACKAGE', 'libfprint')
ent_conf.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/libfprint/libfprint/issues')
ent_conf.set('PACKAGE_NAME', 'libfprint')
ent_conf.set('PACKAGE_STRING', 'libfprint')
ent_conf.set('PACKAGE_TARNAME', 'libfprint-' + meson.project_version())
ent_conf.set('PACKAGE_URL', 'https://fprint.freedesktop.org/')
ent_conf.set('PACKAGE_VERSION', meson.project_version())
ent_conf.set('PACKAGE_API_VERSION', '1.0')
configure_file(input: 'gtkdocentities.ent.in', output: 'gtkdocentities.ent', configuration: ent_conf)

View File

@@ -1,23 +0,0 @@
INCLUDES = -I$(top_srcdir)
noinst_PROGRAMS = verify_live enroll verify img_capture
verify_live_SOURCES = verify_live.c
verify_live_LDADD = ../libfprint/libfprint.la
enroll_SOURCES = enroll.c
enroll_LDADD = ../libfprint/libfprint.la
verify_SOURCES = verify.c
verify_LDADD = ../libfprint/libfprint.la
img_capture_SOURCES = img_capture.c
img_capture_LDADD = ../libfprint/libfprint.la
if BUILD_X11_EXAMPLES
noinst_PROGRAMS += img_capture_continuous
img_capture_continuous_CFLAGS = $(X_CFLAGS) $(XV_CFLAGS)
img_capture_continuous_SOURCES = img_capture_continuous.c
img_capture_continuous_LDADD = ../libfprint/libfprint.la $(X_LIBS) $(X_PRE_LIBS) $(XV_LIBS) -lX11 $(X_EXTRA_LIBS);
endif

11
examples/cpp-test.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libfprint/fprint.h>
int main (int argc, char **argv)
{
fp_init ();
return 0;
}

View File

@@ -24,6 +24,8 @@
#include <libfprint/fprint.h>
#include "storage.h"
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
{
struct fp_dscv_dev *ddev = discovered_devs[0];
@@ -45,8 +47,7 @@ struct fp_print_data *enroll(struct fp_dev *dev) {
do {
struct fp_img *img = NULL;
sleep(1);
printf("\nScan your finger now.\n");
r = fp_enroll_finger_img(dev, &enrolled_print, &img);
@@ -110,12 +111,14 @@ int main(void)
"Ctrl+C\n");
getchar();
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
@@ -141,7 +144,7 @@ int main(void)
if (!data)
goto out_close;
r = fp_print_data_save(data, RIGHT_INDEX);
r = print_data_save(data, RIGHT_INDEX);
if (r < 0)
fprintf(stderr, "Data save failed, code %d\n", r);

View File

@@ -42,12 +42,14 @@ int main(void)
struct fp_dev *dev;
struct fp_img *img = NULL;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
@@ -57,6 +59,7 @@ int main(void)
ddev = discover_device(discovered_devs);
if (!ddev) {
fp_dscv_devs_free(discovered_devs);
fprintf(stderr, "No devices detected.\n");
goto out;
}

View File

@@ -29,7 +29,7 @@
#define FORMAT 0x32595559
static int adaptor = -1;
static unsigned char *framebuffer = NULL;
static char *framebuffer = NULL;
static Display *display = NULL;
static Window window=(Window)NULL;
@@ -50,10 +50,10 @@ static int connection = -1;
u = u > 255 ? 255 : u;\
v = v > 255 ? 255 : v
static void grey2yuy2 (unsigned char *grey, unsigned char *YUV, int num) {
static void grey2yuy2 (unsigned char *grey, char *YUV, int num) {
int i, j;
int y0, y1, u0, u1, v0, v1;
int gval;
uint64_t gval;
for (i = 0, j = 0; i < num; i += 2, j += 4)
{
@@ -84,9 +84,9 @@ static void display_frame(struct fp_img *img)
0, 0, width, height, 0, 0, width, height);
}
static void QueryXv()
static void QueryXv(void)
{
int num_adaptors;
unsigned int num_adaptors;
int num_formats;
XvImageFormatValues *formats = NULL;
int i,j;
@@ -141,12 +141,14 @@ int main(void)
int img_height;
int standardize = 0;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {

29
examples/meson.build Normal file
View File

@@ -0,0 +1,29 @@
examples = [ 'verify_live', 'enroll', 'verify', 'img_capture' ]
foreach example: examples
executable(example,
[example + '.c', 'storage.c'],
dependencies: [libfprint_dep, glib_dep],
include_directories: [
root_inc,
],
c_args: common_cflags)
endforeach
executable('cpp-test',
'cpp-test.cpp',
dependencies: libfprint_dep,
include_directories: [
root_inc,
],
c_args: common_cflags)
if get_option('x11-examples')
executable('img_capture_continuous',
'img_capture_continuous.c',
dependencies: [ libfprint_dep, xv_dep, x11_dep ],
include_directories: [
root_inc,
],
c_args: common_cflags)
endif

136
examples/storage.c Normal file
View File

@@ -0,0 +1,136 @@
/*
* Trivial storage driver for example programs
*
* 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
*/
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libfprint/fprint.h>
#define STORAGE_FILE "test-storage.variant"
static char *
get_print_data_descriptor (struct fp_print_data *data, struct fp_dev *dev, enum fp_finger finger)
{
gint drv_id;
gint devtype;
if (data) {
drv_id = fp_print_data_get_driver_id (data);
devtype = fp_print_data_get_devtype (data);
} else {
drv_id = fp_driver_get_driver_id(fp_dev_get_driver (dev));
devtype = fp_dev_get_devtype (dev);
}
return g_strdup_printf("%x/%08x/%x",
drv_id,
devtype,
finger);
}
static GVariantDict*
load_data(void)
{
GVariantDict *res;
GVariant *var;
gchar *contents = NULL;
gssize length = 0;
if (!g_file_get_contents (STORAGE_FILE, &contents, &length, NULL)) {
g_warning ("Error loading storage, assuming it is empty");
return g_variant_dict_new(NULL);
}
var = g_variant_new_from_data (G_VARIANT_TYPE_VARDICT, contents, length, FALSE, NULL, NULL);
res = g_variant_dict_new(var);
g_variant_unref(var);
return res;
}
static int
save_data(GVariant *data)
{
const gchar *contents = NULL;
gsize length;
length = g_variant_get_size(data);
contents = (gchar*) g_variant_get_data (data);
if (!g_file_set_contents (STORAGE_FILE, contents, length, NULL)) {
g_warning ("Error saving storage,!");
return -1;
}
g_variant_ref_sink(data);
g_variant_unref(data);
return 0;
}
int
print_data_save(struct fp_print_data *fp_data, enum fp_finger finger)
{
gchar *descr = get_print_data_descriptor (fp_data, NULL, finger);
GVariantDict *dict;
GVariant *val;
guchar *data;
gsize size;
int res;
dict = load_data();
size = fp_print_data_get_data(fp_data, &data);
val = g_variant_new_fixed_array (G_VARIANT_TYPE("y"), data, size, 1);
g_variant_dict_insert_value (dict, descr, val);
res = save_data(g_variant_dict_end(dict));
g_variant_dict_unref(dict);
return res;
}
struct fp_print_data*
print_data_load(struct fp_dev *dev, enum fp_finger finger)
{
gchar *descr = get_print_data_descriptor (NULL, dev, finger);
GVariantDict *dict;
guchar *stored_data;
gsize stored_len;
GVariant *val;
struct fp_print_data *res = NULL;
dict = load_data();
val = g_variant_dict_lookup_value (dict, descr, G_VARIANT_TYPE ("ay"));
if (val) {
stored_data = (guchar*) g_variant_get_fixed_array (val, &stored_len, 1);
res = fp_print_data_from_data(stored_data, stored_len);
g_variant_unref(val);
}
g_variant_dict_unref(dict);
g_free(descr);
return res;
}

27
examples/storage.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* Trivial storage driver for example programs
*
* 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
*/
#ifndef __STORAGE_H
#define __STORAGE_H
int print_data_save(struct fp_print_data *fp_data, enum fp_finger finger);
struct fp_print_data* print_data_load(struct fp_dev *dev, enum fp_finger finger);
#endif /* __STORAGE_H */

View File

@@ -24,6 +24,8 @@
#include <libfprint/fprint.h>
#include "storage.h"
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
{
struct fp_dscv_dev *ddev = discovered_devs[0];
@@ -86,12 +88,14 @@ int main(void)
struct fp_dev *dev;
struct fp_print_data *data;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
@@ -115,8 +119,8 @@ int main(void)
printf("Opened device. Loading previously enrolled right index finger "
"data...\n");
r = fp_print_data_load(dev, RIGHT_INDEX, &data);
if (r != 0) {
data = print_data_load(dev, RIGHT_INDEX);
if (!data) {
fprintf(stderr, "Failed to load fingerprint, error %d\n", r);
fprintf(stderr, "Did you remember to enroll your right index finger "
"first?\n");

View File

@@ -43,7 +43,6 @@ struct fp_print_data *enroll(struct fp_dev *dev) {
"complete the process.\n", fp_dev_get_nr_enroll_stages(dev));
do {
sleep(1);
printf("\nScan your finger now.\n");
r = fp_enroll_finger(dev, &enrolled_print);
if (r < 0) {
@@ -129,12 +128,14 @@ int main(void)
struct fp_dev *dev;
struct fp_print_data *data;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {

View File

@@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libfprint
Description: Generic C API for fingerprint reader access
Version: @VERSION@
Libs: -L${libdir} -lfprint
Cflags: -I${includedir}/libfprint

View File

@@ -1,150 +0,0 @@
lib_LTLIBRARIES = libfprint.la
noinst_PROGRAMS = fprint-list-hal-info fprint-list-udev-rules
MOSTLYCLEANFILES = $(hal_fdi_DATA) $(udev_rules_DATA)
UPEKE2_SRC = drivers/upeke2.c
UPEKTS_SRC = drivers/upekts.c
UPEKTC_SRC = drivers/upektc.c
UPEKSONLY_SRC = drivers/upeksonly.c
URU4000_SRC = drivers/uru4000.c
AES1610_SRC = drivers/aes1610.c
AES2501_SRC = drivers/aes2501.c drivers/aes2501.h
AES4000_SRC = drivers/aes4000.c
FDU2000_SRC = drivers/fdu2000.c
VCOM5S_SRC = drivers/vcom5s.c
DRIVER_SRC =
OTHER_SRC =
NBIS_SRC = \
nbis/include/bozorth.h \
nbis/include/bz_array.h \
nbis/include/defs.h \
nbis/include/lfs.h \
nbis/include/log.h \
nbis/include/morph.h \
nbis/include/sunrast.h \
nbis/bozorth3/bozorth3.c \
nbis/bozorth3/bz_alloc.c \
nbis/bozorth3/bz_drvrs.c \
nbis/bozorth3/bz_gbls.c \
nbis/bozorth3/bz_io.c \
nbis/bozorth3/bz_sort.c \
nbis/mindtct/binar.c \
nbis/mindtct/block.c \
nbis/mindtct/contour.c \
nbis/mindtct/detect.c \
nbis/mindtct/dft.c \
nbis/mindtct/free.c \
nbis/mindtct/globals.c \
nbis/mindtct/imgutil.c \
nbis/mindtct/init.c \
nbis/mindtct/line.c \
nbis/mindtct/log.c \
nbis/mindtct/loop.c \
nbis/mindtct/maps.c \
nbis/mindtct/matchpat.c \
nbis/mindtct/minutia.c \
nbis/mindtct/morph.c \
nbis/mindtct/quality.c \
nbis/mindtct/remove.c \
nbis/mindtct/ridges.c \
nbis/mindtct/shape.c \
nbis/mindtct/sort.c \
nbis/mindtct/util.c
libfprint_la_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(CRYPTO_LIBS)
fprint_list_hal_info_SOURCES = fprint-list-hal-info.c
fprint_list_hal_info_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
fprint_list_hal_info_LDADD = $(builddir)/libfprint.la
hal_fdi_DATA = 10-fingerprint-reader-fprint.fdi
hal_fdidir = $(datadir)/hal/fdi/information/20thirdparty/
$(hal_fdi_DATA): fprint-list-hal-info
$(builddir)/fprint-list-hal-info > $@
fprint_list_udev_rules_SOURCES = fprint-list-udev-rules.c
fprint_list_udev_rules_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
fprint_list_udev_rules_LDADD = $(builddir)/libfprint.la
udev_rules_DATA = 60-fprint-autosuspend.rules
udev_rulesdir = $(sysconfdir)/udev/rules.d/
$(udev_rules_DATA): fprint-list-udev-rules
$(builddir)/fprint-list-udev-rules > $@
if ENABLE_UPEKE2
DRIVER_SRC += $(UPEKE2_SRC)
endif
if ENABLE_UPEKTS
DRIVER_SRC += $(UPEKTS_SRC)
endif
if ENABLE_UPEKSONLY
DRIVER_SRC += $(UPEKSONLY_SRC)
endif
#if ENABLE_UPEKTC
#DRIVER_SRC += $(UPEKTC_SRC)
#endif
if ENABLE_URU4000
DRIVER_SRC += $(URU4000_SRC)
endif
if ENABLE_VCOM5S
DRIVER_SRC += $(VCOM5S_SRC)
endif
#if ENABLE_FDU2000
#DRIVER_SRC += $(FDU2000_SRC)
#endif
if ENABLE_AES1610
DRIVER_SRC += $(AES1610_SRC)
endif
if ENABLE_AES2501
DRIVER_SRC += $(AES2501_SRC)
endif
if ENABLE_AES4000
DRIVER_SRC += $(AES4000_SRC)
endif
if REQUIRE_IMAGEMAGICK
OTHER_SRC += imagemagick.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
libfprint_la_LIBADD += $(IMAGING_LIBS)
endif
if REQUIRE_GDKPIXBUF
OTHER_SRC += gdkpixbuf.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
libfprint_la_LIBADD += $(IMAGING_LIBS)
endif
if REQUIRE_AESLIB
OTHER_SRC += aeslib.c aeslib.h
endif
libfprint_la_SOURCES = \
fp_internal.h \
async.c \
core.c \
data.c \
drv.c \
img.c \
imgdev.c \
poll.c \
sync.c \
$(DRIVER_SRC) \
$(OTHER_SRC) \
$(NBIS_SRC)
pkginclude_HEADERS = fprint.h

View File

@@ -1,971 +0,0 @@
/*
* Core functions for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <libusb.h>
#include "fp_internal.h"
static int log_level = 0;
static int log_level_fixed = 0;
libusb_context *fpi_usb_ctx = NULL;
GSList *opened_devices = NULL;
/**
* \mainpage libfprint API Reference
* libfprint is an open source library to provide access to fingerprint
* scanning devices. For more info, see the
* <a href="http://www.reactivated.net/fprint/Libfprint">libfprint project
* homepage</a>.
*
* This documentation is aimed at application developers who wish to integrate
* fingerprint-related functionality into their software. libfprint has been
* designed so that you only have to do this once - by integrating your
* software with libfprint, you'll be supporting all the fingerprint readers
* that we have got our hands on. As such, the API is rather general (and
* therefore hopefully easy to comprehend!), and does its best to hide the
* technical details that required to operate the hardware.
*
* This documentation is not aimed at developers wishing to develop and
* contribute fingerprint device drivers to libfprint.
*
* Feedback on this API and its associated documentation is appreciated. Was
* anything unclear? Does anything seem unreasonably complicated? Is anything
* missing? Let us know on the
* <a href="http://www.reactivated.net/fprint/Mailing_list">mailing list</a>.
*
* \section enrollment Enrollment
*
* Before you dive into the API, it's worth introducing a couple of concepts.
*
* The process of enrolling a finger is where you effectively scan your
* finger for the purposes of teaching the system what your finger looks like.
* This means that you scan your fingerprint, then the system processes it and
* stores some data about your fingerprint to refer to later.
*
* \section verification Verification
*
* Verification is what most people think of when they think about fingerprint
* scanning. The process of verification is effectively performing a fresh
* fingerprint scan, and then comparing that scan to a finger that was
* previously enrolled.
*
* As an example scenario, verification can be used to implement what people
* would picture as fingerprint login (i.e. fingerprint replaces password).
* For example:
* - I enroll my fingerprint through some software that trusts I am who I say
* I am. This is a prerequisite before I can perform fingerprint-based
* login for my account.
* - Some time later, I want to login to my computer. I enter my username,
* but instead of prompting me for a password, it asks me to scan my finger.
* I scan my finger.
* - The system compares the finger I just scanned to the one that was
* enrolled earlier. If the system decides that the fingerprints match,
* I am successfully logged in. Otherwise, the system informs me that I am
* not authorised to login as that user.
*
* \section identification Identification
*
* Identification is the process of comparing a freshly scanned fingerprint
* to a <em>collection</em> of previously enrolled fingerprints. For example,
* imagine there are 100 people in an organisation, and they all have enrolled
* their fingerprints. One user walks up to a fingerprint scanner and scans
* their finger. With <em>no other knowledge</em> of who that user might be,
* the system examines their fingerprint, looks in the database, and determines
* that the user is user number #61.
*
* In other words, verification might be seen as a one-to-one fingerprint
* comparison where you know the identity of the user that you wish to
* authenticate, whereas identification is a one-to-many comparison where you
* do not know the identity of the user that you wish to authenticate.
*
* \section compat_general Device and print compatibility
* Moving off generic conceptual ideas and onto libfprint-specific
* implementation details, here are some introductory notes regarding how
* libfprint copes with compatibility of fingerprints.
*
* libfprint deals with a whole variety of different fingerprint readers and
* the design includes considerations of compatibility and interoperability
* between multiple devices. Your application should also be prepared to
* work with more than one type of fingerprint reader and should consider that
* enrolled fingerprint X may not be compatible with the device the user has
* plugged in today.
*
* libfprint implements the principle that fingerprints from different devices
* are not necessarily compatible. For example, different devices may see
* significantly different areas of fingerprint surface, and comparing images
* between the devices would be unreliable. Also, devices can stretch and
* distort images in different ways.
*
* libfprint also implements the principle that in some cases, fingerprints
* <em>are</em> compatible between different devices. If you go and buy two
* identical fingerprint readers, it seems logical that you should be able
* to enroll on one and verify on another without problems.
*
* libfprint takes a fairly simplistic approach to these issues. Internally,
* fingerprint hardware is driven by individual drivers. libfprint enforces
* that a fingerprint that came from a device backed by driver X is never
* compared to a fingerprint that came from a device backed by driver Y.
*
* Additionally, libfprint is designed for the situation where a single driver
* may support a range of devices which differ in imaging or scanning
* properties. For example, a driver may support two ranges of devices which
* even though are programmed over the same interface, one device sees
* substantially less of the finger flesh, therefore images from the two
* device types should be incompatible despite being from the same driver. To
* implement this, each driver assigns a <em>device type</em> to each device
* that it detects based on its imaging characteristics. libfprint ensures that
* two prints being compared have the same device type.
*
* In summary, libfprint represents fingerprints in several internal structures
* and each representation will offer you a way of determining the
* \ref driver_id "driver ID" and \ref devtype "devtype" of the print in
* question. Prints are only compatible if the driver ID <b>and</b> devtypes
* match. libfprint does offer you some "is this print compatible?" helper
* functions, so you don't have to worry about these details too much.
*
* \section sync Synchronity/asynchronity
*
* Currently, all data acquisition operations are synchronous and can
* potentially block for extended periods of time. For example, the enroll
* function will block for an unpredictable amount of time until the user
* scans their finger.
*
* Alternative asynchronous/non-blocking functionality will be offered in
* future but has not been implemented yet.
*
* \section getting_started Getting started
*
* libfprint includes several simple functional examples under the examples/
* directory in the libfprint source distribution. Those are good starting
* points.
*
* Usually the first thing you want to do is determine which fingerprint
* devices are present. This is done through \ref dscv_dev "device discovery".
*
* Once you have found a device you would like to operate, you should open it.
* Refer to \ref dev "device operations". This section also details enrollment,
* image capture, and verification.
*
*
* That should be enough to get you started, but do remember there are
* documentation pages on other aspects of libfprint's API (see the modules
* page).
*/
/** @defgroup core Core library operations */
/**
* @defgroup dev Device operations
* In order to interact with fingerprint scanners, your software will
* interface primarily with libfprint's representation of devices, detailed
* on this page.
*
* \section enrolling Enrolling
* Enrolling is represented within libfprint as a multi-stage process. This
* slightly complicates things for application developers, but is required
* for a smooth process.
*
* Some devices require the user to scan their finger multiple times in
* order to complete the enrollment process. libfprint must return control
* to your application inbetween each scan in order for your application to
* instruct the user to swipe their finger again. Each scan is referred to
* as a stage, so a device that requires 3 scans for enrollment corresponds
* to you running 3 enrollment stages using libfprint.
*
* The fp_dev_get_nr_enroll_stages() function can be used to find out how
* many enroll stages are needed.
*
* In order to complete an enroll stage, you call an enroll function such
* as fp_enroll_finger(). The return of this function does not necessarily
* indicate that a stage has completed though, as the user may not have
* produced a good enough scan. Each stage may have to be retried several
* times.
*
* The exact semantics of the enroll functions are described in the
* fp_enroll_finger() documentation. You should pay careful attention to the
* details.
*
* \section imaging Imaging
* libfprint provides you with some ways to retrieve images of scanned
* fingers, such as the fp_dev_img_capture() function, or some enroll/verify
* function variants which provide images. You may wish to do something with
* such images in your application.
*
* However, you must be aware that not all hardware supported by libfprint
* operates like this. Most hardware does operate simply by sending
* fingerprint images to the host computer for further processing, but some
* devices do all fingerprint processing in hardware and do not present images
* to the host computer.
*
* You can use fp_dev_supports_imaging() to see if image capture is possible
* on a particular device. Your application must be able to cope with the
* fact that libfprint does support regular operations (e.g. enrolling and
* verification) on some devices which do not provide images.
*
* \section devtype Devtypes
* Internally, the \ref drv "driver" behind a device assigns a 32-bit
* <em>devtype</em> identifier to the device. This cannot be used as a unique
* ID for a specific device as many devices under the same range may share
* the same devtype. The devtype may even be 0 in all cases.
*
* The only reason you may be interested in retrieving the devtype for a
* device is for the purpose of checking if some print data is compatible
* with a device. libfprint uses the devtype as one way of checking that the
* print you are verifying is compatible with the device in question - the
* devtypes must be equal. This effectively allows drivers to support more
* than one type of device where the data from each one is not compatible with
* the other. Note that libfprint does provide you with helper functions to
* determine whether a print is compatible with a device, so under most
* circumstances, you don't have to worry about devtypes at all.
*/
/** @defgroup dscv_dev Device discovery
* These functions allow you to scan the system for supported fingerprint
* scanning hardware. This is your starting point when integrating libfprint
* into your software.
*
* When you've identified a discovered device that you would like to control,
* you can open it with fp_dev_open(). Note that discovered devices may no
* longer be available at the time when you want to open them, for example
* the user may have unplugged the device.
*/
/** @defgroup drv Driver operations
* Internally, libfprint is abstracted into various drivers to communicate
* with the different types of supported fingerprint readers. libfprint works
* hard so that you don't have to care about these internal abstractions,
* however there are some situations where you may be interested in a little
* behind-the-scenes driver info.
*
* You can obtain the driver for a device using fp_dev_get_driver(), which
* you can pass to the functions documented on this page.
*
* \section driver_id Driver IDs
* Each driver is assigned a unique ID by the project maintainer. These
* assignments are
* <a href="http://www.reactivated.net/fprint/Driver_ID_assignments">
* documented on the wiki</a> and will never change.
*
* The only reason you may be interested in retrieving the driver ID for a
* driver is for the purpose of checking if some print data is compatible
* with a device. libfprint uses the driver ID as one way of checking that
* the print you are trying to verify is compatible with the device in
* question - it ensures that enrollment data from one driver is never fed to
* another. Note that libfprint does provide you with helper functions to
* determine whether a print is compatible with a device, so under most
* circumstances, you don't have to worry about driver IDs at all.
*/
static GSList *registered_drivers = NULL;
void fpi_log(enum fpi_log_level level, const char *component,
const char *function, const char *format, ...)
{
va_list args;
FILE *stream = stdout;
const char *prefix;
#ifndef ENABLE_DEBUG_LOGGING
if (!log_level)
return;
if (level == LOG_LEVEL_WARNING && log_level < 2)
return;
if (level == LOG_LEVEL_INFO && log_level < 3)
return;
#endif
switch (level) {
case LOG_LEVEL_INFO:
prefix = "info";
break;
case LOG_LEVEL_WARNING:
stream = stderr;
prefix = "warning";
break;
case LOG_LEVEL_ERROR:
stream = stderr;
prefix = "error";
break;
case LOG_LEVEL_DEBUG:
stream = stderr;
prefix = "debug";
break;
default:
stream = stderr;
prefix = "unknown";
break;
}
fprintf(stream, "%s:%s [%s] ", component ? component : "fp", prefix,
function);
va_start (args, format);
vfprintf(stream, format, args);
va_end (args);
fprintf(stream, "\n");
}
static void register_driver(struct fp_driver *drv)
{
if (drv->id == 0) {
fp_err("not registering driver %s: driver ID is 0", drv->name);
return;
}
registered_drivers = g_slist_prepend(registered_drivers, (gpointer) drv);
fp_dbg("registered driver %s", drv->name);
}
static struct fp_driver * const primitive_drivers[] = {
#ifdef ENABLE_UPEKTS
&upekts_driver,
#endif
#ifdef ENABLE_UPEKE2
&upeke2_driver,
#endif
};
static struct fp_img_driver * const img_drivers[] = {
#ifdef ENABLE_AES4000
&aes4000_driver,
#endif
#ifdef ENABLE_AES2501
&aes2501_driver,
#endif
#ifdef ENABLE_URU4000
&uru4000_driver,
#endif
#ifdef ENABLE_VCOM5S
&vcom5s_driver,
#endif
#ifdef ENABLE_UPEKSONLY
&upeksonly_driver,
#endif
#ifdef ENABLE_AES1610
&aes1610_driver,
#endif
/*#ifdef ENABLE_UPEKTC
&upektc_driver,
#endif
#ifdef ENABLE_FDU2000
&fdu2000_driver,
#endif
*/
};
static void register_drivers(void)
{
unsigned int i;
for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++)
register_driver(primitive_drivers[i]);
for (i = 0; i < G_N_ELEMENTS(img_drivers); i++) {
struct fp_img_driver *imgdriver = img_drivers[i];
fpi_img_driver_setup(imgdriver);
register_driver(&imgdriver->driver);
}
}
API_EXPORTED struct fp_driver **fprint_get_drivers (void)
{
GPtrArray *array;
unsigned int i;
array = g_ptr_array_new ();
for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++)
g_ptr_array_add (array, primitive_drivers[i]);
for (i = 0; i < G_N_ELEMENTS(img_drivers); i++)
g_ptr_array_add (array, &(img_drivers[i]->driver));
/* Add a null item terminating the array */
g_ptr_array_add (array, NULL);
return (struct fp_driver **) g_ptr_array_free (array, FALSE);
}
static struct fp_driver *find_supporting_driver(libusb_device *udev,
const struct usb_id **usb_id, uint32_t *devtype)
{
int ret;
GSList *elem = registered_drivers;
struct libusb_device_descriptor dsc;
const struct usb_id *best_usb_id;
struct fp_driver *best_drv;
uint32_t best_devtype;
int drv_score = 0;
ret = libusb_get_device_descriptor(udev, &dsc);
if (ret < 0) {
fp_err("Failed to get device descriptor");
return NULL;
}
best_drv = NULL;
best_devtype = 0;
do {
struct fp_driver *drv = elem->data;
uint32_t type = 0;
const struct usb_id *id;
for (id = drv->id_table; id->vendor; id++) {
if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) {
if (drv->discover) {
int r = drv->discover(&dsc, &type);
if (r < 0)
fp_err("%s discover failed, code %d", drv->name, r);
if (r <= 0)
continue;
/* Has a discover function, and matched our device */
drv_score = 100;
} else {
/* Already got a driver as good */
if (drv_score >= 50)
continue;
drv_score = 50;
}
fp_dbg("driver %s supports USB device %04x:%04x",
drv->name, id->vendor, id->product);
best_usb_id = id;
best_drv = drv;
best_devtype = type;
/* We found the best possible driver */
if (drv_score == 100)
break;
}
}
} while ((elem = g_slist_next(elem)));
if (best_drv != NULL) {
fp_dbg("selected driver %s supports USB device %04x:%04x",
best_drv->name, dsc.idVendor, dsc.idProduct);
*devtype = best_devtype;
*usb_id = best_usb_id;
}
return best_drv;
}
static struct fp_dscv_dev *discover_dev(libusb_device *udev)
{
const struct usb_id *usb_id;
struct fp_driver *drv;
struct fp_dscv_dev *ddev;
uint32_t devtype;
drv = find_supporting_driver(udev, &usb_id, &devtype);
if (!drv)
return NULL;
ddev = g_malloc0(sizeof(*ddev));
ddev->drv = drv;
ddev->udev = udev;
ddev->driver_data = usb_id->driver_data;
ddev->devtype = devtype;
return ddev;
}
/** \ingroup dscv_dev
* Scans the system and returns a list of discovered devices. This is your
* entry point into finding a fingerprint reader to operate.
* \returns a NULL-terminated list of discovered devices. Must be freed with
* fp_dscv_devs_free() after use.
*/
API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void)
{
GSList *tmplist = NULL;
struct fp_dscv_dev **list;
libusb_device *udev;
libusb_device **devs;
int dscv_count = 0;
int r;
int i = 0;
if (registered_drivers == NULL)
return NULL;
r = libusb_get_device_list(fpi_usb_ctx, &devs);
if (r < 0) {
fp_err("couldn't enumerate USB devices, error %d", r);
return NULL;
}
/* Check each device against each driver, temporarily storing successfully
* discovered devices in a GSList.
*
* Quite inefficient but excusable as we'll only be dealing with small
* sets of drivers against small sets of USB devices */
while ((udev = devs[i++]) != NULL) {
struct fp_dscv_dev *ddev = discover_dev(udev);
if (!ddev)
continue;
tmplist = g_slist_prepend(tmplist, (gpointer) ddev);
dscv_count++;
}
/* Convert our temporary GSList into a standard NULL-terminated pointer
* array. */
list = g_malloc(sizeof(*list) * (dscv_count + 1));
if (dscv_count > 0) {
GSList *elem = tmplist;
i = 0;
do {
list[i++] = elem->data;
} while ((elem = g_slist_next(elem)));
}
list[dscv_count] = NULL; /* NULL-terminate */
g_slist_free(tmplist);
return list;
}
/** \ingroup dscv_dev
* Free a list of discovered devices. This function destroys the list and all
* discovered devices that it included, so make sure you have opened your
* discovered device <b>before</b> freeing the list.
* \param devs the list of discovered devices. If NULL, function simply
* returns.
*/
API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs)
{
int i;
if (!devs)
return;
for (i = 0; devs[i]; i++)
g_free(devs[i]);
g_free(devs);
}
/** \ingroup dscv_dev
* Gets the \ref drv "driver" for a discovered device.
* \param dev the discovered device
* \returns the driver backing the device
*/
API_EXPORTED struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev)
{
return dev->drv;
}
/** \ingroup dscv_dev
* Gets the \ref devtype "devtype" for a discovered device.
* \param dev the discovered device
* \returns the devtype of the device
*/
API_EXPORTED uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev)
{
return dev->devtype;
}
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv)
{
switch (drv->type) {
case DRIVER_PRIMITIVE:
return PRINT_DATA_RAW;
case DRIVER_IMAGING:
return PRINT_DATA_NBIS_MINUTIAE;
default:
fp_err("unrecognised drv type %d", drv->type);
return PRINT_DATA_RAW;
}
}
/** \ingroup dscv_dev
* Determines if a specific \ref print_data "stored print" appears to be
* compatible with a discovered device.
* \param dev the discovered device
* \param data the print for compatibility checking
* \returns 1 if the print is compatible with the device, 0 otherwise
*/
API_EXPORTED int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev,
struct fp_print_data *data)
{
return fpi_print_data_compatible(dev->drv->id, dev->devtype,
fpi_driver_get_data_type(dev->drv), data->driver_id, data->devtype,
data->type);
}
/** \ingroup dscv_dev
* Determines if a specific \ref dscv_print "discovered print" appears to be
* compatible with a discovered device.
* \param dev the discovered device
* \param data the discovered print for compatibility checking
* \returns 1 if the print is compatible with the device, 0 otherwise
*/
API_EXPORTED int fp_dscv_dev_supports_dscv_print(struct fp_dscv_dev *dev,
struct fp_dscv_print *data)
{
return fpi_print_data_compatible(dev->drv->id, dev->devtype, 0,
data->driver_id, data->devtype, 0);
}
/** \ingroup dscv_dev
* Searches a list of discovered devices for a device that appears to be
* compatible with a \ref print_data "stored print".
* \param devs a list of discovered devices
* \param data the print under inspection
* \returns the first discovered device that appears to support the print, or
* NULL if no apparently compatible devices could be found
*/
API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_print_data(struct fp_dscv_dev **devs,
struct fp_print_data *data)
{
struct fp_dscv_dev *ddev;
int i;
for (i = 0; (ddev = devs[i]); i++)
if (fp_dscv_dev_supports_print_data(ddev, data))
return ddev;
return NULL;
}
/** \ingroup dscv_dev
* Searches a list of discovered devices for a device that appears to be
* compatible with a \ref dscv_print "discovered print".
* \param devs a list of discovered devices
* \param print the print under inspection
* \returns the first discovered device that appears to support the print, or
* NULL if no apparently compatible devices could be found
*/
API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_dscv_print(struct fp_dscv_dev **devs,
struct fp_dscv_print *print)
{
struct fp_dscv_dev *ddev;
int i;
for (i = 0; (ddev = devs[i]); i++)
if (fp_dscv_dev_supports_dscv_print(ddev, print))
return ddev;
return NULL;
}
/** \ingroup dev
* Get the \ref drv "driver" for a fingerprint device.
* \param dev the device
* \returns the driver controlling the device
*/
API_EXPORTED struct fp_driver *fp_dev_get_driver(struct fp_dev *dev)
{
return dev->drv;
}
/** \ingroup dev
* Gets the number of \ref enrolling "enroll stages" required to enroll a
* fingerprint with the device.
* \param dev the device
* \returns the number of enroll stages
*/
API_EXPORTED int fp_dev_get_nr_enroll_stages(struct fp_dev *dev)
{
return dev->nr_enroll_stages;
}
/** \ingroup dev
* Gets the \ref devtype "devtype" for a device.
* \param dev the device
* \returns the devtype
*/
API_EXPORTED uint32_t fp_dev_get_devtype(struct fp_dev *dev)
{
return dev->devtype;
}
/** \ingroup dev
* Determines if a stored print is compatible with a certain device.
* \param dev the device
* \param data the stored print
* \returns 1 if the print is compatible with the device, 0 if not
*/
API_EXPORTED int fp_dev_supports_print_data(struct fp_dev *dev,
struct fp_print_data *data)
{
return fpi_print_data_compatible(dev->drv->id, dev->devtype,
fpi_driver_get_data_type(dev->drv), data->driver_id, data->devtype,
data->type);
}
/** \ingroup dev
* Determines if a \ref dscv_print "discovered print" appears to be compatible
* with a certain device.
* \param dev the device
* \param data the discovered print
* \returns 1 if the print is compatible with the device, 0 if not
*/
API_EXPORTED int fp_dev_supports_dscv_print(struct fp_dev *dev,
struct fp_dscv_print *data)
{
return fpi_print_data_compatible(dev->drv->id, dev->devtype,
0, data->driver_id, data->devtype, 0);
}
/** \ingroup drv
* Retrieves the name of the driver. For example: "upekts"
* \param drv the driver
* \returns the driver name. Must not be modified or freed.
*/
API_EXPORTED const char *fp_driver_get_name(struct fp_driver *drv)
{
return drv->name;
}
/** \ingroup drv
* Retrieves a descriptive name of the driver. For example: "UPEK TouchStrip"
* \param drv the driver
* \returns the descriptive name. Must not be modified or freed.
*/
API_EXPORTED const char *fp_driver_get_full_name(struct fp_driver *drv)
{
return drv->full_name;
}
/** \ingroup drv
* Retrieves the driver ID code for a driver.
* \param drv the driver
* \returns the driver ID
*/
API_EXPORTED uint16_t fp_driver_get_driver_id(struct fp_driver *drv)
{
return drv->id;
}
/** \ingroup drv
* Retrieves the scan type for the devices associated with the driver.
* \param drv the driver
* \returns the scan type
*/
API_EXPORTED enum fp_scan_type fp_driver_get_scan_type(struct fp_driver *drv)
{
return drv->scan_type;
}
static struct fp_img_dev *dev_to_img_dev(struct fp_dev *dev)
{
if (dev->drv->type != DRIVER_IMAGING)
return NULL;
return dev->priv;
}
/** \ingroup dev
* Determines if a device has imaging capabilities. If a device has imaging
* capabilities you are able to perform imaging operations such as retrieving
* scan images using fp_dev_img_capture(). However, not all devices are
* imaging devices - some do all processing in hardware. This function will
* indicate which class a device in question falls into.
* \param dev the fingerprint device
* \returns 1 if the device is an imaging device, 0 if the device does not
* provide images to the host computer
*/
API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev)
{
return dev->drv->type == DRIVER_IMAGING;
}
/** \ingroup dev
* Determines if a device is capable of \ref identification "identification"
* through fp_identify_finger() and similar. Not all devices support this
* functionality.
* \param dev the fingerprint device
* \returns 1 if the device is capable of identification, 0 otherwise.
*/
API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
{
return dev->drv->identify_start != NULL;
}
/** \ingroup dev
* Captures an \ref img "image" from a device. The returned image is the raw
* image provided by the device, you may wish to \ref img_std "standardize" it.
*
* If set, the <tt>unconditional</tt> flag indicates that the device should
* capture an image unconditionally, regardless of whether a finger is there
* or not. If unset, this function will block until a finger is detected on
* the sensor.
*
* \param dev the device
* \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected
* \param image a location to return the captured image. Must be freed with
* fp_img_free() after use.
* \return 0 on success, non-zero on error. -ENOTSUP indicates that either the
* unconditional flag was set but the device does not support this, or that the
* device does not support imaging.
* \sa fp_dev_supports_imaging()
*/
API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
struct fp_img **image)
{
struct fp_img_dev *imgdev = dev_to_img_dev(dev);
if (!imgdev) {
fp_dbg("image capture on non-imaging device");
return -ENOTSUP;
}
//return fpi_imgdev_capture(imgdev, unconditional, image);
/* FIXME reimplement async */
return -ENOTSUP;
}
/** \ingroup dev
* Gets the expected width of images that will be captured from the device.
* This function will return -1 for devices that are not
* \ref imaging "imaging devices". If the width of images from this device
* can vary, 0 will be returned.
* \param dev the device
* \returns the expected image width, or 0 for variable, or -1 for non-imaging
* devices.
*/
API_EXPORTED int fp_dev_get_img_width(struct fp_dev *dev)
{
struct fp_img_dev *imgdev = dev_to_img_dev(dev);
if (!imgdev) {
fp_dbg("get image width for non-imaging device");
return -1;
}
return fpi_imgdev_get_img_width(imgdev);
}
/** \ingroup dev
* Gets the expected height of images that will be captured from the device.
* This function will return -1 for devices that are not
* \ref imaging "imaging devices". If the height of images from this device
* can vary, 0 will be returned.
* \param dev the device
* \returns the expected image height, or 0 for variable, or -1 for non-imaging
* devices.
*/
API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev)
{
struct fp_img_dev *imgdev = dev_to_img_dev(dev);
if (!imgdev) {
fp_dbg("get image height for non-imaging device");
return -1;
}
return fpi_imgdev_get_img_height(imgdev);
}
/** \ingroup core
* Set message verbosity.
* - Level 0: no messages ever printed by the library (default)
* - Level 1: error messages are printed to stderr
* - Level 2: warning and error messages are printed to stderr
* - Level 3: informational messages are printed to stdout, warning and error
* messages are printed to stderr
*
* The default level is 0, which means no messages are ever printed. If you
* choose to increase the message verbosity level, ensure that your
* application does not close the stdout/stderr file descriptors.
*
* You are advised to set level 3. libfprint is conservative with its message
* logging and most of the time, will only log messages that explain error
* conditions and other oddities. This will help you debug your software.
*
* If the LIBFPRINT_DEBUG environment variable was set when libfprint was
* initialized, this function does nothing: the message verbosity is fixed
* to the value in the environment variable.
*
* If libfprint was compiled without any message logging, this function does
* nothing: you'll never get any messages.
*
* If libfprint was compiled with verbose debug message logging, this function
* does nothing: you'll always get messages from all levels.
*
* \param ctx the context to operate on, or NULL for the default context
* \param level debug level to set
*/
API_EXPORTED void fp_set_debug(int level)
{
if (log_level_fixed)
return;
log_level = level;
libusb_set_debug(fpi_usb_ctx, level);
}
/** \ingroup core
* Initialise libfprint. This function must be called before you attempt to
* use the library in any way.
* \return 0 on success, non-zero on error.
*/
API_EXPORTED int fp_init(void)
{
char *dbg = getenv("LIBFPRINT_DEBUG");
int r;
fp_dbg("");
r = libusb_init(&fpi_usb_ctx);
if (r < 0)
return r;
if (dbg) {
log_level = atoi(dbg);
if (log_level) {
log_level_fixed = 1;
libusb_set_debug(fpi_usb_ctx, log_level);
}
}
register_drivers();
fpi_poll_init();
return 0;
}
/** \ingroup core
* Deinitialise libfprint. This function should be called during your program
* exit sequence. You must not use any libfprint functions after calling this
* function, unless you call fp_init() again.
*/
API_EXPORTED void fp_exit(void)
{
fp_dbg("");
if (opened_devices) {
GSList *copy = g_slist_copy(opened_devices);
GSList *elem = copy;
fp_dbg("naughty app left devices open on exit!");
do
fp_dev_close((struct fp_dev *) elem->data);
while ((elem = g_slist_next(elem)));
g_slist_free(copy);
g_slist_free(opened_devices);
opened_devices = NULL;
}
fpi_data_exit();
fpi_poll_exit();
g_slist_free(registered_drivers);
registered_drivers = NULL;
libusb_exit(fpi_usb_ctx);
}

View File

@@ -4,6 +4,7 @@
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007 Vasily Khoruzhick
* Copyright (C) 2009 Guido Grazioli <guido.grazioli@gmail.com>
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* Based on code from libfprint aes2501 driver.
*
@@ -24,13 +25,8 @@
#define FP_COMPONENT "aes1610"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "drivers_api.h"
#include "aeslib.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
@@ -48,6 +44,9 @@ static int adjust_gain(unsigned char *buffer, int status);
#define BULK_TIMEOUT 4000
#define FINGER_DETECTION_LEN 19
#define STRIP_CAPTURE_LEN 665
/*
* The AES1610 is an imaging device using a swipe-type sensor. It samples
* the finger at preprogrammed intervals, sending a 128x8 frame to the
@@ -63,6 +62,7 @@ static int adjust_gain(unsigned char *buffer, int status);
#define FRAME_WIDTH 128
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
/* maximum number of frames to read during a scan */
/* FIXME reduce substantially */
#define MAX_FRAMES 350
@@ -77,6 +77,13 @@ struct aes1610_dev {
uint8_t blanks_count;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
typedef void (*aes1610_read_regs_cb)(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data);
@@ -97,12 +104,12 @@ static void stub_capture_stop_cb(struct fp_img_dev *dev, int result, void *user_
/* check that read succeeded but ignore all data */
static void generic_ignore_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
else if (transfer->length != transfer->actual_length)
fpi_ssm_mark_aborted(ssm, -EPROTO);
fpi_ssm_mark_failed(ssm, -EPROTO);
else
fpi_ssm_next_state(ssm);
@@ -110,284 +117,36 @@ static void generic_ignore_data_cb(struct libusb_transfer *transfer)
libusb_free_transfer(transfer);
}
static void read_regs_data_cb(struct libusb_transfer *transfer)
{
struct aes1610_read_regs *rdata = transfer->user_data;
unsigned char *retdata = NULL;
int r;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
r = -EIO;
} else if (transfer->length != transfer->actual_length) {
r = -EPROTO;
} else {
r = 0;
retdata = transfer->buffer;
}
rdata->callback(rdata->dev, r, retdata, rdata->user_data);
g_free(rdata);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void read_regs_rq_cb(struct fp_img_dev *dev, int result, void *user_data)
{
struct aes1610_read_regs *rdata = user_data;
struct libusb_transfer *transfer;
unsigned char *data;
int r;
g_free(rdata->regwrite);
if (result != 0)
goto err;
transfer = libusb_alloc_transfer(0);
if (!transfer) {
result = -ENOMEM;
goto err;
}
data = g_malloc(126);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 126,
read_regs_data_cb, rdata, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
result = -EIO;
goto err;
}
return;
err:
rdata->callback(dev, result, NULL, rdata->user_data);
g_free(rdata);
}
// XXX: this comes from aes2501 driver but it is unused here
static void read_regs(struct fp_img_dev *dev, aes1610_read_regs_cb callback,
void *user_data)
{
/* FIXME: regwrite is dynamic because of asynchronity. is this really
* required? */
struct aes_regwrite *regwrite = g_malloc(sizeof(*regwrite));
struct aes1610_read_regs *rdata = g_malloc(sizeof(*rdata));
fp_dbg("");
//regwrite->reg = AES1610_REG_CTRL2;
//regwrite->value = AES1610_CTRL2_READ_REGS;
rdata->dev = dev;
rdata->callback = callback;
rdata->user_data = user_data;
rdata->regwrite = regwrite;
//aes_write_regv(dev, (const struct aes_regwrite *) regwrite, 1,
// read_regs_rq_cb, rdata);
}
/* Read the value of a specific register from a register dump */
static int regval_from_dump(unsigned char *data, uint8_t target)
{
if (*data != FIRST_AES1610_REG) {
fp_err("not a register dump");
return -EILSEQ;
}
if (!(FIRST_AES1610_REG <= target && target <= LAST_AES1610_REG)) {
fp_err("out of range");
return -EINVAL;
}
target -= FIRST_AES1610_REG;
target *= 2;
return data[target + 1];
}
static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
struct fpi_ssm *ssm = user_data;
fpi_ssm *ssm = user_data;
if (result == 0)
fpi_ssm_next_state(ssm);
else
fpi_ssm_mark_aborted(ssm, result);
fpi_ssm_mark_failed(ssm, result);
}
/* read the specified number of bytes from the IN endpoint but throw them
* away, then increment the SSM */
static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
static void generic_read_ignore_data(fpi_ssm *ssm, struct fp_dev *dev, size_t bytes)
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
data = g_malloc(bytes);
libusb_fill_bulk_transfer(transfer, ssm->dev->udev, EP_IN, data, bytes,
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(dev), EP_IN, data, bytes,
generic_ignore_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
}
}
/****** IMAGE PROCESSING ******/
static int sum_histogram_values(unsigned char *data, uint8_t threshold)
{
int r = 0;
int i;
uint16_t *histogram = (uint16_t *)(data + 1);
if (*data != 0xde)
return -EILSEQ;
if (threshold > 0x0f)
return -EINVAL;
/* FIXME endianness */
for (i = threshold; i < 16; i++)
r += histogram[i];
return r;
}
/* find overlapping parts of frames */
static unsigned int find_overlap(unsigned char *first_frame,
unsigned char *second_frame, unsigned int *min_error)
{
unsigned int dy;
unsigned int not_overlapped_height = 0;
*min_error = 255 * FRAME_SIZE;
for (dy = 0; dy < FRAME_HEIGHT; dy++) {
/* Calculating difference (error) between parts of frames */
unsigned int i;
unsigned int error = 0;
for (i = 0; i < FRAME_WIDTH * (FRAME_HEIGHT - dy); i++) {
/* Using ? operator to avoid abs function */
error += first_frame[i] > second_frame[i] ?
(first_frame[i] - second_frame[i]) :
(second_frame[i] - first_frame[i]);
}
/* Normalize error */
error *= 15;
error /= i;
if (error < *min_error) {
*min_error = error;
not_overlapped_height = dy;
}
first_frame += FRAME_WIDTH;
}
return not_overlapped_height;
}
/* assemble a series of frames into a single image */
static unsigned int assemble(struct aes1610_dev *aesdev, unsigned char *output,
gboolean reverse, unsigned int *errors_sum)
{
uint8_t *assembled = output;
int frame;
uint32_t image_height = FRAME_HEIGHT;
unsigned int min_error;
size_t num_strips = aesdev->strips_len;
GSList *list_entry = aesdev->strips;
*errors_sum = 0;
if (num_strips < 1)
return 0;
/* Rotating given data by 90 degrees
* Taken from document describing aes1610 image format
* TODO: move reversing detection here */
if (reverse)
output += (num_strips - 1) * FRAME_SIZE;
for (frame = 0; frame < num_strips; frame++) {
aes_assemble_image(list_entry->data, FRAME_WIDTH, FRAME_HEIGHT, output);
if (reverse)
output -= FRAME_SIZE;
else
output += FRAME_SIZE;
list_entry = g_slist_next(list_entry);
}
/* Detecting where frames overlaped */
output = assembled;
for (frame = 1; frame < num_strips; frame++) {
int not_overlapped;
output += FRAME_SIZE;
not_overlapped = find_overlap(assembled, output, &min_error);
*errors_sum += min_error;
image_height += not_overlapped;
assembled += FRAME_WIDTH * not_overlapped;
memcpy(assembled, output, FRAME_SIZE);
}
return image_height;
}
static void assemble_and_submit_image(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
size_t final_size;
struct fp_img *img;
unsigned int errors_sum, r_errors_sum;
fp_dbg("");
BUG_ON(aesdev->strips_len == 0);
/* reverse list */
aesdev->strips = g_slist_reverse(aesdev->strips);
/* create buffer big enough for max image */
img = fpi_img_new(aesdev->strips_len * FRAME_SIZE);
img->flags = FP_IMG_COLORS_INVERTED;
img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
img->height = assemble(aesdev, img->data, TRUE, &r_errors_sum);
if (r_errors_sum > errors_sum) {
img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
img->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
fp_dbg("normal scan direction");
} else {
fp_dbg("reversed scan direction");
}
/* now that overlap has been removed, resize output image buffer */
final_size = img->height * FRAME_WIDTH;
img = fpi_img_resize(img, final_size);
/* FIXME: ugly workaround */
if (img->height < 12)
img->height = 12;
fpi_imgdev_image_captured(dev, img);
/* free strips and strip list */
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
aesdev->blanks_count = 0;
}
/****** FINGER PRESENCE DETECTION ******/
@@ -416,15 +175,6 @@ static const struct aes_regwrite finger_det_reqs[] = {
{ 0x81, 0x04 }
};
static const struct aes_regwrite finger_det_none[] = {
{ 0x80, 0x01 },
{ 0x82, 0x00 },
{ 0x86, 0x00 },
{ 0xB1, 0x28 },
{ 0x1D, 0x00 }
};
static void start_finger_detection(struct fp_img_dev *dev);
static void finger_det_data_cb(struct libusb_transfer *transfer)
@@ -461,12 +211,6 @@ out:
libusb_free_transfer(transfer);
}
static void finger_det_none_cb(struct fp_img_dev *dev, int result, void *user_data){
fpi_imgdev_report_finger_status(dev, FALSE);
start_finger_detection(dev);
}
static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
{
struct libusb_transfer *transfer;
@@ -478,14 +222,9 @@ static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_da
return;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
return;
}
data = g_malloc(19);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 19,
transfer = fpi_usb_alloc();
data = g_malloc(FINGER_DETECTION_LEN);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, FINGER_DETECTION_LEN,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -499,8 +238,7 @@ static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_da
static void start_finger_detection(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
struct libusb_transfer *transfer;
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (aesdev->deactivating) {
complete_deactivation(dev);
@@ -660,13 +398,13 @@ static unsigned char list_BE_values[10] = {
/*
* The different possible values for 0xBD register */
static unsigned char list_BD_values[10] = {
0x48, 0x4B, 0x4F, 0x52, 0x57, 0x59, 0x5B
0x28, 0x2b, 0x30, 0x3b, 0x45, 0x49, 0x4B
};
/*
* Adjust the gain according to the histogram data
* 0xbd, 0xbe, 0x29 and 0x2A registers are affected
* Returns 0 if no problem occured
* Returns 0 if no problem occurred
* TODO: This is a basic support for gain. It needs testing/tweaking. */
static int adjust_gain(unsigned char *buffer, int status)
{
@@ -674,32 +412,32 @@ static int adjust_gain(unsigned char *buffer, int status)
static int pos_list_BE = 0;
static int pos_list_BD = 0;
// This is the first adjustement (we begin acquisition)
// This is the first adjustment (we begin acquisition)
// We adjust strip_scan_reqs for future strips and capture_reqs that is sent just after this step
if (status == GAIN_STATUS_FIRST) {
if (buffer[1] > 0x78) { // maximum gain needed
strip_scan_reqs[0].value = 0x6B;
strip_scan_reqs[1].value = 0x06;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x5B;
strip_scan_reqs[3].value = 0x4B;
}
else if (buffer[1] > 0x55) {
strip_scan_reqs[0].value = 0x63;
strip_scan_reqs[1].value = 0x15;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x4F;
strip_scan_reqs[3].value = 0x3b;
}
else if (buffer[1] > 0x40 || buffer[16] > 0x19) {
strip_scan_reqs[0].value = 0x43;
strip_scan_reqs[1].value = 0x13;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x4B;
strip_scan_reqs[3].value = 0x30;
}
else { // minimum gain needed
strip_scan_reqs[0].value = 0x23;
strip_scan_reqs[1].value = 0x07;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x48;
strip_scan_reqs[3].value = 0x28;
}
// Now copy this values in capture_reqs
@@ -780,7 +518,7 @@ static int adjust_gain(unsigned char *buffer, int status)
/*
* Restore the default gain values */
static void restore_gain()
static void restore_gain(void)
{
strip_scan_reqs[0].value = list_BE_values[0];
strip_scan_reqs[1].value = 0x04;
@@ -796,7 +534,7 @@ static void restore_gain()
/* capture SM movement:
* request and read strip,
* jump back to request UNLESS theres no finger, in which case exit SM,
* jump back to request UNLESS there's no finger, in which case exit SM,
* report lack of finger presence, and move to finger detection */
enum capture_states {
@@ -810,33 +548,21 @@ enum capture_states {
static void capture_read_strip_cb(struct libusb_transfer *transfer)
{
unsigned char *stripdata;
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aes1610_dev *aesdev = dev->priv;
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = transfer->buffer;
int sum, i;
int threshold;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fpi_ssm_mark_aborted(ssm, -EPROTO);
fpi_ssm_mark_failed(ssm, -EPROTO);
goto out;
}
/* FIXME: would preallocating strip buffers be a decent optimization? */
//stripdata = g_malloc(128 * 4);
//memcpy(stripdata, data + 1, 128 * 4);
//aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
//aesdev->strips_len++;
/*threshold = regval_from_dump(data + 1 + 128*8 + 1 + 16*2 + 1 + 8,
0x97);
if (threshold < 0) {
fpi_ssm_mark_aborted(ssm, threshold);
goto out;
}*/
sum = 0;
for (i = 516; i < 530; i++)
@@ -847,16 +573,19 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
}
if (sum > 0) {
/* FIXME: would preallocating strip buffers be a decent optimization? */
stripdata = g_malloc(128 * 4);
memcpy(stripdata, data + 1, 128 * 4);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips_len++;
/* FIXME: would preallocating strip buffers be a decent optimization? */
struct fpi_frame *stripe = g_malloc(FRAME_WIDTH * (FRAME_HEIGHT / 2) + sizeof(struct fpi_frame));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
memcpy(stripdata, data + 1, FRAME_WIDTH * (FRAME_HEIGHT / 2));
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
aesdev->blanks_count = 0;
}
if (sum < 0) {
fpi_ssm_mark_aborted(ssm, sum);
fpi_ssm_mark_failed(ssm, sum);
goto out;
}
fp_dbg("sum=%d", sum);
@@ -876,11 +605,20 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
/* stop capturing if MAX_FRAMES is reached */
if (aesdev->blanks_count > 10 || g_slist_length(aesdev->strips) >= MAX_FRAMES) {
struct fp_img *img;
fp_dbg("sending stop capture.... blanks=%d frames=%d", aesdev->blanks_count, g_slist_length(aesdev->strips));
/* send stop capture bits */
aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop), stub_capture_stop_cb, NULL);
/* assemble image and submit it to library */
assemble_and_submit_image(dev);
aesdev->strips = g_slist_reverse(aesdev->strips);
fpi_do_movement_estimation(&assembling_ctx, aesdev->strips, aesdev->strips_len);
img = fpi_assemble_frames(&assembling_ctx, aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
aesdev->blanks_count = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
@@ -896,13 +634,13 @@ out:
libusb_free_transfer(transfer);
}
static void capture_run_state(struct fpi_ssm *ssm)
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
struct aes1610_dev *aesdev = dev->priv;
struct fp_img_dev *dev = user_data;
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(_dev);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_REQS:
fp_dbg("write reqs");
aes_write_regv(dev, capture_reqs, G_N_ELEMENTS(capture_reqs),
@@ -910,7 +648,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
break;
case CAPTURE_READ_DATA:
fp_dbg("read data");
generic_read_ignore_data(ssm, 665);
generic_read_ignore_data(ssm, _dev, STRIP_CAPTURE_LEN);
break;
case CAPTURE_REQUEST_STRIP:
fp_dbg("request strip");
@@ -921,38 +659,33 @@ static void capture_run_state(struct fpi_ssm *ssm)
generic_write_regv_cb, ssm);
break;
case CAPTURE_READ_STRIP: ;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(665);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 665,
data = g_malloc(STRIP_CAPTURE_LEN);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, STRIP_CAPTURE_LEN,
capture_read_strip_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
}
break;
};
}
static void capture_sm_complete(struct fpi_ssm *ssm)
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
struct aes1610_dev *aesdev = dev->priv;
struct fp_img_dev *dev = user_data;
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(_dev);
fp_dbg("");
G_DEBUG_HERE();
if (aesdev->deactivating)
complete_deactivation(dev);
else if (ssm->error)
fpi_imgdev_session_error(dev, ssm->error);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
@@ -960,17 +693,16 @@ static void capture_sm_complete(struct fpi_ssm *ssm)
static void start_capture(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
struct fpi_ssm *ssm;
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
G_DEBUG_HERE();
fpi_ssm_start(ssm, capture_sm_complete);
}
@@ -980,73 +712,48 @@ static const struct aes_regwrite init[] = {
{ 0x82, 0x00 }
};
/*
static const struct aes_regwrite stop_reader[] = {
{ 0xFF, 0x00 }
};
*/
enum activate_states {
WRITE_INIT,
// READ_DATA,
// READ_REGS,
ACTIVATE_NUM_STATES,
};
/* this come from aes2501 and is unused here
void activate_read_regs_cb(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data)
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct aes1610_dev *aesdev = dev->priv;
if (status != 0) {
fpi_ssm_mark_aborted(ssm, status);
} else {
fpi_ssm_next_state(ssm);
}
}
*/
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = user_data;
/* activation on aes1610 seems much more straightforward compared to aes2501 */
/* verify theres anything missing here */
switch (ssm->cur_state) {
/* verify there's anything missing here */
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT:
fp_dbg("write init");
aes_write_regv(dev, init, G_N_ELEMENTS(init), generic_write_regv_cb, ssm);
break;
/* case READ_DATA:
fp_dbg("read data");
generic_read_ignore_data(ssm, 20);
break;
case READ_REGS:
fp_dbg("read regs");
read_regs(dev, activate_read_regs_cb, ssm);
break;*/
}
}
/* jump to finger detection */
static void activate_sm_complete(struct fpi_ssm *ssm)
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
fp_dbg("status %d", ssm->error);
fpi_imgdev_activate_complete(dev, ssm->error);
struct fp_img_dev *dev = user_data;
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!ssm->error)
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
aesdev->read_regs_retry_count = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
@@ -1054,7 +761,7 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
/* FIXME: audit cancellation points, probably need more, specifically
* in error handling paths? */
aesdev->deactivating = TRUE;
@@ -1062,8 +769,8 @@ static void dev_deactivate(struct fp_img_dev *dev)
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
fp_dbg("");
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
/* FIXME: if we're in the middle of a scan, we should cancel the scan.
* maybe we can do this with a master reset, unconditionally? */
@@ -1080,22 +787,26 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* FIXME check endpoints */
int r;
struct aes1610_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = g_malloc0(sizeof(struct aes1610_dev));
aesdev = g_malloc0(sizeof(struct aes1610_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
g_free(dev->priv);
libusb_release_interface(dev->udev, 0);
struct aes1610_dev *aesdev;
aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
@@ -1106,7 +817,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver aes1610_driver = {
.driver = {
.id = 6,
.id = AES1610_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1610",
.id_table = id_table,
@@ -1114,13 +825,9 @@ struct fp_img_driver aes1610_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 128,
.img_width = IMAGE_WIDTH,
/* temporarily lowered until we sort out image processing code
* binarized scan quality is good, minutiae detection is accurate,
* it's just that we get fewer minutiae than other scanners (less scanning
* area) */
.bz3_threshold = 10,
.bz3_threshold = 20,
.open = dev_init,
.close = dev_deinit,

112
libfprint/drivers/aes1660.c Normal file
View File

@@ -0,0 +1,112 @@
/*
* AuthenTec AES1660 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* 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 "aes1660"
#include "drivers_api.h"
#include "aeslib.h"
#include "aesx660.h"
#include "aes1660.h"
#define FRAME_WIDTH 128
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = AESX660_FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = g_malloc0(sizeof(struct aesX660_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
aesdev->buffer = g_malloc0(AES1660_FRAME_SIZE + AESX660_HEADER_SIZE);
aesdev->init_seqs[0] = aes1660_init_1;
aesdev->init_seqs_len[0] = G_N_ELEMENTS(aes1660_init_1);
aesdev->init_seqs[1] = aes1660_init_2;
aesdev->init_seqs_len[1] = G_N_ELEMENTS(aes1660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes1660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes1660_start_imaging_cmd);
aesdev->assembling_ctx = &assembling_ctx;
aesdev->extra_img_flags = FP_IMG_PARTIAL;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x1660 },
{ .vendor = 0x08ff, .product = 0x1680 },
{ .vendor = 0x08ff, .product = 0x1681 },
{ .vendor = 0x08ff, .product = 0x1682 },
{ .vendor = 0x08ff, .product = 0x1683 },
{ .vendor = 0x08ff, .product = 0x1684 },
{ .vendor = 0x08ff, .product = 0x1685 },
{ .vendor = 0x08ff, .product = 0x1686 },
{ .vendor = 0x08ff, .product = 0x1687 },
{ .vendor = 0x08ff, .product = 0x1688 },
{ .vendor = 0x08ff, .product = 0x1689 },
{ .vendor = 0x08ff, .product = 0x168a },
{ .vendor = 0x08ff, .product = 0x168b },
{ .vendor = 0x08ff, .product = 0x168c },
{ .vendor = 0x08ff, .product = 0x168d },
{ .vendor = 0x08ff, .product = 0x168e },
{ .vendor = 0x08ff, .product = 0x168f },
{ 0, 0, 0, },
};
struct fp_img_driver aes1660_driver = {
.driver = {
.id = AES1660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1660",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.bz3_threshold = 20,
.open = dev_init,
.close = dev_deinit,
.activate = aesX660_dev_activate,
.deactivate = aesX660_dev_deactivate,
};

1990
libfprint/drivers/aes1660.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
* AuthenTec AES2501 driver for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007 Vasily Khoruzhick
* Copyright (C) 2007-2008, 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* Based on code from http://home.gna.org/aes2501, relicensed with permission
*
@@ -23,13 +23,8 @@
#define FP_COMPONENT "aes2501"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "drivers_api.h"
#include "aeslib.h"
#include "aes2501.h"
static void start_capture(struct fp_img_dev *dev);
@@ -41,6 +36,11 @@ static void complete_deactivation(struct fp_img_dev *dev);
#define BULK_TIMEOUT 4000
#define FINGER_DETECTION_LEN 20
#define READ_REGS_LEN 126
#define READ_REGS_RESP_LEN 159
#define STRIP_CAPTURE_LEN 1705
/*
* The AES2501 is an imaging device using a swipe-type sensor. It samples
* the finger at preprogrammed intervals, sending a 192x16 frame to the
@@ -56,6 +56,7 @@ static void complete_deactivation(struct fp_img_dev *dev);
#define FRAME_WIDTH 192
#define FRAME_HEIGHT 16
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
/* maximum number of frames to read during a scan */
/* FIXME reduce substantially */
#define MAX_FRAMES 150
@@ -67,6 +68,14 @@ struct aes2501_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
int no_finger_cnt;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
typedef void (*aes2501_read_regs_cb)(struct fp_img_dev *dev, int status,
@@ -79,9 +88,12 @@ struct aes2501_read_regs {
void *user_data;
};
static void read_regs_data_cb(struct libusb_transfer *transfer)
static void read_regs_data_cb(struct libusb_transfer *transfer,
struct fp_dev *dev,
fpi_ssm *ssm,
void *user_data)
{
struct aes2501_read_regs *rdata = transfer->user_data;
struct aes2501_read_regs *rdata = user_data;
unsigned char *retdata = NULL;
int r;
@@ -96,14 +108,12 @@ static void read_regs_data_cb(struct libusb_transfer *transfer)
rdata->callback(rdata->dev, r, retdata, rdata->user_data);
g_free(rdata);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void read_regs_rq_cb(struct fp_img_dev *dev, int result, void *user_data)
{
struct aes2501_read_regs *rdata = user_data;
struct libusb_transfer *transfer;
fpi_usb_transfer *transfer;
unsigned char *data;
int r;
@@ -111,20 +121,18 @@ static void read_regs_rq_cb(struct fp_img_dev *dev, int result, void *user_data)
if (result != 0)
goto err;
transfer = libusb_alloc_transfer(0);
if (!transfer) {
result = -ENOMEM;
goto err;
}
data = g_malloc(READ_REGS_LEN);
transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
NULL,
EP_IN,
data,
READ_REGS_LEN,
read_regs_data_cb,
rdata,
BULK_TIMEOUT);
data = g_malloc(126);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 126,
read_regs_data_cb, rdata, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
r = fpi_usb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
result = -EIO;
goto err;
}
@@ -143,7 +151,7 @@ static void read_regs(struct fp_img_dev *dev, aes2501_read_regs_cb callback,
struct aes_regwrite *regwrite = g_malloc(sizeof(*regwrite));
struct aes2501_read_regs *rdata = g_malloc(sizeof(*rdata));
fp_dbg("");
G_DEBUG_HERE();
regwrite->reg = AES2501_REG_CTRL2;
regwrite->value = AES2501_CTRL2_READ_REGS;
rdata->dev = dev;
@@ -176,52 +184,48 @@ static int regval_from_dump(unsigned char *data, uint8_t target)
static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
struct fpi_ssm *ssm = user_data;
fpi_ssm *ssm = user_data;
if (result == 0)
fpi_ssm_next_state(ssm);
else
fpi_ssm_mark_aborted(ssm, result);
fpi_ssm_mark_failed(ssm, result);
}
/* check that read succeeded but ignore all data */
static void generic_ignore_data_cb(struct libusb_transfer *transfer)
static void generic_ignore_data_cb(struct libusb_transfer *transfer,
struct fp_dev *dev,
fpi_ssm *ssm,
void *user_data)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
else if (transfer->length != transfer->actual_length)
fpi_ssm_mark_aborted(ssm, -EPROTO);
fpi_ssm_mark_failed(ssm, -EPROTO);
else
fpi_ssm_next_state(ssm);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
/* read the specified number of bytes from the IN endpoint but throw them
* away, then increment the SSM */
static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
static void generic_read_ignore_data(fpi_ssm *ssm, struct fp_dev *dev, size_t bytes)
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
fpi_usb_transfer *transfer;
unsigned char *data;
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
data = g_malloc(bytes);
libusb_fill_bulk_transfer(transfer, ssm->dev->udev, EP_IN, data, bytes,
generic_ignore_data_cb, ssm, BULK_TIMEOUT);
transfer = fpi_usb_fill_bulk_transfer(dev,
ssm,
EP_IN,
data,
bytes,
generic_ignore_data_cb,
NULL,
BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
r = fpi_usb_submit_transfer(transfer);
if (r < 0)
fpi_ssm_mark_failed(ssm, r);
}
/****** IMAGE PROCESSING ******/
@@ -245,119 +249,6 @@ static int sum_histogram_values(unsigned char *data, uint8_t threshold)
return r;
}
/* find overlapping parts of frames */
static unsigned int find_overlap(unsigned char *first_frame,
unsigned char *second_frame, unsigned int *min_error)
{
unsigned int dy;
unsigned int not_overlapped_height = 0;
*min_error = 255 * FRAME_SIZE;
for (dy = 0; dy < FRAME_HEIGHT; dy++) {
/* Calculating difference (error) between parts of frames */
unsigned int i;
unsigned int error = 0;
for (i = 0; i < FRAME_WIDTH * (FRAME_HEIGHT - dy); i++) {
/* Using ? operator to avoid abs function */
error += first_frame[i] > second_frame[i] ?
(first_frame[i] - second_frame[i]) :
(second_frame[i] - first_frame[i]);
}
/* Normalize error */
error *= 15;
error /= i;
if (error < *min_error) {
*min_error = error;
not_overlapped_height = dy;
}
first_frame += FRAME_WIDTH;
}
return not_overlapped_height;
}
/* assemble a series of frames into a single image */
static unsigned int assemble(struct aes2501_dev *aesdev, unsigned char *output,
gboolean reverse, unsigned int *errors_sum)
{
uint8_t *assembled = output;
int frame;
uint32_t image_height = FRAME_HEIGHT;
unsigned int min_error;
size_t num_strips = aesdev->strips_len;
GSList *list_entry = aesdev->strips;
*errors_sum = 0;
/* Rotating given data by 90 degrees
* Taken from document describing aes2501 image format
* TODO: move reversing detection here */
if (reverse)
output += (num_strips - 1) * FRAME_SIZE;
for (frame = 0; frame < num_strips; frame++) {
aes_assemble_image(list_entry->data, FRAME_WIDTH, FRAME_HEIGHT, output);
if (reverse)
output -= FRAME_SIZE;
else
output += FRAME_SIZE;
list_entry = g_slist_next(list_entry);
}
/* Detecting where frames overlaped */
output = assembled;
for (frame = 1; frame < num_strips; frame++) {
int not_overlapped;
output += FRAME_SIZE;
not_overlapped = find_overlap(assembled, output, &min_error);
*errors_sum += min_error;
image_height += not_overlapped;
assembled += FRAME_WIDTH * not_overlapped;
memcpy(assembled, output, FRAME_SIZE);
}
return image_height;
}
static void assemble_and_submit_image(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
size_t final_size;
struct fp_img *img;
unsigned int errors_sum, r_errors_sum;
BUG_ON(aesdev->strips_len == 0);
/* reverse list */
aesdev->strips = g_slist_reverse(aesdev->strips);
/* create buffer big enough for max image */
img = fpi_img_new(aesdev->strips_len * FRAME_SIZE);
img->flags = FP_IMG_COLORS_INVERTED;
img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
img->height = assemble(aesdev, img->data, TRUE, &r_errors_sum);
if (r_errors_sum > errors_sum) {
img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
img->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
fp_dbg("normal scan direction");
} else {
fp_dbg("reversed scan direction");
}
/* now that overlap has been removed, resize output image buffer */
final_size = img->height * FRAME_WIDTH;
img = fpi_img_resize(img, final_size);
fpi_imgdev_image_captured(dev, img);
/* free strips and strip list */
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
}
/****** FINGER PRESENCE DETECTION ******/
static const struct aes_regwrite finger_det_reqs[] = {
@@ -390,19 +281,22 @@ static const struct aes_regwrite finger_det_reqs[] = {
static void start_finger_detection(struct fp_img_dev *dev);
static void finger_det_data_cb(struct libusb_transfer *transfer)
static void finger_det_data_cb(struct libusb_transfer *transfer,
struct fp_dev *_dev,
fpi_ssm *ssm,
void *user_data)
{
struct fp_img_dev *dev = transfer->user_data;
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
unsigned char *data = transfer->buffer;
int i;
int sum = 0;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_imgdev_session_error(dev, -EIO);
goto out;
return;
} else if (transfer->length != transfer->actual_length) {
fpi_imgdev_session_error(dev, -EPROTO);
goto out;
return;
}
/* examine histogram to determine finger presence */
@@ -416,16 +310,12 @@ static void finger_det_data_cb(struct libusb_transfer *transfer)
/* no finger, poll for a new histogram */
start_finger_detection(dev);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_reqs_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
struct libusb_transfer *transfer;
fpi_usb_transfer *transfer;
unsigned char *data;
int r;
@@ -434,28 +324,25 @@ static void finger_det_reqs_cb(struct fp_img_dev *dev, int result,
return;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
return;
}
data = g_malloc(FINGER_DETECTION_LEN);
transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
NULL,
EP_IN,
data,
FINGER_DETECTION_LEN,
finger_det_data_cb,
NULL,
BULK_TIMEOUT);
data = g_malloc(20);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 20,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
r = fpi_usb_submit_transfer(transfer);
if (r < 0)
fpi_imgdev_session_error(dev, r);
}
}
static void start_finger_detection(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
fp_dbg("");
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
if (aesdev->deactivating) {
complete_deactivation(dev);
@@ -514,13 +401,13 @@ static const struct aes_regwrite capture_reqs_2[] = {
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
};
static const struct aes_regwrite strip_scan_reqs[] = {
static struct aes_regwrite strip_scan_reqs[] = {
{ AES2501_REG_IMAGCTRL,
AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE },
{ AES2501_REG_STRTCOL, 0x00 },
{ AES2501_REG_ENDCOL, 0x2f },
{ AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X },
{ AES2501_REG_ADREFHI, 0x5b },
{ AES2501_REG_ADREFHI, AES2501_ADREFHI_MAX_VALUE },
{ AES2501_REG_ADREFLO, 0x20 },
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
};
@@ -528,7 +415,7 @@ static const struct aes_regwrite strip_scan_reqs[] = {
/* capture SM movement:
* write reqs and read data 1 + 2,
* request and read strip,
* jump back to request UNLESS theres no finger, in which case exit SM,
* jump back to request UNLESS there's no finger, in which case exit SM,
* report lack of finger presence, and move to finger detection */
enum capture_states {
@@ -541,86 +428,111 @@ enum capture_states {
CAPTURE_NUM_STATES,
};
static void capture_read_strip_cb(struct libusb_transfer *transfer)
static void capture_read_strip_cb(struct libusb_transfer *transfer,
struct fp_dev *_dev,
fpi_ssm *ssm,
void *user_data)
{
unsigned char *stripdata;
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aes2501_dev *aesdev = dev->priv;
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(_dev);
unsigned char *data = transfer->buffer;
int sum;
int threshold;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
fpi_ssm_mark_failed(ssm, -EIO);
return;
} else if (transfer->length != transfer->actual_length) {
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
fpi_ssm_mark_failed(ssm, -EPROTO);
return;
}
/* FIXME: would preallocating strip buffers be a decent optimization? */
stripdata = g_malloc(192 * 8);
memcpy(stripdata, data + 1, 192*8);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips_len++;
threshold = regval_from_dump(data + 1 + 192*8 + 1 + 16*2 + 1 + 8,
AES2501_REG_DATFMT);
if (threshold < 0) {
fpi_ssm_mark_aborted(ssm, threshold);
goto out;
fpi_ssm_mark_failed(ssm, threshold);
return;
}
sum = sum_histogram_values(data + 1 + 192*8, threshold & 0x0f);
sum = sum_histogram_values(data + 1 + 192*8, threshold & 0x0f);
if (sum < 0) {
fpi_ssm_mark_aborted(ssm, sum);
goto out;
fpi_ssm_mark_failed(ssm, sum);
return;
}
fp_dbg("sum=%d", sum);
/* FIXME: 0 might be too low as a threshold */
/* FIXME: sometimes we get 0 in the middle of a scan, should we wait for
* a few consecutive zeroes? */
/* FIXME: we should have an upper limit on the number of strips */
if (sum < AES2501_SUM_LOW_THRESH) {
strip_scan_reqs[4].value -= 0x8;
if (strip_scan_reqs[4].value < AES2501_ADREFHI_MIN_VALUE)
strip_scan_reqs[4].value = AES2501_ADREFHI_MIN_VALUE;
} else if (sum > AES2501_SUM_HIGH_THRESH) {
strip_scan_reqs[4].value += 0x8;
if (strip_scan_reqs[4].value > AES2501_ADREFHI_MAX_VALUE)
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
}
fp_dbg("ADREFHI is %.2x", strip_scan_reqs[4].value);
/* If sum is 0, finger has been removed */
/* Sum is 0, maybe finger was removed? Wait for 3 empty frames
* to ensure
*/
if (sum == 0) {
/* assemble image and submit it to library */
assemble_and_submit_image(dev);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
aesdev->no_finger_cnt++;
if (aesdev->no_finger_cnt == 3) {
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
fpi_do_movement_estimation(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img = fpi_assemble_frames(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
}
} else {
/* obtain next strip */
/* FIXME: would preallocating strip buffers be a decent optimization? */
struct fpi_frame *stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
memcpy(stripdata, data + 1, 192*8);
aesdev->no_finger_cnt = 0;
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void capture_run_state(struct fpi_ssm *ssm)
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
struct aes2501_dev *aesdev = dev->priv;
struct fp_img_dev *dev = user_data;
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(_dev);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_REQS_1:
aes_write_regv(dev, capture_reqs_1, G_N_ELEMENTS(capture_reqs_1),
generic_write_regv_cb, ssm);
break;
case CAPTURE_READ_DATA_1:
generic_read_ignore_data(ssm, 159);
generic_read_ignore_data(ssm, _dev, READ_REGS_RESP_LEN);
break;
case CAPTURE_WRITE_REQS_2:
aes_write_regv(dev, capture_reqs_2, G_N_ELEMENTS(capture_reqs_2),
generic_write_regv_cb, ssm);
break;
case CAPTURE_READ_DATA_2:
generic_read_ignore_data(ssm, 159);
generic_read_ignore_data(ssm, _dev, READ_REGS_RESP_LEN);
break;
case CAPTURE_REQUEST_STRIP:
if (aesdev->deactivating)
@@ -630,38 +542,36 @@ static void capture_run_state(struct fpi_ssm *ssm)
generic_write_regv_cb, ssm);
break;
case CAPTURE_READ_STRIP: ;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
fpi_usb_transfer *transfer;
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(STRIP_CAPTURE_LEN);
transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
ssm,
EP_IN,
data,
STRIP_CAPTURE_LEN,
capture_read_strip_cb,
NULL,
BULK_TIMEOUT);
data = g_malloc(1705);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 1705,
capture_read_strip_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
r = fpi_usb_submit_transfer(transfer);
if (r < 0)
fpi_ssm_mark_failed(ssm, r);
break;
};
}
static void capture_sm_complete(struct fpi_ssm *ssm)
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
struct aes2501_dev *aesdev = dev->priv;
struct fp_img_dev *dev = user_data;
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(_dev);
fp_dbg("");
G_DEBUG_HERE();
if (aesdev->deactivating)
complete_deactivation(dev);
else if (ssm->error)
fpi_imgdev_session_error(dev, ssm->error);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
@@ -669,17 +579,19 @@ static void capture_sm_complete(struct fpi_ssm *ssm)
static void start_capture(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
struct fpi_ssm *ssm;
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
aesdev->no_finger_cnt = 0;
/* Reset gain */
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
G_DEBUG_HERE();
fpi_ssm_start(ssm, capture_sm_complete);
}
@@ -791,11 +703,11 @@ enum activate_states {
void activate_read_regs_cb(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct aes2501_dev *aesdev = dev->priv;
fpi_ssm *ssm = user_data;
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (status != 0) {
fpi_ssm_mark_aborted(ssm, status);
fpi_ssm_mark_failed(ssm, status);
} else {
fp_dbg("reg 0xaf = %x", regs[0x5f]);
if (regs[0x5f] != 0x6b || ++aesdev->read_regs_retry_count == 13)
@@ -808,16 +720,16 @@ void activate_read_regs_cb(struct fp_img_dev *dev, int status,
static void activate_init3_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
struct fpi_ssm *ssm = user_data;
fpi_ssm *ssm = user_data;
if (result == 0)
fpi_ssm_jump_to_state(ssm, READ_REGS);
else
fpi_ssm_mark_aborted(ssm, result);
fpi_ssm_mark_failed(ssm, result);
}
static void activate_run_state(struct fpi_ssm *ssm)
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = user_data;
/* This state machine isn't as linear as it may appear. After doing init1
* and init2 register configuration writes, we have to poll a register
@@ -838,14 +750,14 @@ static void activate_run_state(struct fpi_ssm *ssm)
aes_write_regv(init_4);
*/
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT_1:
aes_write_regv(dev, init_1, G_N_ELEMENTS(init_1),
generic_write_regv_cb, ssm);
break;
case READ_DATA_1:
fp_dbg("read data 1");
generic_read_ignore_data(ssm, 20);
generic_read_ignore_data(ssm, _dev, FINGER_DETECTION_LEN);
break;
case WRITE_INIT_2:
aes_write_regv(dev, init_2, G_N_ELEMENTS(init_2),
@@ -855,7 +767,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
read_regs(dev, activate_read_regs_cb, ssm);
break;
case WRITE_INIT_3:
aes_write_regv(dev, init_4, G_N_ELEMENTS(init_4),
aes_write_regv(dev, init_3, G_N_ELEMENTS(init_3),
activate_init3_cb, ssm);
break;
case WRITE_INIT_4:
@@ -869,23 +781,22 @@ static void activate_run_state(struct fpi_ssm *ssm)
}
}
static void activate_sm_complete(struct fpi_ssm *ssm)
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
fp_dbg("status %d", ssm->error);
fpi_imgdev_activate_complete(dev, ssm->error);
struct fp_img_dev *dev = user_data;
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!ssm->error)
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
aesdev->read_regs_retry_count = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
@@ -893,7 +804,7 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
/* FIXME: audit cancellation points, probably need more, specifically
* in error handling paths? */
aesdev->deactivating = TRUE;
@@ -901,8 +812,8 @@ static void dev_deactivate(struct fp_img_dev *dev)
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
fp_dbg("");
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
/* FIXME: if we're in the middle of a scan, we should cancel the scan.
* maybe we can do this with a master reset, unconditionally? */
@@ -918,22 +829,25 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* FIXME check endpoints */
int r;
struct aes2501_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = g_malloc0(sizeof(struct aes2501_dev));
aesdev = g_malloc0(sizeof(struct aes2501_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
g_free(dev->priv);
libusb_release_interface(dev->udev, 0);
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
@@ -945,7 +859,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver aes2501_driver = {
.driver = {
.id = 4,
.id = AES2501_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2501",
.id_table = id_table,
@@ -953,7 +867,7 @@ struct fp_img_driver aes2501_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 192,
.img_width = IMAGE_WIDTH,
.open = dev_init,
.close = dev_deinit,

View File

@@ -109,7 +109,7 @@ enum aes2501_mesure_drive {
/* Select (1=square | 0=sine) wave drive during measure */
#define AES2501_MEASDRV_SQUARE 0x20
/* 0 = use mesure drive setting, 1 = when sine wave is selected */
/* 0 = use measure drive setting, 1 = when sine wave is selected */
#define AES2501_MEASDRV_MEASURE_SQUARE 0x10
enum aes2501_measure_freq {
@@ -167,4 +167,10 @@ enum aes2501_sensor_gain2 {
#define AES2501_LPONT_MIN_VALUE 0x00 /* 0 ms */
#define AES2501_LPONT_MAX_VALUE 0x1f /* About 16 ms */
#define AES2501_ADREFHI_MIN_VALUE 0x28
#define AES2501_ADREFHI_MAX_VALUE 0x58
#define AES2501_SUM_HIGH_THRESH 1000
#define AES2501_SUM_LOW_THRESH 700
#endif /* __AES2501_H */

620
libfprint/drivers/aes2550.c Normal file
View File

@@ -0,0 +1,620 @@
/*
* AuthenTec AES2550/AES2810 driver for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007-2012 Vasily Khoruzhick
*
* Based on AES2501 driver
*
* 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 "aes2550"
#include "drivers_api.h"
#include "aes2550.h"
#include "aeslib.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
/*
* The AES2550 is an imaging device using a swipe-type sensor. It samples
* the finger at preprogrammed intervals, sending a 192x16 frame to the
* computer.
* Unless the user is scanning their finger unreasonably fast, the frames
* *will* overlap. The implementation below detects this overlap and produces
* a contiguous image as the end result.
* The fact that the user determines the length of the swipe (and hence the
* number of useful frames) and also the fact that overlap varies means that
* images returned from this driver vary in height.
*/
#define FRAME_WIDTH 192
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
struct aes2550_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
int heartbeat_cnt;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
/****** FINGER PRESENCE DETECTION ******/
static unsigned char finger_det_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET,
0x95, (8 << AES2550_REG95_COL_SCANNED_OFS) | (1 << AES2550_REG95_EPIX_AVG_OFS),
0xad, 0x00,
0xbd, (0 << AES2550_REGBD_LPO_IN_15_8_OFS),
0xbe, (0 << AES2550_REGBE_LPO_IN_7_0_OFS),
0xcf, AES2550_REGCF_INTERFERENCE_CHK_EN,
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */
AES2550_CMD_RUN_FD,
};
static void start_finger_detection(struct fp_img_dev *dev);
static void finger_det_data_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
unsigned char *data = transfer->buffer;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("data transfer status %d\n", transfer->status);
fpi_imgdev_session_error(dev, -EIO);
goto out;
}
fp_dbg("transfer completed, len: %.4x, data: %.2x %.2x",
transfer->actual_length, (int)data[0], (int)data[1]);
/* Check if we got 2 bytes, reg address 0x83 and its value */
if ((transfer->actual_length >= 2) && (data[0] == 0x83) && (data[1] & AES2550_REG83_FINGER_PRESENT)) {
/* finger present, start capturing */
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
} else {
/* no finger, poll for a new histogram */
start_finger_detection(dev);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_reqs_cb(struct libusb_transfer *t)
{
struct libusb_transfer *transfer;
unsigned char *data;
int r;
struct fp_img_dev *dev = t->user_data;
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("req transfer status %d\n", t->status);
fpi_imgdev_session_error(dev, -EIO);
goto exit_free_transfer;
} else if (t->length != t->actual_length) {
fp_dbg("expected %d, got %d bytes", t->length, t->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
goto exit_free_transfer;
}
transfer = fpi_usb_alloc();
/* 2 bytes of result */
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
exit_free_transfer:
libusb_free_transfer(t);
}
static void start_finger_detection(struct fp_img_dev *dev)
{
int r;
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
struct libusb_transfer *transfer;
G_DEBUG_HERE();
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT, finger_det_reqs,
sizeof(finger_det_reqs), finger_det_reqs_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
}
/****** CAPTURE ******/
static unsigned char capture_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET,
0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_HGC_ENABLE),
0x85, AES2550_REG85_FLUSH_PER_FRAME,
0x8f, AES2550_REG8F_AUTH_DISABLE | AES2550_REG8F_EHISTO_DISABLE,
0xbf, AES2550_REGBF_RSR_DIR_UPDOWN_MOTION | AES2550_REGBF_RSR_LEVEL_SUPER_RSR,
0xcf, (3 << AES2550_REGCF_INTERFERENCE_AVG_OFFS) | AES2550_REGCF_INTERFERENCE_AVG_EN,
0xdc, (1 << AES2550_REGDC_BP_NUM_REF_SWEEP_OFS),
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x03, /* Heart beat cmd, 3 * 16 cycles without sending image */
AES2550_CMD_GET_ENROLL_IMG,
};
static unsigned char capture_set_idle_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET,
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */
AES2550_CMD_SET_IDLE_MODE,
};
enum capture_states {
CAPTURE_WRITE_REQS,
CAPTURE_READ_DATA,
CAPTURE_SET_IDLE,
CAPTURE_NUM_STATES,
};
/* Returns number of processed bytes */
static int process_strip_data(fpi_ssm *ssm, struct fp_img_dev *dev, unsigned char *data)
{
unsigned char *stripdata;
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
struct fpi_frame *stripe;
int len;
if (data[0] != AES2550_EDATA_MAGIC) {
fp_dbg("Bogus magic: %.2x\n", (int)(data[0]));
return -EPROTO;
}
len = data[1] * 256 + data[2];
if (len != (AES2550_STRIP_SIZE - 3)) {
fp_dbg("Bogus frame len: %.4x\n", len);
}
stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame)); /* 4 bits per pixel */
stripe->delta_x = (int8_t)data[6];
stripe->delta_y = -(int8_t)data[7];
stripdata = stripe->data;
memcpy(stripdata, data + 33, FRAME_WIDTH * FRAME_HEIGHT / 2);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
fp_dbg("deltas: %dx%d", stripe->delta_x, stripe->delta_y);
return 0;
}
static void capture_reqs_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_set_idle_reqs_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length) &&
aesdev->strips_len) {
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = fpi_assemble_frames(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_read_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = transfer->buffer;
int r;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("request is not completed, %d", transfer->status);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
fp_dbg("request completed, len: %.4x", transfer->actual_length);
if (transfer->actual_length >= 2)
fp_dbg("data: %.2x %.2x", (int)data[0], (int)data[1]);
switch (transfer->actual_length) {
case AES2550_STRIP_SIZE:
r = process_strip_data(ssm, dev, data);
if (r < 0) {
fp_dbg("Processing strip data failed: %d", r);
fpi_ssm_mark_failed(ssm, -EPROTO);
goto out;
}
aesdev->heartbeat_cnt = 0;
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
break;
case AES2550_HEARTBEAT_SIZE:
if (data[0] == AES2550_HEARTBEAT_MAGIC) {
/* No data for a long time => finger was removed or there's no movement */
aesdev->heartbeat_cnt++;
if (aesdev->heartbeat_cnt == 3) {
/* Got 3 heartbeat message, that's enough to consider that finger was removed,
* assemble image and submit it to the library */
fp_dbg("Got 3 heartbeats => finger removed");
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
}
}
break;
default:
fp_dbg("Short frame %d, skip", transfer->actual_length);
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
break;
}
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_REQS:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT, capture_reqs,
sizeof(capture_reqs), capture_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, -ENOMEM);
}
}
break;
case CAPTURE_READ_DATA:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
capture_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
break;
case CAPTURE_SET_IDLE:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT, capture_set_idle_reqs,
sizeof(capture_set_idle_reqs), capture_set_idle_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, -ENOMEM);
}
}
break;
};
}
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(_dev);
fp_dbg("Capture completed");
if (aesdev->deactivating)
complete_deactivation(dev);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static void start_capture(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
aesdev->heartbeat_cnt = 0;
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
G_DEBUG_HERE();
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
static unsigned char init_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET, /* Master reset */
0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_FORCE_FINGER_PRESENT),
0x85, AES2550_REG85_FLUSH_PER_FRAME,
0xa8, AES2550_REGA8_DIG_BIT_EN,
0x81, AES2550_REG81_NSHOT,
};
static unsigned char calibrate_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET, /* Master reset */
AES2550_CMD_CALIBRATE,
AES2550_CMD_READ_CALIBRATION_DATA,
};
enum activate_states {
WRITE_INIT,
READ_DATA,
CALIBRATE,
READ_CALIB_TABLE,
ACTIVATE_NUM_STATES,
};
static void init_reqs_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void init_read_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
/* TODO: use calibration table, datasheet is rather terse on that
* need more info for implementation */
static void calibrate_read_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT, init_reqs,
sizeof(init_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, -ENOMEM);
}
}
break;
case READ_DATA:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
init_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
break;
case CALIBRATE:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT, calibrate_reqs,
sizeof(calibrate_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, -ENOMEM);
}
}
break;
case READ_CALIB_TABLE:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
calibrate_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
break;
}
}
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev)
{
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
aesdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_deactivate_complete(dev);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct aes2550_dev *aes2550_dev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aes2550_dev = g_malloc0(sizeof(struct aes2550_dev));
fp_dev_set_instance_data(FP_DEV(dev), aes2550_dev);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev;
aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x2550 }, /* AES2550 */
{ .vendor = 0x08ff, .product = 0x2810 }, /* AES2810 */
{ 0, 0, 0, },
};
struct fp_img_driver aes2550_driver = {
.driver = {
.id = AES2550_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2550/AES2810",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

114
libfprint/drivers/aes2550.h Normal file
View File

@@ -0,0 +1,114 @@
/*
* AuthenTec AES2550/AES2810 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* 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
*/
#ifndef __AES2550_H
#define __AES2550_H
/* Registers bits */
#define AES2550_REG80_MASTER_RESET (1 << 0)
#define AES2550_REG80_FORCE_FINGER_PRESENT (1 << 1)
#define AES2550_REG80_LPO_START (1 << 2)
#define AES2550_REG80_HGC_ENABLE (1 << 3)
#define AES2550_REG80_SENSOR_MODE_OFS (4)
#define AES2550_REG80_AUTO_RESTART_FD (1 << 6)
#define AES2550_REG80_EXT_REG_ENABLE (1 << 7)
#define AES2550_REG81_CONT_SCAN (1 << 0)
#define AES2550_REG81_READ_REG (1 << 1)
#define AES2550_REG81_NSHOT (1 << 2)
#define AES2550_REG81_RUN_FD (1 << 3)
#define AES2550_REG81_READ_ID (1 << 4)
#define AES2550_REG81_RUN_CAL (1 << 5)
#define AES2550_REG81_RUN_TIMER (1 << 6)
#define AES2550_REG81_RUN_BIST (1 << 7)
#define AES2550_REG83_FINGER_PRESENT (1 << 7)
#define AES2550_REG85_FLUSH_PER_FRAME (1 << 7)
#define AES2550_REG8F_EDATA_DISABLE (1 << 1)
#define AES2550_REG8F_AUTH_DISABLE (1 << 2)
#define AES2550_REG8F_EHISTO_DISABLE (1 << 3)
#define AES2550_REG8F_HISTO64 (1 << 4)
#define AES2550_REG8F_SINGLE_REG_ENABLE (1 << 6)
#define AES2550_REG95_COL_SCANNED_OFS (0)
#define AES2550_REG95_EPIX_AVG_OFS (4)
#define AES2550_REGA8_DIG_BIT_DATA_OFS (0)
#define AES2550_REGA8_DIG_BIT_EN (1 << 4)
#define AES2550_REGA8_FIXED_BIT_DATA (1 << 5)
#define AES2550_REGA8_INVERT_BIT_DATA (1 << 6)
#define AES2550_REGAD_LPFD_AVG_OFS (0)
#define AES2550_REGAD_DETECT_FGROFF (1 << 4)
#define AES2550_REGAD_ADVRANGE_2V (1 << 6)
#define AES2550_REGB1_ATE_CONT_IMAGE (1 << 1)
#define AES2550_REGB1_ANALOG_RESET (1 << 2)
#define AES2550_REGB1_ANALOG_PD (1 << 3)
#define AES2550_REGB1_TEST_EMBD_WORD (1 << 4)
#define AES2550_REGB1_ORIG_EMBD_WORD (1 << 5)
#define AES2550_REGB1_RESET_UHSM (1 << 6)
#define AES2550_REGB1_RESET_SENSOR (1 << 7)
#define AES2550_REGBD_LPO_IN_15_8_OFS (0)
#define AES2550_REGBE_LPO_IN_7_0_OFS (0)
#define AES2550_REGBF_RSR_LEVEL_DISABLED (0 << 0)
#define AES2550_REGBF_RSR_LEVEL_LEADING_RSR (1 << 0)
#define AES2550_REGBF_RSR_LEVEL_SIMPLE_RSR (2 << 0)
#define AES2550_REGBF_RSR_LEVEL_SUPER_RSR (3 << 0)
#define AES2550_REGBF_RSR_DIR_DOWN_MOTION (0 << 2)
#define AES2550_REGBF_RSR_DIR_UP_MOTION (1 << 2)
#define AES2550_REGBF_RSR_DIR_UPDOWN_MOTION (2 << 2)
#define AES2550_REGBF_NOISE_FLOOR_MODE (1 << 4)
#define AES2550_REGBF_QUADRATURE_MODE (1 << 5)
#define AES2550_REGCF_INTERFERENCE_CHK_EN (1 << 0)
#define AES2550_REGCF_INTERFERENCE_AVG_EN (1 << 1)
#define AES2550_REGCF_INTERFERENCE_AVG_OFFS (4)
#define AES2550_REGDC_BP_NUM_REF_SWEEP_OFS (0)
#define AES2550_REGDC_DEBUG_CTRL2_OFS (3)
#define AES2550_REGDD_DEBUG_CTRL1_OFS (0)
/* Commands */
enum aes2550_cmds {
AES2550_CMD_SET_IDLE_MODE = 0x00,
AES2550_CMD_RUN_FD = 0x01,
AES2550_CMD_GET_ENROLL_IMG = 0x02,
AES2550_CMD_CALIBRATE = 0x06,
AES2550_CMD_READ_CALIBRATION_DATA = 0x10,
AES2550_CMD_HEARTBEAT = 0x70,
};
/* Messages */
#define AES2550_STRIP_SIZE (0x31e + 3)
#define AES2550_HEARTBEAT_SIZE (4 + 3)
#define AES2550_EDATA_MAGIC 0xe0
#define AES2550_HEARTBEAT_MAGIC 0xdb
#define AES2550_EP_IN_BUF_SIZE 8192
#endif

114
libfprint/drivers/aes2660.c Normal file
View File

@@ -0,0 +1,114 @@
/*
* AuthenTec AES2660 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* 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 "aes2660"
#include "drivers_api.h"
#include "aeslib.h"
#include "aesx660.h"
#include "aes2660.h"
#define FRAME_WIDTH 192
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = AESX660_FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = g_malloc0(sizeof(struct aesX660_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
aesdev->buffer = g_malloc0(AES2660_FRAME_SIZE + AESX660_HEADER_SIZE);
/* No scaling for AES2660 */
aesdev->init_seqs[0] = aes2660_init_1;
aesdev->init_seqs_len[0] = G_N_ELEMENTS(aes2660_init_1);
aesdev->init_seqs[1] = aes2660_init_2;
aesdev->init_seqs_len[1] = G_N_ELEMENTS(aes2660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes2660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes2660_start_imaging_cmd);
aesdev->assembling_ctx = &assembling_ctx;
aesdev->extra_img_flags = FP_IMG_PARTIAL;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev;
aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x2660 },
{ .vendor = 0x08ff, .product = 0x2680 },
{ .vendor = 0x08ff, .product = 0x2681 },
{ .vendor = 0x08ff, .product = 0x2682 },
{ .vendor = 0x08ff, .product = 0x2683 },
{ .vendor = 0x08ff, .product = 0x2684 },
{ .vendor = 0x08ff, .product = 0x2685 },
{ .vendor = 0x08ff, .product = 0x2686 },
{ .vendor = 0x08ff, .product = 0x2687 },
{ .vendor = 0x08ff, .product = 0x2688 },
{ .vendor = 0x08ff, .product = 0x2689 },
{ .vendor = 0x08ff, .product = 0x268a },
{ .vendor = 0x08ff, .product = 0x268b },
{ .vendor = 0x08ff, .product = 0x268c },
{ .vendor = 0x08ff, .product = 0x268d },
{ .vendor = 0x08ff, .product = 0x268e },
{ .vendor = 0x08ff, .product = 0x268f },
{ .vendor = 0x08ff, .product = 0x2691 },
{ 0, 0, 0, },
};
struct fp_img_driver aes2660_driver = {
.driver = {
.id = AES2660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2660",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,
.activate = aesX660_dev_activate,
.deactivate = aesX660_dev_deactivate,
};

1964
libfprint/drivers/aes2660.h Normal file

File diff suppressed because it is too large Load Diff

183
libfprint/drivers/aes3500.c Normal file
View File

@@ -0,0 +1,183 @@
/*
* AuthenTec AES3500 driver for libfprint
*
* AES3500 is a press-typed sensor, which captures image in 128x128
* pixels.
*
* Thanks Rafael Toledo for the Windows driver and the help.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2011-2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 "aes3500"
#include "drivers_api.h"
#include "aeslib.h"
#include "aes3k.h"
#define DATA_BUFLEN 0x2089
/* image size = FRAME_WIDTH x FRAME_WIDTH */
#define FRAME_WIDTH 128
#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
#define ENLARGE_FACTOR 2
static struct aes_regwrite init_reqs[] = {
/* master reset */
{ 0x80, 0x01 },
{ 0, 0 },
{ 0x80, 0x00 },
{ 0, 0 },
{ 0x81, 0x00 },
{ 0x80, 0x00 },
{ 0, 0 },
/* scan reset */
{ 0x80, 0x02 },
{ 0, 0 },
{ 0x80, 0x00 },
{ 0, 0 },
/* disable register buffering */
{ 0x80, 0x04 },
{ 0, 0 },
{ 0x80, 0x00 },
{ 0, 0 },
{ 0x81, 0x00 },
{ 0, 0 },
/* windows driver reads registers now (81 02) */
{ 0x80, 0x00 },
{ 0x81, 0x00 },
/* set excitation bias current: 2mhz drive ring frequency,
* 4V drive ring voltage, 16.5mA excitation bias */
{ 0x82, 0x04 },
/* continuously sample drive ring for finger detection,
* 62.50ms debounce delay */
{ 0x83, 0x13 },
{ 0x84, 0x07 }, /* set calibration resistance to 12 kiloohms */
{ 0x85, 0x3d }, /* set calibration capacitance */
{ 0x86, 0x03 }, /* detect drive voltage */
{ 0x87, 0x01 }, /* set detection frequency to 125khz */
{ 0x88, 0x02 }, /* set column scan period */
{ 0x89, 0x02 }, /* set measure drive */
{ 0x8a, 0x33 }, /* set measure frequency and sense amplifier bias */
{ 0x8b, 0x33 }, /* set matrix pattern */
{ 0x8c, 0x0f }, /* set demodulation phase 1 */
{ 0x8d, 0x04 }, /* set demodulation phase 2 */
{ 0x8e, 0x23 }, /* set sensor gain */
{ 0x8f, 0x07 }, /* set image parameters */
{ 0x90, 0x00 }, /* carrier offset null */
{ 0x91, 0x1c }, /* set A/D reference high */
{ 0x92, 0x08 }, /* set A/D reference low */
{ 0x93, 0x00 }, /* set start row to 0 */
{ 0x94, 0x07 }, /* set end row */
{ 0x95, 0x00 }, /* set start column to 0 */
{ 0x96, 0x1f }, /* set end column */
{ 0x97, 0x04 }, /* data format and thresholds */
{ 0x98, 0x28 }, /* image data control */
{ 0x99, 0x00 }, /* disable general purpose outputs */
{ 0x9a, 0x0b }, /* set initial scan state */
{ 0x9b, 0x00 }, /* clear challenge word bits */
{ 0x9c, 0x00 }, /* clear challenge word bits */
{ 0x9d, 0x09 }, /* set some challenge word bits */
{ 0x9e, 0x53 }, /* clear challenge word bits */
{ 0x9f, 0x6b }, /* set some challenge word bits */
{ 0, 0 },
{ 0x80, 0x00 },
{ 0x81, 0x00 },
{ 0, 0 },
{ 0x81, 0x04 },
{ 0, 0 },
{ 0x81, 0x00 },
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
struct aes3k_dev *aesdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = g_malloc0(sizeof(struct aes3k_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
if (!aesdev)
return -ENOMEM;
aesdev->data_buflen = DATA_BUFLEN;
aesdev->frame_width = FRAME_WIDTH;
aesdev->frame_size = FRAME_SIZE;
aesdev->frame_number = FRAME_NUMBER;
aesdev->enlarge_factor = ENLARGE_FACTOR;
aesdev->init_reqs = init_reqs;
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
return r;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x5731 },
{ 0, 0, 0, },
};
struct fp_img_driver aes3500_driver = {
.driver = {
.id = AES3500_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES3500",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
.img_height = FRAME_WIDTH * ENLARGE_FACTOR,
.img_width = FRAME_WIDTH * ENLARGE_FACTOR,
/* temporarily lowered until image quality improves */
.bz3_threshold = 9,
.open = dev_init,
.close = dev_deinit,
.activate = aes3k_dev_activate,
.deactivate = aes3k_dev_deactivate,
};

158
libfprint/drivers/aes3k.c Normal file
View File

@@ -0,0 +1,158 @@
/*
* AuthenTec AES3500/AES4000 common routines
*
* The AES3500 and AES4000 sensors are press-typed, and could capture
* fingerprint images in 128x128 and 96x96 pixels respectively. They
* share a same communication interface: a number of frames are
* transferred and captured, from which a final image could be
* assembled. Each frame has fixed height of 16 pixels.
*
* As the imaging area is a bit small, only a part of finger could be
* captured, the detected minutiae are not so many that the NBIS
* matching works not so good. The verification rate is very low at the
* moment.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 "aes3k"
#include "drivers_api.h"
#include "aeslib.h"
#include "aes3k.h"
#define CTRL_TIMEOUT 1000
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
static void do_capture(struct fp_img_dev *dev);
static void aes3k_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output)
{
size_t row, column;
for (column = 0; column < width; column++) {
for (row = 0; row < height; row += 2) {
output[width * row + column] = (*input & 0x0f) * 17;
output[width * (row + 1) + column] = ((*input & 0xf0) >> 4) * 17;
input++;
}
}
}
static void img_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *ptr = transfer->buffer;
struct fp_img *tmp;
struct fp_img *img;
int i;
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
goto err;
} else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_imgdev_session_error(dev, -EIO);
goto err;
} else if (transfer->length != transfer->actual_length) {
fpi_imgdev_session_error(dev, -EPROTO);
goto err;
}
fpi_imgdev_report_finger_status(dev, TRUE);
tmp = fpi_img_new(aesdev->frame_width * aesdev->frame_width);
tmp->width = aesdev->frame_width;
tmp->height = aesdev->frame_width;
tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
for (i = 0; i < aesdev->frame_number; i++) {
fp_dbg("frame header byte %02x", *ptr);
ptr++;
aes3k_assemble_image(ptr, aesdev->frame_width, AES3K_FRAME_HEIGHT, tmp->data + (i * aesdev->frame_width * AES3K_FRAME_HEIGHT));
ptr += aesdev->frame_size;
}
/* FIXME: this is an ugly hack to make the image big enough for NBIS
* to process reliably */
img = fpi_img_resize(tmp, aesdev->enlarge_factor, aesdev->enlarge_factor);
fp_img_free(tmp);
fpi_imgdev_image_captured(dev, img);
/* FIXME: rather than assuming finger has gone, we should poll regs until
* it really has, then restart the capture */
fpi_imgdev_report_finger_status(dev, FALSE);
do_capture(dev);
err:
g_free(transfer->buffer);
aesdev->img_trf = NULL;
libusb_free_transfer(transfer);
}
static void do_capture(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data;
int r;
aesdev->img_trf = fpi_usb_alloc();
data = g_malloc(aesdev->data_buflen);
libusb_fill_bulk_transfer(aesdev->img_trf, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data,
aesdev->data_buflen, img_cb, dev, 0);
r = libusb_submit_transfer(aesdev->img_trf);
if (r < 0) {
g_free(data);
libusb_free_transfer(aesdev->img_trf);
aesdev->img_trf = NULL;
fpi_imgdev_session_error(dev, r);
}
}
static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
{
fpi_imgdev_activate_complete(dev, result);
if (result == 0)
do_capture(dev);
}
int aes3k_dev_activate(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
aes_write_regv(dev, aesdev->init_reqs, aesdev->init_reqs_len, init_reqs_cb, NULL);
return 0;
}
void aes3k_dev_deactivate(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
/* FIXME: should wait for cancellation to complete before returning
* from deactivation, otherwise app may legally exit before we've
* cleaned up */
if (aesdev->img_trf)
libusb_cancel_transfer(aesdev->img_trf);
fpi_imgdev_deactivate_complete(dev);
}

58
libfprint/drivers/aes3k.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* AuthenTec AES3500/AES4000 common routines
*
* The AES3500 and AES4000 sensors are press-typed, and could capture
* fingerprint images in 128x128 and 96x96 pixels respectively. They
* share a same communication interface: a number of frames are
* transferred and captured, from which a final image could be
* assembled. Each frame has fixed height of 16 pixels.
*
* As the imaging area is a bit small, only a part of finger could be
* captured, the detected minutiae are not so many that the NBIS
* matching works not so good. The verification rate is very low at the
* moment.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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
*
*/
#ifndef __AES3K_H
#define __AES3K_H
#define AES3K_FRAME_HEIGHT 16
struct aes3k_dev {
struct libusb_transfer *img_trf;
size_t frame_width; /* image size = frame_width x frame_width */
size_t frame_size; /* 4 bits/pixel: frame_width x AES3K_FRAME_HEIGHT / 2 */
size_t frame_number; /* number of frames */
size_t enlarge_factor;
size_t data_buflen; /* buffer length of usb bulk transfer */
struct aes_regwrite *init_reqs; /* initial values sent to device */
size_t init_reqs_len;
};
int aes3k_dev_activate(struct fp_img_dev *dev);
void aes3k_dev_deactivate(struct fp_img_dev *dev);
#endif

View File

@@ -1,5 +1,12 @@
/*
* AuthenTec AES4000 driver for libfprint
*
* AES4000 is a press-typed sensor, which captures image in 96x96
* pixels.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
@@ -19,30 +26,20 @@
#define FP_COMPONENT "aes4000"
#include <errno.h>
#include "drivers_api.h"
#include "aeslib.h"
#include "aes3k.h"
#include <glib.h>
#include <libusb.h>
#define DATA_BUFLEN 0x1259
#include <aeslib.h>
#include <fp_internal.h>
/* image size = FRAME_WIDTH x FRAME_WIDTH */
#define FRAME_WIDTH 96
#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
#define ENLARGE_FACTOR 3
#define CTRL_TIMEOUT 1000
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define DATA_BUFLEN 0x1259
#define NR_SUBARRAYS 6
#define SUBARRAY_LEN 768
#define IMG_HEIGHT 96
#define IMG_WIDTH 96
#define ENLARGE_FACTOR 3
struct aes4k_dev {
struct libusb_transfer *img_trf;
};
static const struct aes_regwrite init_reqs[] = {
static struct aes_regwrite init_reqs[] = {
/* master reset */
{ 0x80, 0x01 },
{ 0, 0 },
@@ -117,131 +114,44 @@ static const struct aes_regwrite init_reqs[] = {
{ 0x81, 0x00 },
};
static void do_capture(struct fp_img_dev *dev);
static void img_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct aes4k_dev *aesdev = dev->priv;
unsigned char *ptr = transfer->buffer;
struct fp_img *tmp;
struct fp_img *img;
int i;
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
goto err;
} else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_imgdev_session_error(dev, -EIO);
goto err;
} else if (transfer->length != transfer->actual_length) {
fpi_imgdev_session_error(dev, -EPROTO);
goto err;
}
fpi_imgdev_report_finger_status(dev, TRUE);
tmp = fpi_img_new(IMG_WIDTH * IMG_HEIGHT);
tmp->width = IMG_WIDTH;
tmp->height = IMG_HEIGHT;
tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
for (i = 0; i < NR_SUBARRAYS; i++) {
fp_dbg("subarray header byte %02x", *ptr);
ptr++;
aes_assemble_image(ptr, 96, 16, tmp->data + (i * 96 * 16));
ptr += SUBARRAY_LEN;
}
/* FIXME: this is an ugly hack to make the image big enough for NBIS
* to process reliably */
img = fpi_im_resize(tmp, ENLARGE_FACTOR);
fp_img_free(tmp);
fpi_imgdev_image_captured(dev, img);
/* FIXME: rather than assuming finger has gone, we should poll regs until
* it really has, then restart the capture */
fpi_imgdev_report_finger_status(dev, FALSE);
do_capture(dev);
err:
g_free(transfer->buffer);
aesdev->img_trf = NULL;
libusb_free_transfer(transfer);
}
static void do_capture(struct fp_img_dev *dev)
{
struct aes4k_dev *aesdev = dev->priv;
unsigned char *data;
int r;
aesdev->img_trf = libusb_alloc_transfer(0);
if (!aesdev->img_trf) {
fpi_imgdev_session_error(dev, -EIO);
return;
}
data = g_malloc(DATA_BUFLEN);
libusb_fill_bulk_transfer(aesdev->img_trf, dev->udev, EP_IN, data,
DATA_BUFLEN, img_cb, dev, 0);
r = libusb_submit_transfer(aesdev->img_trf);
if (r < 0) {
g_free(data);
libusb_free_transfer(aesdev->img_trf);
aesdev->img_trf = NULL;
fpi_imgdev_session_error(dev, r);
}
}
static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
{
fpi_imgdev_activate_complete(dev, result);
if (result == 0)
do_capture(dev);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
aes_write_regv(dev, init_reqs, G_N_ELEMENTS(init_reqs), init_reqs_cb, NULL);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes4k_dev *aesdev = dev->priv;
/* FIXME: should wait for cancellation to complete before returning
* from deactivation, otherwise app may legally exit before we've
* cleaned up */
if (aesdev->img_trf)
libusb_cancel_transfer(aesdev->img_trf);
fpi_imgdev_deactivate_complete(dev);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
struct aes3k_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
fp_err("could not claim interface 0");
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = g_malloc0(sizeof(struct aes4k_dev));
aesdev = g_malloc0(sizeof(struct aes3k_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
if (r == 0)
fpi_imgdev_open_complete(dev, 0);
if (!aesdev)
return -ENOMEM;
aesdev->data_buflen = DATA_BUFLEN;
aesdev->frame_width = FRAME_WIDTH;
aesdev->frame_size = FRAME_SIZE;
aesdev->frame_number = FRAME_NUMBER;
aesdev->enlarge_factor = ENLARGE_FACTOR;
aesdev->init_reqs = init_reqs;
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
return r;
}
static void dev_deinit(struct fp_img_dev *dev)
{
g_free(dev->priv);
libusb_release_interface(dev->udev, 0);
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x5501 },
{ 0, 0, 0, },
@@ -249,22 +159,22 @@ static const struct usb_id id_table[] = {
struct fp_img_driver aes4000_driver = {
.driver = {
.id = 3,
.id = AES4000_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES4000",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
.img_height = IMG_HEIGHT * ENLARGE_FACTOR,
.img_width = IMG_WIDTH * ENLARGE_FACTOR,
.img_height = FRAME_WIDTH * ENLARGE_FACTOR,
.img_width = FRAME_WIDTH * ENLARGE_FACTOR,
/* temporarily lowered until image quality improves */
.bz3_threshold = 9,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
.activate = aes3k_dev_activate,
.deactivate = aes3k_dev_deactivate,
};

View File

@@ -19,12 +19,15 @@
#define FP_COMPONENT "aeslib"
#include <errno.h>
#include "fp_internal.h"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <glib.h>
#include "fp_internal.h"
#include "fpi-usb.h"
#include "fpi-assembling.h"
#include "aeslib.h"
#define MAX_REGWRITES_PER_REQUEST 16
@@ -50,12 +53,15 @@ static void write_regv_trf_complete(struct libusb_transfer *transfer)
{
struct write_regv_data *wdata = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
wdata->callback(wdata->imgdev, -EIO, wdata->user_data);
else if (transfer->length != transfer->actual_length)
g_free(wdata);
} else if (transfer->length != transfer->actual_length) {
wdata->callback(wdata->imgdev, -EPROTO, wdata->user_data);
else
g_free(wdata);
} else {
continue_write_regv(wdata);
}
g_free(transfer->buffer);
libusb_free_transfer(transfer);
@@ -70,21 +76,16 @@ static int do_write_regv(struct write_regv_data *wdata, int upper_bound)
unsigned char *data = g_malloc(alloc_size);
unsigned int i;
size_t data_offset = 0;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct libusb_transfer *transfer = fpi_usb_alloc();
int r;
if (!transfer) {
g_free(data);
return -ENOMEM;
}
for (i = offset; i < offset + num; i++) {
const struct aes_regwrite *regwrite = &wdata->regs[i];
data[data_offset++] = regwrite->reg;
data[data_offset++] = regwrite->value;
}
libusb_fill_bulk_transfer(transfer, wdata->imgdev->udev, EP_OUT, data,
libusb_fill_bulk_transfer(transfer, FP_DEV(wdata->imgdev)->udev, EP_OUT, data,
alloc_size, write_regv_trf_complete, wdata, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
@@ -111,6 +112,7 @@ static void continue_write_regv(struct write_regv_data *wdata)
if (offset >= wdata->num_regs) {
fp_dbg("all registers written");
wdata->callback(wdata->imgdev, 0, wdata->user_data);
g_free(wdata);
return;
}
if (wdata->regs[offset].reg)
@@ -134,6 +136,7 @@ static void continue_write_regv(struct write_regv_data *wdata)
r = do_write_regv(wdata, upper_bound);
if (r < 0) {
wdata->callback(wdata->imgdev, r, wdata->user_data);
g_free(wdata);
return;
}
@@ -146,8 +149,10 @@ static void continue_write_regv(struct write_regv_data *wdata)
void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
unsigned int num_regs, aes_write_regv_cb callback, void *user_data)
{
struct write_regv_data *wdata = g_malloc(sizeof(*wdata));
struct write_regv_data *wdata;
fp_dbg("write %d regs", num_regs);
wdata = g_malloc(sizeof(*wdata));
wdata->imgdev = dev;
wdata->num_regs = num_regs;
wdata->regs = regs;
@@ -157,17 +162,16 @@ void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
continue_write_regv(wdata);
}
void aes_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output)
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y)
{
size_t row, column;
unsigned char ret;
for (column = 0; column < width; column++) {
for (row = 0; row < height; row += 2) {
output[width * row + column] = (*input & 0x07) * 36;
output[width * (row + 1) + column] = ((*input & 0x70) >> 4) * 36;
input++;
}
}
ret = frame->data[x * (ctx->frame_height >> 1) + (y >> 1)];
ret = y % 2 ? ret >> 4 : ret & 0xf;
ret *= 17;
return ret;
}

View File

@@ -20,21 +20,26 @@
#ifndef __AESLIB_H__
#define __AESLIB_H__
#include <fp_internal.h>
#include <fprint.h>
struct aes_regwrite {
unsigned char reg;
unsigned char value;
};
struct fpi_frame;
struct fpi_frame_asmbl_ctx;
typedef void (*aes_write_regv_cb)(struct fp_img_dev *dev, int result,
void *user_data);
void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
unsigned int num_regs, aes_write_regv_cb callback, void *user_data);
void aes_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output);
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y);
#endif

620
libfprint/drivers/aesx660.c Normal file
View File

@@ -0,0 +1,620 @@
/*
* AuthenTec AES1660/AES2660 common routines
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007-2008,2012 Vasily Khoruzhick
*
* Based on AES2550 driver
*
* 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 "aesX660"
#include "drivers_api.h"
#include "aeslib.h"
#include "aesx660.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
#define FRAME_HEIGHT AESX660_FRAME_HEIGHT
#define ID_LEN 8
#define INIT_LEN 4
#define CALIBRATE_DATA_LEN 4
#define FINGER_DET_DATA_LEN 4
static void
aesX660_send_cmd_timeout(fpi_ssm *ssm,
struct fp_dev *_dev,
const unsigned char *cmd,
size_t cmd_len,
libusb_transfer_cb_fn callback,
int timeout)
{
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
struct libusb_transfer *transfer = fpi_usb_alloc();
int r;
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT,
(unsigned char *)cmd, cmd_len,
callback, ssm, timeout);
r = libusb_submit_transfer(transfer);
if (r < 0) {
fp_dbg("failed to submit transfer\n");
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, -ENOMEM);
}
}
static void
aesX660_send_cmd(fpi_ssm *ssm,
struct fp_dev *dev,
const unsigned char *cmd,
size_t cmd_len,
libusb_transfer_cb_fn callback)
{
return aesX660_send_cmd_timeout(ssm, dev, cmd, cmd_len, callback, BULK_TIMEOUT);
}
static void
aesX660_read_response(fpi_ssm *ssm,
struct fp_dev *_dev,
size_t buf_len,
libusb_transfer_cb_fn callback)
{
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
int r;
data = g_malloc(buf_len);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN,
data, buf_len,
callback, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
fp_dbg("Failed to submit rx transfer: %d\n", r);
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
static void aesX660_send_cmd_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fp_dbg("tx transfer status: %d, actual_len: %.4x\n",
transfer->status, transfer->actual_length);
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void aesX660_read_calibrate_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
unsigned char *data = transfer->buffer;
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
/* Calibrate response was read correctly? */
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_CALIBRATE_RESPONSE) {
fp_dbg("Bogus calibrate response: %.2x\n", data[0]);
fpi_ssm_mark_failed(ssm, -EPROTO);
goto out;
}
fpi_ssm_next_state(ssm);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
/****** FINGER PRESENCE DETECTION ******/
enum finger_det_states {
FINGER_DET_SEND_LED_CMD,
FINGER_DET_SEND_FD_CMD,
FINGER_DET_READ_FD_DATA,
FINGER_DET_SET_IDLE,
FINGER_DET_NUM_STATES,
};
static void finger_det_read_fd_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = transfer->buffer;
aesdev->fd_data_transfer = NULL;
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
fp_dbg("Cancelling transfer...\n");
fpi_ssm_next_state(ssm);
goto out;
}
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fp_dbg("Failed to read FD data\n");
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_FINGER_DET_RESPONSE) {
fp_dbg("Bogus FD response: %.2x\n", data[0]);
fpi_ssm_mark_failed(ssm, -EPROTO);
goto out;
}
if (data[AESX660_FINGER_PRESENT_OFFSET] == AESX660_FINGER_PRESENT || aesdev->deactivating) {
/* Finger present or we're deactivating... */
fpi_ssm_next_state(ssm);
} else {
fp_dbg("Wait for finger returned %.2x as result\n",
data[AESX660_FINGER_PRESENT_OFFSET]);
fpi_ssm_jump_to_state(ssm, FINGER_DET_SEND_FD_CMD);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_set_idle_cmd_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void finger_det_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(_dev);
int err = fpi_ssm_get_error(ssm);
fp_dbg("Finger detection completed");
fpi_imgdev_report_finger_status(dev, TRUE);
fpi_ssm_free(ssm);
if (aesdev->deactivating)
complete_deactivation(dev);
else if (err)
fpi_imgdev_session_error(dev, err);
else {
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
}
}
static void finger_det_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
switch (fpi_ssm_get_cur_state(ssm)) {
case FINGER_DET_SEND_LED_CMD:
aesX660_send_cmd(ssm, dev, led_blink_cmd, sizeof(led_blink_cmd),
aesX660_send_cmd_cb);
break;
case FINGER_DET_SEND_FD_CMD:
aesX660_send_cmd_timeout(ssm, dev, wait_for_finger_cmd, sizeof(wait_for_finger_cmd),
aesX660_send_cmd_cb, 0);
break;
case FINGER_DET_READ_FD_DATA:
aesX660_read_response(ssm, dev, FINGER_DET_DATA_LEN, finger_det_read_fd_data_cb);
break;
case FINGER_DET_SET_IDLE:
aesX660_send_cmd(ssm, dev, set_idle_cmd, sizeof(set_idle_cmd),
finger_det_set_idle_cmd_cb);
break;
}
}
static void start_finger_detection(struct fp_img_dev *dev)
{
fpi_ssm *ssm;
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(FP_DEV(dev), finger_det_run_state, FINGER_DET_NUM_STATES, dev);
fpi_ssm_start(ssm, finger_det_sm_complete);
}
/****** CAPTURE ******/
enum capture_states {
CAPTURE_SEND_LED_CMD,
CAPTURE_SEND_CAPTURE_CMD,
CAPTURE_READ_STRIPE_DATA,
CAPTURE_SET_IDLE,
CAPTURE_NUM_STATES,
};
/* Returns number of processed bytes */
static int process_stripe_data(fpi_ssm *ssm, struct fp_img_dev *dev, unsigned char *data)
{
struct fpi_frame *stripe;
unsigned char *stripdata;
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
stripe = g_malloc(aesdev->assembling_ctx->frame_width * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame)); /* 4 bpp */
stripdata = stripe->data;
fp_dbg("Processing frame %.2x %.2x", data[AESX660_IMAGE_OK_OFFSET],
data[AESX660_LAST_FRAME_OFFSET]);
stripe->delta_x = (int8_t)data[AESX660_FRAME_DELTA_X_OFFSET];
stripe->delta_y = -(int8_t)data[AESX660_FRAME_DELTA_Y_OFFSET];
fp_dbg("Offset to previous frame: %d %d", stripe->delta_x, stripe->delta_y);
if (data[AESX660_IMAGE_OK_OFFSET] == AESX660_IMAGE_OK) {
memcpy(stripdata, data + AESX660_IMAGE_OFFSET, aesdev->assembling_ctx->frame_width * FRAME_HEIGHT / 2);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
return (data[AESX660_LAST_FRAME_OFFSET] & AESX660_LAST_FRAME_BIT);
}
g_free(stripe);
return 0;
}
static void capture_set_idle_cmd_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = fpi_assemble_frames(aesdev->assembling_ctx, aesdev->strips, aesdev->strips_len);
img->flags |= aesdev->extra_img_flags;
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_read_stripe_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = transfer->buffer;
int finger_missing = 0;
size_t copied, actual_len = transfer->actual_length;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
fp_dbg("Got %lu bytes of data", actual_len);
do {
copied = MIN(aesdev->buffer_max - aesdev->buffer_size, actual_len);
memcpy(aesdev->buffer + aesdev->buffer_size,
data,
copied);
actual_len -= copied;
data += copied;
aesdev->buffer_size += copied;
fp_dbg("Copied %.4lx bytes into internal buffer",
copied);
if (aesdev->buffer_size == aesdev->buffer_max) {
if (aesdev->buffer_max == AESX660_HEADER_SIZE) {
aesdev->buffer_max = aesdev->buffer[AESX660_RESPONSE_SIZE_LSB_OFFSET] +
(aesdev->buffer[AESX660_RESPONSE_SIZE_MSB_OFFSET] << 8) + AESX660_HEADER_SIZE;
fp_dbg("Got frame, type %.2x size %.4lx",
aesdev->buffer[AESX660_RESPONSE_TYPE_OFFSET],
aesdev->buffer_max);
continue;
} else {
finger_missing |= process_stripe_data(ssm, dev, aesdev->buffer);
aesdev->buffer_max = AESX660_HEADER_SIZE;
aesdev->buffer_size = 0;
}
}
} while (actual_len);
fp_dbg("finger %s\n", finger_missing ? "missing" : "present");
if (finger_missing) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_STRIPE_DATA);
}
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_SEND_LED_CMD:
aesX660_send_cmd(ssm, _dev, led_solid_cmd, sizeof(led_solid_cmd),
aesX660_send_cmd_cb);
break;
case CAPTURE_SEND_CAPTURE_CMD:
aesdev->buffer_size = 0;
aesdev->buffer_max = AESX660_HEADER_SIZE;
aesX660_send_cmd(ssm, _dev, aesdev->start_imaging_cmd,
aesdev->start_imaging_cmd_len,
aesX660_send_cmd_cb);
break;
case CAPTURE_READ_STRIPE_DATA:
aesX660_read_response(ssm, _dev, AESX660_BULK_TRANSFER_SIZE,
capture_read_stripe_data_cb);
break;
case CAPTURE_SET_IDLE:
fp_dbg("Got %lu frames\n", aesdev->strips_len);
aesX660_send_cmd(ssm, _dev, set_idle_cmd, sizeof(set_idle_cmd),
capture_set_idle_cmd_cb);
break;
}
}
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(_dev);
int err = fpi_ssm_get_error(ssm);
fp_dbg("Capture completed");
fpi_ssm_free(ssm);
if (aesdev->deactivating)
complete_deactivation(dev);
else if (err)
fpi_imgdev_session_error(dev, err);
else
start_finger_detection(dev);
}
static void start_capture(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
G_DEBUG_HERE();
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
enum activate_states {
ACTIVATE_SET_IDLE,
ACTIVATE_SEND_READ_ID_CMD,
ACTIVATE_READ_ID,
ACTIVATE_SEND_CALIBRATE_CMD,
ACTIVATE_READ_CALIBRATE_DATA,
ACTIVATE_SEND_INIT_CMD,
ACTIVATE_READ_INIT_RESPONSE,
ACTIVATE_NUM_STATES,
};
static void activate_read_id_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = transfer->buffer;
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fp_dbg("read_id cmd failed\n");
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
/* ID was read correctly */
if (data[0] == 0x07) {
fp_dbg("Sensor device id: %.2x%2x, bcdDevice: %.2x.%.2x, init status: %.2x\n",
data[4], data[3], data[5], data[6], data[7]);
} else {
fp_dbg("Bogus read ID response: %.2x\n", data[AESX660_RESPONSE_TYPE_OFFSET]);
fpi_ssm_mark_failed(ssm, -EPROTO);
goto out;
}
switch (aesdev->init_seq_idx) {
case 0:
aesdev->init_seq = aesdev->init_seqs[0];
aesdev->init_seq_len = aesdev->init_seqs_len[0];
aesdev->init_seq_idx = 1;
aesdev->init_cmd_idx = 0;
/* Do calibration only after 1st init sequence */
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_INIT_CMD);
break;
case 1:
aesdev->init_seq = aesdev->init_seqs[1];
aesdev->init_seq_len = aesdev->init_seqs_len[1];
aesdev->init_seq_idx = 2;
aesdev->init_cmd_idx = 0;
fpi_ssm_next_state(ssm);
break;
default:
fp_dbg("Failed to init device! init status: %.2x\n", data[7]);
fpi_ssm_mark_failed(ssm, -EPROTO);
break;
}
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_read_init_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = transfer->buffer;
fp_dbg("read_init_cb\n");
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fp_dbg("read_init transfer status: %d, actual_len: %d\n", transfer->status, transfer->actual_length);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
/* ID was read correctly */
if (data[0] != 0x42 || data[3] != 0x01) {
fp_dbg("Bogus read init response: %.2x %.2x\n", data[0],
data[3]);
fpi_ssm_mark_failed(ssm, -EPROTO);
goto out;
}
aesdev->init_cmd_idx++;
if (aesdev->init_cmd_idx == aesdev->init_seq_len) {
if (aesdev->init_seq_idx < 2)
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_READ_ID_CMD);
else
fpi_ssm_mark_completed(ssm);
goto out;
}
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_INIT_CMD);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
switch (fpi_ssm_get_cur_state(ssm)) {
case ACTIVATE_SET_IDLE:
aesdev->init_seq_idx = 0;
fp_dbg("Activate: set idle\n");
aesX660_send_cmd(ssm, _dev, set_idle_cmd, sizeof(set_idle_cmd),
aesX660_send_cmd_cb);
break;
case ACTIVATE_SEND_READ_ID_CMD:
fp_dbg("Activate: read ID\n");
aesX660_send_cmd(ssm, _dev, read_id_cmd, sizeof(read_id_cmd),
aesX660_send_cmd_cb);
break;
case ACTIVATE_READ_ID:
aesX660_read_response(ssm, _dev, ID_LEN, activate_read_id_cb);
break;
case ACTIVATE_SEND_INIT_CMD:
fp_dbg("Activate: send init seq #%d cmd #%d\n",
aesdev->init_seq_idx,
aesdev->init_cmd_idx);
aesX660_send_cmd(ssm, _dev,
aesdev->init_seq[aesdev->init_cmd_idx].cmd,
aesdev->init_seq[aesdev->init_cmd_idx].len,
aesX660_send_cmd_cb);
break;
case ACTIVATE_READ_INIT_RESPONSE:
fp_dbg("Activate: read init response\n");
aesX660_read_response(ssm, _dev, INIT_LEN, activate_read_init_cb);
break;
case ACTIVATE_SEND_CALIBRATE_CMD:
aesX660_send_cmd(ssm, _dev, calibrate_cmd, sizeof(calibrate_cmd),
aesX660_send_cmd_cb);
break;
case ACTIVATE_READ_CALIBRATE_DATA:
aesX660_read_response(ssm, _dev, CALIBRATE_DATA_LEN, aesX660_read_calibrate_data_cb);
break;
}
}
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
int err = fpi_ssm_get_error(ssm);
fp_dbg("status %d", err);
fpi_imgdev_activate_complete(dev, err);
fpi_ssm_free(ssm);
if (!err)
start_finger_detection(dev);
}
int aesX660_dev_activate(struct fp_img_dev *dev)
{
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
void aesX660_dev_deactivate(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (aesdev->fd_data_transfer)
libusb_cancel_transfer(aesdev->fd_data_transfer);
aesdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_deactivate_complete(dev);
}

122
libfprint/drivers/aesx660.h Normal file
View File

@@ -0,0 +1,122 @@
/*
* AuthenTec AES1660/AES2660 common definitions
* Copyright (c) 2012 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
*/
#ifndef __AESX660_H
#define __AESX660_H
#define AESX660_HEADER_SIZE 3
#define AESX660_RESPONSE_TYPE_OFFSET 0x00
#define AESX660_RESPONSE_SIZE_LSB_OFFSET 0x01
#define AESX660_RESPONSE_SIZE_MSB_OFFSET 0x02
#define AESX660_CALIBRATE_RESPONSE 0x06
#define AESX660_FINGER_DET_RESPONSE 0x40
#define AESX660_FINGER_PRESENT_OFFSET 0x03
#define AESX660_FINGER_PRESENT 0x01
#define AESX660_IMAGE_OK_OFFSET 0x03
#define AESX660_IMAGE_OK 0x0d
#define AESX660_LAST_FRAME_OFFSET 0x04
#define AESX660_LAST_FRAME_BIT 0x01
#define AESX660_FRAME_DELTA_X_OFFSET 16
#define AESX660_FRAME_DELTA_Y_OFFSET 17
#define AESX660_IMAGE_OFFSET 43
#define AESX660_BULK_TRANSFER_SIZE 4096
#define AESX660_FRAME_HEIGHT 8
struct aesX660_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
struct aesX660_cmd *init_seq;
size_t init_seq_len;
unsigned int init_cmd_idx;
unsigned int init_seq_idx;
struct libusb_transfer *fd_data_transfer;
unsigned char *buffer;
size_t buffer_size;
size_t buffer_max;
/* Device-specific stuff */
struct aesX660_cmd *init_seqs[2];
size_t init_seqs_len[2];
unsigned char *start_imaging_cmd;
size_t start_imaging_cmd_len;
struct fpi_frame_asmbl_ctx *assembling_ctx;
uint16_t extra_img_flags;
};
struct aesX660_cmd {
const unsigned char *cmd;
size_t len;
};
/* 0x77 cmd seems to control LED, this sequence
* makes LED blink
*/
static const unsigned char led_blink_cmd[] = {
0x77, 0x18, 0x00,
0x00, 0x3f, 0x00, 0xff, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0xf3, 0x01, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xf3,
0x01, 0x00, 0x7f
};
/* This sequence makes LED light solid
*/
static const unsigned char led_solid_cmd[] = {
0x77, 0x18, 0x00, 0x00, 0x3f, 0x00, 0xff, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0xe7, 0x03, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f
};
static const unsigned char wait_for_finger_cmd[] = {
0x20,
0x40, 0x04, 0x00, 0x02, 0x1e, 0x00, 0x32
};
/* 0x40 cmd response
*
static const unsigned char pkt1371[] = {
0x40, 0x01, 0x00, 0x01
};
*/
static const unsigned char set_idle_cmd[] = {
0x0d, /* Reset or "set idle"? */
};
static const unsigned char read_id_cmd[] = {
0x44, 0x02, 0x00, 0x08, 0x00, /* Max transfer size is 8 */
0x07, /* Read ID? */
};
static const unsigned char calibrate_cmd[] = {
0x44, 0x02, 0x00, 0x04, 0x00,
0x06,
};
int aesX660_dev_activate(struct fp_img_dev *dev);
void aesX660_dev_deactivate(struct fp_img_dev *dev);
#endif

View File

@@ -0,0 +1,47 @@
/*
* Driver IDs
* Copyright (C) 2012 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
*/
#ifndef __DRIVER_IDS
#define __DRIVER_IDS
enum {
UPEKTS_ID = 1,
URU4000_ID = 2,
AES4000_ID = 3,
AES2501_ID = 4,
UPEKTC_ID = 5,
AES1610_ID = 6,
/* FDU2000_ID = 7, */
VCOM5S_ID = 8,
UPEKSONLY_ID = 9,
VFS101_ID = 10,
VFS301_ID = 11,
AES2550_ID = 12,
/* UPEKE2_ID = 13 */
AES1660_ID = 14,
AES2660_ID = 15,
AES3500_ID = 16,
UPEKTC_IMG_ID = 17,
ETES603_ID = 18,
VFS5011_ID = 19,
VFS0050_ID = 20,
ELAN_ID = 21,
};
#endif

988
libfprint/drivers/elan.c Normal file
View File

@@ -0,0 +1,988 @@
/*
* Elan driver for libfprint
*
* Copyright (C) 2017 Igor Filatov <ia.filatov@gmail.com>
* Copyright (C) 2018 Sébastien Béchet <sebastien.bechet@osinix.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
*/
/*
* The algorithm which libfprint uses to match fingerprints doesn't like small
* images like the ones these drivers produce. There's just not enough minutiae
* (recognizable print-specific points) on them for a reliable match. This means
* that unless another matching algo is found/implemented, these readers will
* not work as good with libfprint as they do with vendor drivers.
*
* To get bigger images the driver expects you to swipe the finger over the
* reader. This works quite well for readers with a rectangular 144x64 sensor.
* Worse than real swipe readers but good enough for day-to-day use. It needs
* a steady and relatively slow swipe. There are also square 96x96 sensors and
* I don't know whether they are in fact usable or not because I don't have one.
* I imagine they'd be less reliable because the resulting image is even
* smaller. If they can't be made usable with libfprint, I might end up dropping
* them because it's better than saying they work when they don't.
*/
#define FP_COMPONENT "elan"
#include "drivers_api.h"
#include "elan.h"
#define dbg_buf(buf, len) \
if (len == 1) \
fp_dbg("%02x", buf[0]); \
else if (len == 2) \
fp_dbg("%04x", buf[0] << 8 | buf[1]); \
else if (len > 2) \
fp_dbg("%04x... (%d bytes)", buf[0] << 8 | buf[1], len)
unsigned char elan_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame, unsigned int x,
unsigned int y)
{
return frame->data[x + y * ctx->frame_width];
}
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = 0,
.frame_height = 0,
.image_width = 0,
.get_pixel = elan_get_pixel,
};
struct elan_dev {
/* device config */
unsigned short dev_type;
unsigned short fw_ver;
void (*process_frame) (unsigned short *raw_frame, GSList ** frames);
/* end device config */
/* commands */
const struct elan_cmd *cmd;
int cmd_timeout;
fpi_usb_transfer *cur_transfer;
/* end commands */
/* state */
enum fp_imgdev_state dev_state;
enum fp_imgdev_state dev_state_next;
unsigned char *last_read;
unsigned char calib_atts_left;
unsigned char calib_status;
unsigned short *background;
unsigned char frame_width;
unsigned char frame_height;
unsigned char raw_frame_height;
int num_frames;
GSList *frames;
/* end state */
};
int cmp_short(const void *a, const void *b)
{
return (int)(*(short *)a - *(short *)b);
}
static void elan_dev_reset(struct elan_dev *elandev)
{
G_DEBUG_HERE();
BUG_ON(elandev->cur_transfer);
elandev->cmd = NULL;
elandev->cmd_timeout = ELAN_CMD_TIMEOUT;
elandev->calib_status = 0;
g_free(elandev->last_read);
elandev->last_read = NULL;
g_slist_free_full(elandev->frames, g_free);
elandev->frames = NULL;
elandev->num_frames = 0;
}
static void elan_save_frame(struct elan_dev *elandev, unsigned short *frame)
{
G_DEBUG_HERE();
/* so far 3 types of readers by sensor dimensions and orientation have been
* seen in the wild:
* 1. 144x64. Raw images are in portrait orientation while readers themselves
* are placed (e.g. built into a touchpad) in landscape orientation. These
* need to be rotated before assembling.
* 2. 96x96 rotated. Like the first type but square. Likewise, need to be
* rotated before assembling.
* 3. 96x96 normal. Square and need NOT be rotated. So far there's only been
* 1 report of a 0c03 of this type. Hopefully this type can be identified
* by device id (and manufacturers don't just install the readers as they
* please).
* we also discard stripes of 'frame_margin' from bottom and top because
* assembling works bad for tall frames */
unsigned char frame_width = elandev->frame_width;
unsigned char frame_height = elandev->frame_height;
unsigned char raw_height = elandev->raw_frame_height;
unsigned char frame_margin = (raw_height - elandev->frame_height) / 2;
int frame_idx, raw_idx;
for (int y = 0; y < frame_height; y++)
for (int x = 0; x < frame_width; x++) {
if (elandev->dev_type & ELAN_NOT_ROTATED)
raw_idx = x + (y + frame_margin) * frame_width;
else
raw_idx = frame_margin + y + x * raw_height;
frame_idx = x + y * frame_width;
frame[frame_idx] =
((unsigned short *)elandev->last_read)[raw_idx];
}
}
static void elan_save_background(struct elan_dev *elandev)
{
G_DEBUG_HERE();
g_free(elandev->background);
elandev->background =
g_malloc(elandev->frame_width * elandev->frame_height *
sizeof(short));
elan_save_frame(elandev, elandev->background);
}
/* save a frame as part of the fingerprint image
* background needs to have been captured for this routine to work
* Elantech recommends 2-step non-linear normalization in order to reduce
* 2^14 ADC resolution to 2^8 image:
*
* 1. background is subtracted (done here)
*
* 2. pixels are grouped in 3 groups by intensity and each group is mapped
* separately onto the normalized frame (done in elan_process_frame_*)
* ==== 16383 ____> ======== 255
* /
* ----- lvl3 __/
* 35% pixels
*
* ----- lvl2 --------> ======== 156
*
* 30% pixels
* ----- lvl1 --------> ======== 99
*
* 35% pixels
* ----- lvl0 __
* \
* ======== 0 \____> ======== 0
*
* For some devices we don't do 2. but instead do a simple linear mapping
* because it seems to produce better results (or at least as good):
* ==== 16383 ___> ======== 255
* /
* ------ max __/
*
*
* ------ min __
* \
* ======== 0 \___> ======== 0
*/
static int elan_save_img_frame(struct elan_dev *elandev)
{
G_DEBUG_HERE();
unsigned int frame_size = elandev->frame_width * elandev->frame_height;
unsigned short *frame = g_malloc(frame_size * sizeof(short));
elan_save_frame(elandev, frame);
unsigned int sum = 0;
for (int i = 0; i < frame_size; i++) {
if (elandev->background[i] > frame[i])
frame[i] = 0;
else
frame[i] -= elandev->background[i];
sum += frame[i];
}
if (sum == 0) {
fp_dbg
("frame darker than background; finger present during calibration?");
return -1;
}
elandev->frames = g_slist_prepend(elandev->frames, frame);
elandev->num_frames += 1;
return 0;
}
static void elan_process_frame_linear(unsigned short *raw_frame,
GSList ** frames)
{
unsigned int frame_size =
assembling_ctx.frame_width * assembling_ctx.frame_height;
struct fpi_frame *frame =
g_malloc(frame_size + sizeof(struct fpi_frame));
G_DEBUG_HERE();
unsigned short min = 0xffff, max = 0;
for (int i = 0; i < frame_size; i++) {
if (raw_frame[i] < min)
min = raw_frame[i];
if (raw_frame[i] > max)
max = raw_frame[i];
}
g_assert(max != min);
unsigned short px;
for (int i = 0; i < frame_size; i++) {
px = raw_frame[i];
px = (px - min) * 0xff / (max - min);
frame->data[i] = (unsigned char)px;
}
*frames = g_slist_prepend(*frames, frame);
}
static void elan_process_frame_thirds(unsigned short *raw_frame,
GSList ** frames)
{
G_DEBUG_HERE();
unsigned int frame_size =
assembling_ctx.frame_width * assembling_ctx.frame_height;
struct fpi_frame *frame =
g_malloc(frame_size + sizeof(struct fpi_frame));
unsigned short lvl0, lvl1, lvl2, lvl3;
unsigned short *sorted = g_malloc(frame_size * sizeof(short));
memcpy(sorted, raw_frame, frame_size * sizeof(short));
qsort(sorted, frame_size, sizeof(short), cmp_short);
lvl0 = sorted[0];
lvl1 = sorted[frame_size * 3 / 10];
lvl2 = sorted[frame_size * 65 / 100];
lvl3 = sorted[frame_size - 1];
g_free(sorted);
unsigned short px;
for (int i = 0; i < frame_size; i++) {
px = raw_frame[i];
if (lvl0 <= px && px < lvl1)
px = (px - lvl0) * 99 / (lvl1 - lvl0);
else if (lvl1 <= px && px < lvl2)
px = 99 + ((px - lvl1) * 56 / (lvl2 - lvl1));
else // (lvl2 <= px && px <= lvl3)
px = 155 + ((px - lvl2) * 100 / (lvl3 - lvl2));
frame->data[i] = (unsigned char)px;
}
*frames = g_slist_prepend(*frames, frame);
}
static void elan_submit_image(struct fp_img_dev *dev)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
int num_frames;
GSList *raw_frames;
GSList *frames = NULL;
struct fp_img *img;
G_DEBUG_HERE();
num_frames = elandev->num_frames - ELAN_SKIP_LAST_FRAMES;
raw_frames = g_slist_nth(elandev->frames, ELAN_SKIP_LAST_FRAMES);
assembling_ctx.frame_width = elandev->frame_width;
assembling_ctx.frame_height = elandev->frame_height;
assembling_ctx.image_width = elandev->frame_width * 3 / 2;
g_slist_foreach(raw_frames, (GFunc) elandev->process_frame, &frames);
fpi_do_movement_estimation(&assembling_ctx, frames, num_frames);
img = fpi_assemble_frames(&assembling_ctx, frames, num_frames);
img->flags |= FP_IMG_PARTIAL;
fpi_imgdev_image_captured(dev, img);
}
static void elan_cmd_done(fpi_ssm *ssm)
{
G_DEBUG_HERE();
fpi_ssm_next_state(ssm);
}
static void elan_cmd_cb(struct libusb_transfer *transfer,
struct fp_dev *_dev,
fpi_ssm *ssm,
void *user_data)
{
struct fp_img_dev *dev;
struct elan_dev *elandev;
G_DEBUG_HERE();
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
fp_dbg("transfer cancelled");
return;
}
dev = FP_IMG_DEV(_dev);
elandev = FP_INSTANCE_DATA(_dev);
elandev->cur_transfer = NULL;
switch (transfer->status) {
case LIBUSB_TRANSFER_COMPLETED:
if (transfer->length != transfer->actual_length) {
fp_dbg("transfer length error: expected %d, got %d",
transfer->length, transfer->actual_length);
elan_dev_reset(elandev);
fpi_ssm_mark_failed(ssm, -EPROTO);
} else if (transfer->endpoint & LIBUSB_ENDPOINT_IN) {
/* just finished receiving */
elandev->last_read = g_memdup(transfer->buffer, transfer->actual_length);
dbg_buf(transfer->buffer, transfer->actual_length);
elan_cmd_done(ssm);
} else {
/* just finished sending */
G_DEBUG_HERE();
elan_cmd_read(ssm, dev);
}
break;
case LIBUSB_TRANSFER_TIMED_OUT:
fp_dbg("transfer timed out");
fpi_ssm_mark_failed(ssm, -ETIMEDOUT);
break;
default:
fp_dbg("transfer failed: %d", transfer->status);
elan_dev_reset(elandev);
fpi_ssm_mark_failed(ssm, -EIO);
}
}
static void elan_cmd_read(fpi_ssm *ssm, struct fp_img_dev *dev)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
int response_len = elandev->cmd->response_len;
unsigned char *buffer;
G_DEBUG_HERE();
if (elandev->cmd->response_len == ELAN_CMD_SKIP_READ) {
fp_dbg("skipping read, not expecting anything");
elan_cmd_done(ssm);
return;
}
if (elandev->dev_type == ELAN_0C42) {
/* ELAN_0C42 sends an extra byte in one byte responses */
if (elandev->cmd->response_len == 1)
response_len = 2;
}
if (elandev->cmd->cmd == get_image_cmd.cmd)
/* raw data has 2-byte "pixels" and the frame is vertical */
response_len =
elandev->raw_frame_height * elandev->frame_width * 2;
g_clear_pointer(&elandev->last_read, g_free);
buffer = g_malloc(response_len);
elandev->cur_transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
ssm,
elandev->cmd->response_in,
buffer,
response_len,
elan_cmd_cb,
NULL,
elandev->cmd_timeout);
int r = fpi_usb_submit_transfer(elandev->cur_transfer);
if (r < 0)
fpi_ssm_mark_failed(ssm, r);
}
static void
elan_run_cmd(fpi_ssm *ssm,
struct fp_img_dev *dev,
const struct elan_cmd *cmd,
int cmd_timeout)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
dbg_buf(cmd->cmd, 2);
elandev->cmd = cmd;
if (cmd_timeout != -1)
elandev->cmd_timeout = cmd_timeout;
if (cmd->devices != ELAN_ALL_DEV && !(cmd->devices & elandev->dev_type)) {
fp_dbg("skipping command 0x%x 0x%x for this device (for devices 0x%x but device is 0x%x)",
cmd->cmd[0], cmd->cmd[1], cmd->devices, elandev->dev_type);
elan_cmd_done(ssm);
return;
}
elandev->cur_transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
ssm,
ELAN_EP_CMD_OUT,
g_memdup((char *) cmd->cmd, ELAN_CMD_LEN),
ELAN_CMD_LEN,
elan_cmd_cb,
NULL,
elandev->cmd_timeout);
int r = fpi_usb_submit_transfer(elandev->cur_transfer);
if (r < 0)
fpi_ssm_mark_failed(ssm, r);
}
enum stop_capture_states {
STOP_CAPTURE,
STOP_CAPTURE_NUM_STATES,
};
static void stop_capture_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
G_DEBUG_HERE();
switch (fpi_ssm_get_cur_state(ssm)) {
case STOP_CAPTURE:
elan_run_cmd(ssm, FP_IMG_DEV(dev), &stop_cmd, ELAN_CMD_TIMEOUT);
break;
}
}
static void stop_capture_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
int error = fpi_ssm_get_error(ssm);
G_DEBUG_HERE();
fpi_ssm_free(ssm);
if (!error) {
fpi_imgdev_report_finger_status(dev, FALSE);
/* If verify or identify fails because of short swipe, we need to restart
* capture manually. It feels like libfprint or the application should know
* better if they want to retry, but they don't. Unless we've been asked to
* deactivate, try to re-enter the capture loop. Since state change is
* async, there's still a chance to be deactivated by another pending
* event. */
if (elandev->dev_state_next != IMGDEV_STATE_INACTIVE)
dev_change_state(dev, IMGDEV_STATE_AWAIT_FINGER_ON);
} else if (error != -ECANCELED)
fpi_imgdev_abort_scan(dev, error);
}
static void elan_stop_capture(struct fp_img_dev *dev)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
elan_dev_reset(elandev);
fpi_ssm *ssm =
fpi_ssm_new(FP_DEV(dev), stop_capture_run_state,
STOP_CAPTURE_NUM_STATES, dev);
fpi_ssm_start(ssm, stop_capture_complete);
}
enum capture_states {
CAPTURE_LED_ON,
CAPTURE_WAIT_FINGER,
CAPTURE_READ_DATA,
CAPTURE_CHECK_ENOUGH_FRAMES,
CAPTURE_NUM_STATES,
};
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_LED_ON:
elan_run_cmd(ssm, dev, &led_on_cmd, ELAN_CMD_TIMEOUT);
break;
case CAPTURE_WAIT_FINGER:
elan_run_cmd(ssm, dev, &pre_scan_cmd, -1);
break;
case CAPTURE_READ_DATA:
/* 0x55 - finger present
* 0xff - device not calibrated (probably) */
if (elandev->last_read && elandev->last_read[0] == 0x55) {
if (elandev->dev_state == IMGDEV_STATE_AWAIT_FINGER_ON)
fpi_imgdev_report_finger_status(dev, TRUE);
elan_run_cmd(ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT);
} else
fpi_ssm_mark_failed(ssm, -EBADMSG);
break;
case CAPTURE_CHECK_ENOUGH_FRAMES:
r = elan_save_img_frame(elandev);
if (r < 0)
fpi_ssm_mark_failed(ssm, r);
else if (elandev->num_frames < ELAN_MAX_FRAMES) {
/* quickly stop if finger is removed */
elandev->cmd_timeout = ELAN_FINGER_TIMEOUT;
fpi_ssm_jump_to_state(ssm, CAPTURE_WAIT_FINGER);
} else {
fpi_ssm_next_state(ssm);
}
break;
}
}
static void capture_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
G_DEBUG_HERE();
if (fpi_ssm_get_error(ssm) == -ECANCELED) {
fpi_ssm_free(ssm);
return;
}
/* either max frames captured or timed out waiting for the next frame */
if (!fpi_ssm_get_error(ssm)
|| (fpi_ssm_get_error(ssm) == -ETIMEDOUT
&& fpi_ssm_get_cur_state(ssm) == CAPTURE_WAIT_FINGER))
if (elandev->num_frames >= ELAN_MIN_FRAMES)
elan_submit_image(dev);
else {
fp_dbg("swipe too short: want >= %d frames, got %d",
ELAN_MIN_FRAMES, elandev->num_frames);
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_TOO_SHORT);
}
/* other error
* It says "...abort_scan" but reporting 1 during verification makes it
* successful! */
else
fpi_imgdev_abort_scan(dev, fpi_ssm_get_error(ssm));
fpi_ssm_free(ssm);
}
static void elan_capture(struct fp_img_dev *dev)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
elan_dev_reset(elandev);
fpi_ssm *ssm =
fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
fpi_ssm_start(ssm, capture_complete);
}
/* this function needs to have elandev->background and elandev->last_read to be
* the calibration mean */
static int elan_need_calibration(struct elan_dev *elandev)
{
G_DEBUG_HERE();
unsigned short calib_mean =
elandev->last_read[0] * 0xff + elandev->last_read[1];
unsigned int bg_mean = 0, delta;
unsigned int frame_size = elandev->frame_width * elandev->frame_height;
g_assert(frame_size != 0);
if (elandev->dev_type == ELAN_0C42) {
if (calib_mean > 5500 ||
calib_mean < 2500) {
fp_dbg("Forcing needed recalibration");
return 1;
}
}
for (int i = 0; i < frame_size; i++)
bg_mean += elandev->background[i];
bg_mean /= frame_size;
delta =
bg_mean > calib_mean ? bg_mean - calib_mean : calib_mean - bg_mean;
fp_dbg("calibration mean: %d, bg mean: %d, delta: %d", calib_mean,
bg_mean, delta);
return delta > ELAN_CALIBRATION_MAX_DELTA ? 1 : 0;
}
enum calibrate_states {
CALIBRATE_GET_BACKGROUND,
CALIBRATE_SAVE_BACKGROUND,
CALIBRATE_GET_MEAN,
CALIBRATE_CHECK_NEEDED,
CALIBRATE_GET_STATUS,
CALIBRATE_CHECK_STATUS,
CALIBRATE_REPEAT_STATUS,
CALIBRATE_NUM_STATES,
};
static gboolean elan_supports_calibration(struct elan_dev *elandev)
{
if (elandev->dev_type == ELAN_0C42)
return TRUE;
return elandev->fw_ver >= ELAN_MIN_CALIBRATION_FW;
}
static void calibrate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
G_DEBUG_HERE();
switch (fpi_ssm_get_cur_state(ssm)) {
case CALIBRATE_GET_BACKGROUND:
elan_run_cmd(ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT);
break;
case CALIBRATE_SAVE_BACKGROUND:
elan_save_background(elandev);
if (!elan_supports_calibration(elandev)) {
fp_dbg("FW does not support calibration");
fpi_ssm_mark_completed(ssm);
} else
fpi_ssm_next_state(ssm);
break;
case CALIBRATE_GET_MEAN:
elan_run_cmd(ssm, dev, &get_calib_mean_cmd, ELAN_CMD_TIMEOUT);
break;
case CALIBRATE_CHECK_NEEDED:
if (elan_need_calibration(elandev)) {
elandev->calib_status = 0;
fpi_ssm_next_state(ssm);
} else
fpi_ssm_mark_completed(ssm);
break;
case CALIBRATE_GET_STATUS:
elandev->calib_atts_left -= 1;
if (elandev->calib_atts_left)
elan_run_cmd(ssm, dev, &get_calib_status_cmd,
ELAN_CMD_TIMEOUT);
else {
fp_dbg("calibration failed");
fpi_ssm_mark_failed(ssm, -1);
}
break;
case CALIBRATE_CHECK_STATUS:
/* 0x01 - retry, 0x03 - ok
* It appears that when reading the response soon after 0x4023 the device
* can return 0x03, and only after some time (up to 100 ms) the response
* changes to 0x01. It stays that way for some time and then changes back
* to 0x03. Because of this we don't just expect 0x03, we want to see 0x01
* first. This is to make sure that a full calibration loop has completed */
fp_dbg("calibration status: 0x%02x", elandev->last_read[0]);
if (elandev->calib_status == 0x01
&& elandev->last_read[0] == 0x03) {
elandev->calib_status = 0x03;
fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_BACKGROUND);
} else {
fpi_timeout *timeout;
if (elandev->calib_status == 0x00
&& elandev->last_read[0] == 0x01)
elandev->calib_status = 0x01;
timeout = fpi_timeout_add(50, fpi_ssm_next_state_timeout_cb, _dev, ssm);
fpi_timeout_set_name(timeout, "calibrate_run_state");
}
break;
case CALIBRATE_REPEAT_STATUS:
fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_STATUS);
break;
}
}
static void calibrate_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
G_DEBUG_HERE();
if (fpi_ssm_get_error(ssm) != -ECANCELED)
elan_capture(dev);
fpi_ssm_free(ssm);
}
static void elan_calibrate(struct fp_img_dev *dev)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
elan_dev_reset(elandev);
elandev->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS;
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), calibrate_run_state,
CALIBRATE_NUM_STATES, dev);
fpi_ssm_start(ssm, calibrate_complete);
}
enum activate_states {
ACTIVATE_GET_FW_VER,
ACTIVATE_SET_FW_VER,
ACTIVATE_GET_SENSOR_DIM,
ACTIVATE_SET_SENSOR_DIM,
ACTIVATE_CMD_1,
ACTIVATE_NUM_STATES,
};
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
G_DEBUG_HERE();
switch (fpi_ssm_get_cur_state(ssm)) {
case ACTIVATE_GET_FW_VER:
elan_run_cmd(ssm, dev, &get_fw_ver_cmd, ELAN_CMD_TIMEOUT);
break;
case ACTIVATE_SET_FW_VER:
elandev->fw_ver =
(elandev->last_read[0] << 8 | elandev->last_read[1]);
fp_dbg("FW ver 0x%04hx", elandev->fw_ver);
fpi_ssm_next_state(ssm);
break;
case ACTIVATE_GET_SENSOR_DIM:
elan_run_cmd(ssm, dev, &get_sensor_dim_cmd, ELAN_CMD_TIMEOUT);
break;
case ACTIVATE_SET_SENSOR_DIM:
/* see elan_save_frame for details */
if (elandev->dev_type & ELAN_NOT_ROTATED) {
elandev->frame_width = elandev->last_read[0];
elandev->frame_height = elandev->raw_frame_height =
elandev->last_read[2];
} else {
elandev->frame_width = elandev->last_read[2];
elandev->frame_height = elandev->raw_frame_height =
elandev->last_read[0];
}
/* Work-around sensors returning the sizes as zero-based index
* rather than the number of pixels. */
if ((elandev->frame_width % 2 == 1) &&
(elandev->frame_height % 2 == 1)) {
elandev->frame_width++;
elandev->frame_height++;
elandev->raw_frame_height = elandev->frame_height;
}
if (elandev->frame_height > ELAN_MAX_FRAME_HEIGHT)
elandev->frame_height = ELAN_MAX_FRAME_HEIGHT;
fp_dbg("sensor dimensions, WxH: %dx%d", elandev->frame_width,
elandev->raw_frame_height);
fpi_ssm_next_state(ssm);
break;
case ACTIVATE_CMD_1:
/* TODO: find out what this does, if we need it */
elan_run_cmd(ssm, dev, &activate_cmd_1, ELAN_CMD_TIMEOUT);
break;
}
}
static void activate_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
G_DEBUG_HERE();
if (fpi_ssm_get_error(ssm) != -ECANCELED) {
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
}
fpi_ssm_free(ssm);
}
static void elan_activate(struct fp_img_dev *dev)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
elan_dev_reset(elandev);
fpi_ssm *ssm =
fpi_ssm_new(FP_DEV(dev), activate_run_state, ACTIVATE_NUM_STATES, dev);
fpi_ssm_start(ssm, activate_complete);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
struct elan_dev *elandev;
int r;
G_DEBUG_HERE();
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
elandev = g_malloc0(sizeof(struct elan_dev));
fp_dev_set_instance_data(FP_DEV(dev), elandev);
/* common params */
elandev->dev_type = driver_data;
elandev->background = NULL;
elandev->process_frame = elan_process_frame_thirds;
switch (driver_data) {
case ELAN_0907:
elandev->process_frame = elan_process_frame_linear;
break;
}
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void elan_deactivate(struct fp_img_dev *dev)
{
G_DEBUG_HERE();
fpi_imgdev_deactivate_complete(dev);
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
elan_dev_reset(elandev);
g_free(elandev->background);
g_free(elandev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static int dev_activate(struct fp_img_dev *dev)
{
G_DEBUG_HERE();
elan_activate(dev);
return 0;
}
static void elan_change_state(struct fp_img_dev *dev)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
enum fp_imgdev_state next_state = elandev->dev_state_next;
if (elandev->dev_state == next_state) {
fp_dbg("already in %d", next_state);
return;
} else
fp_dbg("changing to %d", next_state);
switch (next_state) {
case IMGDEV_STATE_INACTIVE:
if (elandev->cur_transfer)
/* deactivation will complete in transfer callback */
fpi_usb_cancel_transfer(elandev->cur_transfer);
else
elan_deactivate(dev);
break;
case IMGDEV_STATE_AWAIT_FINGER_ON:
/* activation completed or another enroll stage started */
elan_calibrate(dev);
break;
case IMGDEV_STATE_CAPTURE:
/* not used */
break;
case IMGDEV_STATE_AWAIT_FINGER_OFF:
elan_stop_capture(dev);
}
elandev->dev_state = next_state;
}
static void
elan_change_state_async(struct fp_dev *dev,
void *data)
{
g_message ("state change dev: %p", dev);
elan_change_state(FP_IMG_DEV (dev));
}
static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_timeout *timeout;
G_DEBUG_HERE();
switch (state) {
case IMGDEV_STATE_INACTIVE:
case IMGDEV_STATE_AWAIT_FINGER_ON:
case IMGDEV_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 */
elandev->dev_state_next = state;
timeout = fpi_timeout_add(10, elan_change_state_async, FP_DEV(dev), NULL);
name = g_strdup_printf ("dev_change_state to %d", state);
fpi_timeout_set_name(timeout, name);
g_free (name);
break;
}
case IMGDEV_STATE_CAPTURE:
/* TODO MAYBE: split capture ssm into smaller ssms and use this state */
elandev->dev_state = state;
elandev->dev_state_next = state;
break;
default:
fp_err("unrecognized state %d", state);
fpi_imgdev_session_error(dev, -EINVAL);
return -EINVAL;
}
/* as of time of writing libfprint never checks the return value */
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
G_DEBUG_HERE();
dev_change_state(dev, IMGDEV_STATE_INACTIVE);
}
struct fp_img_driver elan_driver = {
.driver = {
.id = ELAN_ID,
.name = FP_COMPONENT,
.full_name = "ElanTech Fingerprint Sensor",
.id_table = elan_id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.bz3_threshold = 24,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
.change_state = dev_change_state,
};

224
libfprint/drivers/elan.h Normal file
View File

@@ -0,0 +1,224 @@
/*
* Elan driver for libfprint
*
* Copyright (C) 2017 Igor Filatov <ia.filatov@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
*/
#ifndef __ELAN_H
#define __ELAN_H
#include <libusb.h>
#define ELAN_VEND_ID 0x04f3
/* a default device type */
#define ELAN_ALL_DEV 0
/* devices with quirks */
#define ELAN_0907 (1 << 0)
#define ELAN_0C03 (1 << 1)
#define ELAN_0C42 (1 << 2)
/* devices which don't require frame rotation before assembling */
#define ELAN_NOT_ROTATED ELAN_0C03
/* min FW version that supports calibration */
#define ELAN_MIN_CALIBRATION_FW 0x0138
/* max difference between background image mean and calibration mean
* (the response value of get_calib_mean_cmd)*/
#define ELAN_CALIBRATION_MAX_DELTA 500
/* times to retry reading calibration status during one session
* generally prevents calibration from looping indefinitely */
#define ELAN_CALIBRATION_ATTEMPTS 10
/* min and max frames in a capture */
#define ELAN_MIN_FRAMES 7
#define ELAN_MAX_FRAMES 30
/* crop frames to this height to improve stitching */
#define ELAN_MAX_FRAME_HEIGHT 50
/* number of frames to drop at the end of capture because frames captured
* while the finger is being lifted can be bad */
#define ELAN_SKIP_LAST_FRAMES 2
#define ELAN_CMD_LEN 0x2
#define ELAN_EP_CMD_OUT (0x1 | LIBUSB_ENDPOINT_OUT)
#define ELAN_EP_CMD_IN (0x3 | LIBUSB_ENDPOINT_IN)
#define ELAN_EP_IMG_IN (0x2 | LIBUSB_ENDPOINT_IN)
/* used as response length to tell the driver to skip reading response */
#define ELAN_CMD_SKIP_READ 0
/* usual command timeout and timeout for when we need to check if the finger is
* still on the device */
#define ELAN_CMD_TIMEOUT 10000
#define ELAN_FINGER_TIMEOUT 200
struct elan_cmd {
unsigned char cmd[ELAN_CMD_LEN];
int response_len;
int response_in;
unsigned short devices;
};
static const struct elan_cmd get_sensor_dim_cmd = {
.cmd = {0x00, 0x0c},
.response_len = 0x4,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_ALL_DEV,
};
static const struct elan_cmd get_fw_ver_cmd = {
.cmd = {0x40, 0x19},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_ALL_DEV,
};
/* unknown, returns 0x0 0x1 on 0907 */
static const struct elan_cmd activate_cmd_1 = {
.cmd = {0x40, 0x2a},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_0907,
};
static const struct elan_cmd get_image_cmd = {
.cmd = {0x00, 0x09},
/* raw frame sizes are calculated from image dimensions reported by the
* device */
.response_len = -1,
.response_in = ELAN_EP_IMG_IN,
.devices = ELAN_ALL_DEV,
};
static const struct elan_cmd read_sensor_status_cmd = {
.cmd = {0x40, 0x13},
.response_len = 0x1,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_ALL_DEV,
};
static const struct elan_cmd get_calib_status_cmd = {
.cmd = {0x40, 0x23},
.response_len = 0x1,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_ALL_DEV,
};
static const struct elan_cmd get_calib_mean_cmd = {
.cmd = {0x40, 0x24},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_ALL_DEV,
};
static const struct elan_cmd led_on_cmd = {
.cmd = {0x40, 0x31},
.response_len = ELAN_CMD_SKIP_READ,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_ALL_DEV,
};
/* wait for finger
* subsequent read will not complete until finger is placed on the reader */
static const struct elan_cmd pre_scan_cmd = {
.cmd = {0x40, 0x3f},
.response_len = 0x1,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_ALL_DEV,
};
/* led off, stop waiting for finger */
static const struct elan_cmd stop_cmd = {
.cmd = {0x00, 0x0b},
.response_len = ELAN_CMD_SKIP_READ,
.response_in = ELAN_EP_CMD_IN,
.devices = ELAN_ALL_DEV,
};
static const struct usb_id elan_id_table[] = {
{.vendor = ELAN_VEND_ID,.product = 0x0903,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0907,.driver_data = ELAN_0907},
{.vendor = ELAN_VEND_ID,.product = 0x0c01,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c02,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c03,.driver_data = ELAN_0C03},
{.vendor = ELAN_VEND_ID,.product = 0x0c04,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c05,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c06,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c07,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c08,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c09,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0a,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0b,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0c,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0d,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0e,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c0f,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c10,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c11,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c12,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c13,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c14,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c15,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c16,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c17,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c18,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c19,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1a,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1b,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1c,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1d,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1e,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c1f,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c20,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c21,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c22,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c23,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c24,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c25,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c26,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c27,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c28,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c29,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2a,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2b,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2c,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2d,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2e,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c2f,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c30,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c31,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c32,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c33,.driver_data = ELAN_ALL_DEV},
{.vendor = ELAN_VEND_ID,.product = 0x0c42,.driver_data = ELAN_0C42},
{0, 0, 0,},
};
static void elan_cmd_done(fpi_ssm *ssm);
static void elan_cmd_read(fpi_ssm *ssm, struct fp_img_dev *dev);
static void elan_calibrate(struct fp_img_dev *dev);
static void elan_capture(struct fp_img_dev *dev);
static void elan_deactivate(struct fp_img_dev *dev);
static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state);
#endif

1490
libfprint/drivers/etes603.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,321 +0,0 @@
/*
* Secugen FDU2000 driver for libfprint
* Copyright (C) 2007 Gustavo Chain <g@0xff.cl>
*
* 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 <errno.h>
#include <string.h>
#include <unistd.h>
#include <libusb.h>
#define FP_COMPONENT "fdu2000"
#include <fp_internal.h>
#ifndef HAVE_MEMMEM
gpointer
memmem(const gpointer haystack, size_t haystack_len, const gpointer needle, size_t needle_len) {
const gchar *begin;
const char *const last_possible = (const char *) haystack + haystack_len - needle_len;
/* The first occurrence of the empty string is deemed to occur at
* the beginning of the string. */
if (needle_len == 0)
return (void *) haystack;
/* Sanity check, otherwise the loop might search through the whole
* memory. */
if (haystack_len < needle_len)
return NULL;
for (begin = (const char *) haystack; begin <= last_possible; ++begin)
if (begin[0] == ((const char *) needle)[0] &&
!memcmp((const void *) &begin[1],
(const void *) ((const char *) needle + 1),
needle_len - 1))
return (void *) begin;
return NULL;
}
#endif /* HAVE_MEMMEM */
#define EP_IMAGE ( 0x02 | LIBUSB_ENDPOINT_IN )
#define EP_REPLY ( 0x01 | LIBUSB_ENDPOINT_IN )
#define EP_CMD ( 0x01 | LIBUSB_ENDPOINT_OUT )
#define BULK_TIMEOUT 200
/* fdu_req[] index */
typedef enum {
CAPTURE_READY,
CAPTURE_READ,
CAPTURE_END,
LED_OFF,
LED_ON
} req_index;
#define CMD_LEN 2
#define ACK_LEN 8
static const struct fdu2000_req {
const gchar cmd[CMD_LEN]; // Command to send
const gchar ack[ACK_LEN]; // Expected ACK
const guint ack_len; // ACK has variable length
} fdu_req[] = {
/* Capture */
{
.cmd = { 0x00, 0x04 },
.ack = { 0x00, 0x04, 0x01, 0x01 },
.ack_len = 4
},
{
.cmd = { 0x00, 0x01 },
.ack = { 0x00, 0x01, 0x01, 0x01 },
.ack_len = 4
},
{
.cmd = { 0x00, 0x05 },
.ack = { 0x00, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
.ack_len = 8
},
/* Led */
{
.cmd = { 0x05, 0x00 },
.ack = {},
.ack_len = 0
},
{
.cmd = { 0x05, 0x01 },
.ack = {},
.ack_len = 0
}
};
/*
* Write a command and verify reponse
*/
static gint
bulk_write_safe(libusb_dev_handle *dev, req_index rIndex) {
gchar reponse[ACK_LEN];
gint r;
gchar *cmd = (gchar *)fdu_req[rIndex].cmd;
gchar *ack = (gchar *)fdu_req[rIndex].ack;
gint ack_len = fdu_req[rIndex].ack_len;
struct libusb_bulk_transfer wrmsg = {
.endpoint = EP_CMD,
.data = cmd,
.length = sizeof(cmd),
};
struct libusb_bulk_transfer readmsg = {
.endpoint = EP_REPLY,
.data = reponse,
.length = sizeof(reponse),
};
int trf;
r = libusb_bulk_transfer(dev, &wrmsg, &trf, BULK_TIMEOUT);
if (r < 0)
return r;
if (ack_len == 0)
return 0;
/* Check reply from FP */
r = libusb_bulk_transfer(dev, &readmsg, &trf, BULK_TIMEOUT);
if (r < 0)
return r;
if (!strncmp(ack, reponse, ack_len))
return 0;
fp_err("Expected different ACK from dev");
return 1; /* Error */
}
static gint
capture(struct fp_img_dev *dev, gboolean unconditional,
struct fp_img **ret)
{
#define RAW_IMAGE_WIDTH 398
#define RAW_IMAGE_HEIGTH 301
#define RAW_IMAGE_SIZE (RAW_IMAGE_WIDTH * RAW_IMAGE_HEIGTH)
struct fp_img *img = NULL;
int bytes, r;
const gchar SOF[] = { 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0c, 0x07 }; // Start of frame
const gchar SOL[] = { 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0b, 0x06 }; // Start of line + { L L } (L: Line num) (8 nibbles)
gchar *buffer = g_malloc0(RAW_IMAGE_SIZE * 6);
gchar *image;
gchar *p;
guint offset;
struct libusb_bulk_transfer msg = {
.endpoint = EP_IMAGE,
.data = buffer,
.length = RAW_IMAGE_SIZE * 6,
};
image = g_malloc0(RAW_IMAGE_SIZE);
if ((r = bulk_write_safe(dev->udev, LED_ON))) {
fp_err("Command: LED_ON");
goto out;
}
if ((r = bulk_write_safe(dev->udev, CAPTURE_READY))) {
fp_err("Command: CAPTURE_READY");
goto out;
}
read:
if ((r = bulk_write_safe(dev->udev, CAPTURE_READ))) {
fp_err("Command: CAPTURE_READ");
goto out;
}
/* Now we are ready to read from dev */
r = libusb_bulk_transfer(dev->udev, &msg, &bytes, BULK_TIMEOUT * 10);
if (r < 0 || bytes < 1)
goto read;
/*
* Find SOF (start of line)
*/
p = memmem(buffer, RAW_IMAGE_SIZE * 6,
(const gpointer)SOF, sizeof SOF);
fp_dbg("Read %d byte/s from dev", bytes);
if (!p)
goto out;
p += sizeof SOF;
int i = 0;
bytes = 0;
while(p) {
if ( i >= RAW_IMAGE_HEIGTH )
break;
offset = p - buffer;
p = memmem(p, (RAW_IMAGE_SIZE * 6) - (offset),
(const gpointer)SOL, sizeof SOL);
if (p) {
p += sizeof SOL + 4;
int j;
for (j = 0; j < RAW_IMAGE_WIDTH; j++) {
/**
* Convert from 4 to 8 bits
* The SECUGEN-FDU2000 has 4 lines of data, so we need to join 2 bytes into 1
*/
*(image + bytes + j) = *(p + (j * 2) + 0) << 4 & 0xf0;
*(image + bytes + j) |= *(p + (j * 2) + 1) & 0x0f;
}
p += RAW_IMAGE_WIDTH * 2;
bytes += RAW_IMAGE_WIDTH;
i++;
}
}
if ((r = bulk_write_safe(dev->udev, CAPTURE_END))) {
fp_err("Command: CAPTURE_END");
goto out;
}
if ((r = bulk_write_safe(dev->udev, LED_OFF))) {
fp_err("Command: LED_OFF");
goto out;
}
img = fpi_img_new_for_imgdev(dev);
memcpy(img->data, image, RAW_IMAGE_SIZE);
img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
*ret = img;
out:
g_free(buffer);
g_free(image);
return r;
}
static
gint dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
gint r;
//if ( (r = usb_set_configuration(dev->udev, 1)) < 0 )
// goto out;
if ( (r = libusb_claim_interface(dev->udev, 0)) < 0 )
goto out;
//if ( (r = usb_set_altinterface(dev->udev, 1)) < 0 )
// goto out;
//if ( (r = usb_clear_halt(dev->udev, EP_CMD)) < 0 )
// goto out;
/* Make sure sensor mode is not capture_{ready|read} */
if ((r = bulk_write_safe(dev->udev, CAPTURE_END))) {
fp_err("Command: CAPTURE_END");
goto out;
}
if ((r = bulk_write_safe(dev->udev, LED_OFF))) {
fp_err("Command: LED_OFF");
goto out;
}
return 0;
out:
fp_err("could not init dev");
return r;
}
static
void dev_exit(struct fp_img_dev *dev)
{
if (bulk_write_safe(dev->udev, CAPTURE_END))
fp_err("Command: CAPTURE_END");
libusb_release_interface(dev->udev, 0);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x1162, .product = 0x0300 },
{ 0, 0, 0, },
};
struct fp_img_driver fdu2000_driver = {
.driver = {
.id = 7,
.name = FP_COMPONENT,
.full_name = "Secugen FDU 2000",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.img_height = RAW_IMAGE_HEIGTH,
.img_width = RAW_IMAGE_WIDTH,
.bz3_threshold = 23,
.init = dev_init,
.exit = dev_exit,
.capture = capture,
};

View File

@@ -0,0 +1,66 @@
/*
* LGPL CRC code copied from GStreamer-0.10.10:
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
* 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; version
* 2.1 of the License.
*
* 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 "upek_proto.h"
static const uint16_t crc_table[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
uint16_t
udf_crc(unsigned char *buffer, size_t size)
{
uint16_t crc = 0;
while (size--)
crc = (uint16_t) ((crc << 8) ^
crc_table[((crc >> 8) & 0x00ff) ^ *buffer++]);
return crc;
}

View File

@@ -0,0 +1,24 @@
/*
* LGPL CRC code copied from GStreamer-0.10.10:
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
* 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; version
* 2.1 of the License.
*
* 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 <stdint.h>
#include <stddef.h>
uint16_t udf_crc(unsigned char *buffer, size_t size);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,319 @@
/*
* UPEK TouchStrip Sensor-Only driver for libfprint
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* TCS4C (USB ID 147e:1000) support:
* Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
*
* TCRD5B (USB ID 147e:1001) support:
* Copyright (C) 2014 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 IMG_WIDTH_2016 288
#define IMG_WIDTH_1000 288
#define IMG_WIDTH_1001 216
struct sonly_regwrite {
uint8_t reg;
uint8_t value;
};
/***** AWAIT FINGER *****/
static const struct sonly_regwrite awfsm_2016_writev_1[] = {
{ 0x0a, 0x00 }, { 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x3b },
{ 0x00, 0x67 }, { 0x00, 0x67 },
};
static const struct sonly_regwrite awfsm_1000_writev_1[] = {
/* Initialize sensor settings */
{ 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x37 }, { 0x00, 0x5f },
{ 0x01, 0x6e }, { 0x01, 0xee }, { 0x0c, 0x13 }, { 0x0d, 0x0d },
{ 0x0e, 0x0e }, { 0x0f, 0x0d },
{ 0x13, 0x05 }, { 0x13, 0x45 },
/* Initialize finger detection registers (not enabling yet) */
{ 0x30, 0xe0 }, { 0x15, 0x26 },
{ 0x12, 0x01 }, { 0x20, 0x01 }, { 0x07, 0x10 },
{ 0x10, 0x00 }, { 0x11, 0xbf },
};
static const struct sonly_regwrite awfsm_2016_writev_2[] = {
{ 0x01, 0xc6 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x0b, 0x00 },
};
static const struct sonly_regwrite awfsm_1000_writev_2[] = {
/* Enable finger detection */
{ 0x30, 0xe1 }, { 0x15, 0x06 }, { 0x15, 0x86 },
};
static const struct sonly_regwrite awfsm_2016_writev_3[] = {
{ 0x13, 0x45 }, { 0x30, 0xe0 }, { 0x12, 0x01 }, { 0x20, 0x01 },
{ 0x09, 0x20 }, { 0x0a, 0x00 }, { 0x30, 0xe0 }, { 0x20, 0x01 },
};
static const struct sonly_regwrite awfsm_2016_writev_4[] = {
{ 0x08, 0x00 }, { 0x10, 0x00 }, { 0x12, 0x01 }, { 0x11, 0xbf },
{ 0x12, 0x01 }, { 0x07, 0x10 }, { 0x07, 0x10 }, { 0x04, 0x00 },\
{ 0x05, 0x00 }, { 0x0b, 0x00 },
/* enter finger detection mode */
{ 0x15, 0x20 }, { 0x30, 0xe1 }, { 0x15, 0x24 }, { 0x15, 0x04 },
{ 0x15, 0x84 },
};
/***** CAPTURE MODE *****/
static const struct sonly_regwrite capsm_2016_writev[] = {
/* enter capture mode */
{ 0x09, 0x28 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, { 0x04, 0x00 },
{ 0x05, 0x00 },
};
static const struct sonly_regwrite capsm_1000_writev[] = {
{ 0x08, 0x80 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, /* Enter capture mode */
};
static const struct sonly_regwrite capsm_1001_writev_1[] = {
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4e, 0x05 },
};
static const struct sonly_regwrite capsm_1001_writev_2[] = {
{ 0x4d, 0xc0 }, { 0x4e, 0x09 },
};
static const struct sonly_regwrite capsm_1001_writev_3[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x0b, 0x00 },
{ 0x04, 0x00 },
{ 0x05, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4d, 0x40 }, { 0x4e, 0x09 },
};
static const struct sonly_regwrite capsm_1001_writev_4[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4e, 0x08 },
};
static const struct sonly_regwrite capsm_1001_writev_5[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x00, 0x5f }, { 0x01, 0xee },
{ 0x03, 0x2c },
{ 0x07, 0x00 }, { 0x08, 0x00 }, { 0x09, 0x29 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x10, 0x00 }, { 0x11, 0x8f }, { 0x12, 0x01 }, { 0x13, 0x45 },
{ 0x15, 0x26 },
{ 0x1e, 0x02 },
{ 0x20, 0x01 },
{ 0x25, 0x8f },
{ 0x27, 0x23 },
{ 0x30, 0xe0 },
{ 0x07, 0x10 },
{ 0x09, 0x21 },
{ 0x13, 0x75 },
{ 0x0b, 0x80 },
};
/***** DEINITIALIZATION *****/
static const struct sonly_regwrite deinitsm_2016_writev[] = {
/* reset + enter low power mode */
{ 0x0b, 0x00 }, { 0x09, 0x20 }, { 0x13, 0x45 }, { 0x13, 0x45 },
};
static const struct sonly_regwrite deinitsm_1000_writev[] = {
{ 0x15, 0x26 }, { 0x30, 0xe0 }, /* Disable finger detection */
{ 0x0b, 0x00 }, { 0x13, 0x45 }, { 0x08, 0x00 }, /* Disable capture mode */
};
static const struct sonly_regwrite deinitsm_1001_writev[] = {
{ 0x0b, 0x00 },
{ 0x13, 0x45 },
{ 0x09, 0x29 },
{ 0x1a, 0x00 },
};
/***** INITIALIZATION *****/
static const struct sonly_regwrite initsm_2016_writev_1[] = {
{ 0x49, 0x00 },
/* BSAPI writes different values to register 0x3e each time. I initially
* thought this was some kind of clever authentication, but just blasting
* these sniffed values each time seems to work. */
{ 0x3e, 0x83 }, { 0x3e, 0x4f }, { 0x3e, 0x0f }, { 0x3e, 0xbf },
{ 0x3e, 0x45 }, { 0x3e, 0x35 }, { 0x3e, 0x1c }, { 0x3e, 0xae },
{ 0x44, 0x01 }, { 0x43, 0x06 }, { 0x43, 0x05 }, { 0x43, 0x04 },
{ 0x44, 0x00 }, { 0x0b, 0x00 },
};
static const struct sonly_regwrite initsm_1000_writev_1[] = {
{ 0x49, 0x00 }, /* Encryption disabled */
/* Setting encryption key. Doesn't need to be random since we don't use any
* encryption. */
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x04, 0x00 }, { 0x05, 0x00 },
{ 0x0b, 0x00 }, { 0x08, 0x00 }, /* Initialize capture control registers */
};
static const struct sonly_regwrite initsm_1001_writev_1[] = {
{ 0x4a, 0x9d },
{ 0x4f, 0x06 },
{ 0x4f, 0x05 },
{ 0x4f, 0x04 },
{ 0x4a, 0x9c },
{ 0x3e, 0xa6 },
{ 0x3e, 0x01 },
{ 0x3e, 0x68 },
{ 0x3e, 0xfd },
{ 0x3e, 0x72 },
{ 0x3e, 0xef },
{ 0x3e, 0x5d },
{ 0x3e, 0xc5 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4c, 0x1f }, { 0x4d, 0xb8 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_2[] = {
{ 0x4c, 0x03 }, { 0x4d, 0xb8 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_3[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4c, 0xff }, { 0x4d, 0xc0 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_4[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x09, 0x27 },
{ 0x1a, 0x02 },
{ 0x49, 0x01 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x49, 0x00 },
{ 0x3e, 0x90 },
{ 0x3e, 0xbd },
{ 0x3e, 0xbf },
{ 0x3e, 0x48 },
{ 0x3e, 0x2a },
{ 0x3e, 0xe3 },
{ 0x3e, 0xd2 },
{ 0x3e, 0x58 },
{ 0x09, 0x2f },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4d, 0x40 }, { 0x4e, 0x03 },
};
static const struct sonly_regwrite initsm_1001_writev_5[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
};

View File

@@ -1,6 +1,7 @@
/*
* UPEK TouchChip driver for libfprint
* Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
* Copyright (C) 2012 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
@@ -17,400 +18,461 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "upektc"
#define FP_COMPONENT "upektc"
#include <errno.h>
#include <string.h>
#include "drivers_api.h"
#include "upektc.h"
#include <glib.h>
#include <libusb.h>
#define UPEKTC_EP_IN (2 | LIBUSB_ENDPOINT_IN)
#define UPEKTC_EP_OUT (3 | LIBUSB_ENDPOINT_OUT)
#define UPEKET_EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define UPEKET_EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
#include <fp_internal.h>
#define SENSOR_FULL_IMAGE 59904
#define WAIT_COUNT 5
typedef char sint8;
typedef unsigned char uint8;
typedef int sint32;
typedef unsigned int uint32;
/** scan command */
static const unsigned char anScanCommand[ 0x40 ] = {
0x0e, 0x00, 0x03, 0xa8, 0x00, 0xb6, 0xbb, 0xbb,
0xb8, 0xb7, 0xb8, 0xb5, 0xb8, 0xb9, 0xb8, 0xb9,
0xbb, 0xbb, 0xbe, 0xbb, 0x4e, 0x16, 0xf4, 0x77,
0xa8, 0x07, 0x32, 0x00, 0x6a, 0x16, 0xf4, 0x77,
0x78, 0x24, 0x61, 0x00, 0xc8, 0x00, 0xec, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3c, 0xf3, 0x2f, 0x01,
0x05, 0x90, 0xf6, 0x77, 0x84, 0xf5, 0x2f, 0x01,
0x05, 0x90, 0xf6, 0x00, 0xc8, 0x00, 0xec, 0x00
struct upektc_dev {
gboolean deactivating;
const struct setup_cmd *setup_commands;
size_t setup_commands_len;
int ep_in;
int ep_out;
int init_idx;
int sum_threshold;
};
/** init command */
static const unsigned char anInitCommand[ 0x40 ] = {
0x03, 0x00, 0x00, 0x00, 0x02, 0xfb, 0x0f, 0x00,
0xc4, 0xf9, 0x2f, 0x01, 0x6d, 0x4f, 0x01, 0x10,
0x44, 0xf9, 0x2f, 0x01, 0x40, 0x00, 0x00, 0x00,
0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
enum upektc_driver_data {
UPEKTC_2015,
UPEKTC_3001,
};
/**
* \brief Common interaktion routine for the sensor device
* \param dev fingerprint image device pointer
* \param pnRawString raw data string
* \param nLen length we want to read, if 0 do not read at all
* \param pnBuffer buffer pointer we want to store the read buffer
* \return error code
*/
static sint32 askScanner( struct fp_img_dev *dev, const unsigned char *pnRawString, sint32 nLen, sint8 *pnBuffer ) {
sint8 anBuf[ 65535 ];
sint32 nRet;
int transferred;
struct libusb_bulk_transfer msg1 = {
.endpoint = 3,
.data = pnRawString,
.length = 0x40,
};
struct libusb_bulk_transfer msg2 = {
.endpoint = 0x82,
.data = anBuf,
.length = nLen,
};
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
static void start_finger_detection(struct fp_img_dev *dev);
nRet = libusb_bulk_transfer(dev->udev, &msg1, &transferred, 1003);
if (transferred != 0x40) {
return -1;
}
/****** INITIALIZATION/DEINITIALIZATION ******/
if ( !nLen ) {
return 0;
}
enum activate_states {
WRITE_INIT,
READ_DATA,
ACTIVATE_NUM_STATES,
};
nRet = libusb_bulk_transfer(dev->udev, &msg2, &transferred, 1003);
if ( ( transferred == nLen ) && ( pnBuffer != NULL ) ) {
memcpy( pnBuffer, anBuf, nLen );
return transferred;
}
static void
upektc_next_init_cmd(fpi_ssm *ssm,
struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
return nRet;
upekdev->init_idx += 1;
if (upekdev->init_idx == upekdev->setup_commands_len)
fpi_ssm_mark_completed(ssm);
else
fpi_ssm_jump_to_state(ssm, WRITE_INIT);
}
/**
* \brief Quick test if finger is on sensor
* \param pnImage image pointer
* \return 1 on yes, 0 on no
*/
static sint32 ValidScan( sint8 *pnImage ) {
sint32 nIndex, nSum;
static void write_init_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
nSum = 0;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
if (upekdev->setup_commands[upekdev->init_idx].response_len)
fpi_ssm_next_state(ssm);
else
upektc_next_init_cmd(ssm, dev);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
for ( nIndex = 0; nIndex < SENSOR_FULL_IMAGE; nIndex++ ) {
if ( ( uint8 ) pnImage[ nIndex ] < 160 ) {
nSum++;
static void read_init_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
upektc_next_init_cmd(ssm, dev);
else
fpi_ssm_mark_failed(ssm, -EIO);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(_dev);
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_out,
(unsigned char*)upekdev->setup_commands[upekdev->init_idx].cmd,
UPEKTC_CMD_LEN, write_init_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, -ENOMEM);
}
}
break;
case READ_DATA:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
data = g_malloc(upekdev->setup_commands[upekdev->init_idx].response_len);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_in, data,
upekdev->setup_commands[upekdev->init_idx].response_len,
read_init_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
break;
}
}
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
/****** FINGER PRESENCE DETECTION ******/
static int finger_present(unsigned char *img, size_t len, int sum_threshold)
{
int i, sum;
sum = 0;
for (i = 0; i < len; i++) {
if (img[i] < 160) {
sum++;
}
}
return nSum < 500 ? 0 : 1;
fp_dbg("finger_present: sum is %d\n", sum);
return sum < sum_threshold ? 0 : 1;
}
/**
* \brief Setup Sensor device
* \param dev fingerprint image device pointer
* \return error code
*/
static sint32 SetupSensor( struct fp_img_dev *dev ) {
libusb_claim_interface(dev->udev, 0);
static void finger_det_data_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = transfer->buffer;
/* setup sensor */
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xfe\x00\x01\xc0\xbd\xf0\xff\xff\xff\xff\xff\x00\xf0\xfd\x7f\x00\x60\xfd\x7f\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\xcc\xf8\x2f\x01\x09\x48\xe7\x77\xf0\xfa\x2f\x01\x09\x48\xe7\x77\xe0\x3a\xe6\x77", 0x00, NULL ) < 0 ) {
return -1;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("data transfer status %d\n", transfer->status);
fpi_imgdev_session_error(dev, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fp_dbg("expected %d, got %d bytes", transfer->length,
transfer->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
}
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf7\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x58\xf9\x2f\x01\xe9\x4f\x01\x10\xd8\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x02\xfe\x00\x01\xc0\xbd\xf0\xff\xff\xff\xff\xff\x00\xf0\xfd\x7f", 0x40, NULL ) < 0 ) {
return -2;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf7\xcd\x00\x2c\xf9\x2f\x01\x6d\x4f\x01\x10\xac\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\xfe\x16\x10\x03\xee\x00\x37\x01\x09\x02\x0e\x03\x18\x03\x1a\x03\x20\x10\x2f\x11\x3f\x12\x44\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -3;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf8\x00\x00\x02\xfe\x16\x10\x03\xee\x00\x37\x01\x09\x02\x0e\x03\x18\x03\x1a\x03\x20\x10\x2f\x11\x3f\x12\x44\x01\x01\x07\x08\x0c\x00\x6c\x6c\x00\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -4;
};
if ( askScanner( dev, "\x8b\x00\x00\x00\x3a\x50\xf9\x2f\x01\x18\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x88\xf9\x2f\x01\x91\x99\x00\x10\xf8\x00\x00\x00\xbe\x99\x00\x10\xa0\xa6\x04\x10\x01\x9b\x00\x10\x18\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00", 0x40, NULL ) < 0 ) {
return -5;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -6;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -7;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0d\xff\x36\xdc\xf8\x2f\x01\xf1\x9d\x00\x10\xfc\xf8\x2f\x01\x9d\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x13\x10\x00\x00\x00\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -8;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x0c\x37\x6a\x3d\x73\x3d\x71\x0e\x01\x0e\x81\x3d\x51\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x13\x10\x00\x00\x00\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xf0\xf8\x2f\x01", 0x00, NULL ) < 0 ) {
return -9;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -10;
};
if ( askScanner( dev, "\x8b\x00\x01\x7c\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x14\xf5\x2f\x01\xa0\x20\x14\x00\x40\xf8\x2f\x01\x05\x90\xf6\x77\x04\x00\x00\x00\x08\x00\x00\x00\x50\xf8\x2f\x01\x40\x39\xf4\x77\xa8\x20\x14\x00\x1c\xf6\x2f\x01\x2c\x20\xf4\x77\x80\x4d\xfb\x77", 0x40, NULL ) < 0 ) {
return -11;
};
if ( askScanner( dev, "\x8b\x00\x03\xc8\x3a\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\x6c\x6c\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x6c\x00\x00\x00\x00\x00\x60\x62\x62\x62\x62\x62\x51\x6c\x00\x00\x00\x00\x00\x00\x40\xf9\x2f\x01\x4f\x9d\x00\x10\x3a\x00\x00\x00\x04\xf9\x01", 0x40, NULL ) < 0 ) {
return -12;
};
if ( askScanner( dev, "\x8b\x00\x04\x02\x06\x0b\x07\x13\x0e\x55\x56\x01\x44\xf8\x2f\x01\x00\x00\x00\x00\x40\x00\x00\x00\x40\x40\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -13;
};
if ( askScanner( dev, "\x07\x00\x20\x00\x3a\x0e\x13\x07\x0f\x14\x07\x10\x15\x07\x12\x16\x07\x13\x17\x07\x14\x18\x07\x15\x18\x07\x16\x19\x07\x17\x1a\x07\x19\x1b\x07\x1a\x1c\x07\x1b\x1d\x07\x1c\x1e\x07\x1d\x1f\x07\x1e\x20\x07\x1f\x21\x07\x20\x22\x07\x21\x23\x07\x23\x23\x07\x24\x55", 0x00, NULL ) < 0 ) {
return -14;
};
if ( askScanner( dev, "\x07\x00\x20\x3a\x26\x24\x07\x25\x25\x07\x26\x25\x07\x27\x26\x07\x28\x27\x07\x29\x27\x07\x2a\x28\x07\x2b\x29\x07\x2d\x29\x07\x2e\x2a\x07\x2f\x2b\x07\x30\x2b\x07\x31\x2c\x07\x07\x1d\x1f\x07\x1e\x20\x07\x1f\x21\x07\x20\x22\x07\x21\x23\x07\x23\x23\x07\x24\x55", 0x00, NULL ) < 0 ) {
return -15;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x06\x0e\x81\x0e\x81\x09\x4d\x00\x07\x00\x20\x3a\x26\x24\x07\x25\x25\x07\x26\x25\x07\x27\x26\x07\x28\x27\x07\x29\x27\x07\x2a\x28\x07\x2b\x29\x07\x2d\x29\x07\x2e\x2a\x07\x2f\x2b\x07\x30\x2b\x07\x31\x2c\x07\x07\x1d\x1f\x07\x1e\x20\x07\x1f\x21", 0x00, NULL ) < 0 ) {
return -16;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -17;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0e\x85\x36\xd8\xf8\x2f\x01\xf1\x9d\x00\x10\xf8\xf8\x2f\x01\x99\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -18;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\x0d\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xec\xf8\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -19;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf7\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\x6c\x6c\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x6c\x00\x00\x00\x00\x00\x60\x62\x62\x62\x62\x62\x51\x6c\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -20;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf7\x00\x00\x02\xf9\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\x6c\x6c\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x6c\x00\x00\x00\x00\x00\x60\x62\x62\x62\x62\x62", 0x40, NULL ) < 0 ) {
return -21;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf7\xf4\x00\x14\xf9\x2f\x01\x6d\x4f\x01\x10\x94\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\xf9\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -22;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x20\x6c\x01\x6d\x4f\x01\x10\x94\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\xf9\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xe8\xf8\x2f\x01", 0x00, NULL ) < 0 ) {
return -23;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf9\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xe8\xf8\x2f\x01\xec\xf8\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -24;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf9\x01\x00\x1c\xf9\x2f\x01\x6d\x4f\x01\x10\x9c\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\x6c\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -25;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x12\x1c\x0c\x1b\x08\x1a\x07\x30\x08\x09\x6d\x08\x27\x00\x9e\x00\x1e\x23\x47\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\x6c\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09", 0x00, NULL ) < 0 ) {
return -26;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -27;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0d\xff\x36\xdc\xf8\x2f\x01\xf1\x9d\x00\x10\xfc\xf8\x2f\x01\x9d\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\x6d\x10\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -28;
};
if ( askScanner( dev, "\x08\x00\x00\x00\x0a\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x10\xfc\xf8\x2f\x01\x9d\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\x6d\x10\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -29;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x08\x0e\x85\x09\xed\x09\x6d\x09\xed\x1e\x3f\x05\x05\x02\x05\x26\x27\x6d\x10\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c\xf0\xf8\x2f\x01\x97\x40\x01\x10\x08\x00\x00\x00\x00\x00\x00\x00\x3e\xf9\x2f\x01", 0x00, NULL ) < 0 ) {
return -30;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf3\x6c\x6c\xf0\xf8\x2f\x01\x97\x40\x01\x10\x08\x00\x00\x00\x00\x00\x00\x00\x3e\xf9\x2f\x01\x04\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\x00\x46\x03\x10\x08\x00\x00\x00\x08\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -31;
};
if ( askScanner( dev, "\x84\x00\x00\x00\x32\x02\xa3\x04\x10\x3b\xa3\x04\x10\x1a\xa3\x04\x10\xf9\xa2\x04\x10\xd8\xa2\x00\xb9\x19\xe2\x87\xba\x56\x78\x72\x68\x9e\x7a\xf4\x65\x6d\xd9\xde\xf6\x33\xa2\x04\x10\x12\xa2\x04\x10\xf1\xa1\x04\x10\x04\x00\x00\x00\x00\x00\x00\xb4\x2d\x6c\xe9", 0x40, NULL ) < 0 ) {
return -32;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x06\x1a\x07\x1b\x08\x1c\x0c\x77\x21\xac\xe5\x77\x00\x00\x00\x00\xaa\x4e\x01\x10\x3c\x01\x00\x00\xc4\xf8\x2f\x01\xdc\xf8\x2f\x01\x00\x00\x00\x00\x40\x00\x00\x00\xb9\x19\xe2\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00", 0x00, NULL ) < 0 ) {
return -33;
};
if ( askScanner( dev, "\x08\x00\x00\x00\x0a\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\xcc\xf8\x2f\x01\x8b\x41\x01\x10\x8c\xf8\x2f\x01\x40\x00\x00\x00", 0x00, NULL ) < 0 ) {
return -34;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x04\x3d\x51\x0a\x00\x01\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xfc\xf9\x2f\x01\x31\x10\x01\x10\xd0\xf9\x2f\x01\x00\x00\x00\x00\x1a\x07\x1b\x08\x1c\x0c\xc6\xf8\x66\xbc\xc4\xbe\x0b\x25\xc5\x4c\xf4\x03\x10\x2f\x11\x3f\x12\x44", 0x00, NULL ) < 0 ) {
return -35;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -36;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0a\x10\x36\x88\xf9\x2f\x01\xf1\x9d\x00\x10\xa8\xf9\x2f\x01\x49\xf9\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\xed\x00\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -37;
};
if ( askScanner( dev, "\x8b\x00\x00\xbc\x3a\x40\xd3\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8\xf4\x2f\x01\x80\x69\x67\xff\xff\xff\xff\xff\x00\xf0\xfd\x7f\x00\x60\xfd\x7f\x3c\x01\x00\x00\xa0\xf5\x2f\x01\x03\x01\x00\x00\x9a\x11\xf4\x77\x9f\x11\xf4\x77\x3c\x01\x00\x00\xa0\xf5\x01", 0x40, NULL ) < 0 ) {
return -38;
};
if ( askScanner( dev, "\x8b\x00\x00\xf6\x3a\x0b\x07\xa5\x03\x2f\x63\x97\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -39;
};
if ( askScanner( dev, "\x8b\x00\x01\x30\x3a\x0b\x00\x00\x00\x00\x00\x00\x12\xcd\xa6\x3c\x36\xec\x6a\x73\x00\x64\x75\xdf\x2e\x13\xec\xca\x3c\x03\x00\x00\x06\xa5\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -40;
};
if ( askScanner( dev, "\x8b\x00\x01\x6a\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -41;
};
if ( askScanner( dev, "\x8b\x00\x01\xa4\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\xa5\x83\x1b\x8e\xac\x00\x00\x0b\xa5\x08\x08\x03\x00\x00\x01\x02\x03\x06\x00\x00\x00\x00\x00\x8d\xa5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -42;
};
if ( askScanner( dev, "\x8b\x00\x01\xde\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -43;
};
if ( askScanner( dev, "\x8b\x00\x02\x18\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -44;
};
if ( askScanner( dev, "\x8b\x00\x02\x52\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -45;
};
if ( askScanner( dev, "\x8b\x00\x02\x8c\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -46;
};
if ( askScanner( dev, "\x8b\x00\x02\xc6\x2a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -47;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf1\x2f\x01\x49\xf9\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\xed\x00\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c\x9c\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -48;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf1\x01\x00\xb4\xf9\x2f\x01\x6d\x4f\x01\x10\x34\xf9\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x00, NULL ) < 0 ) {
return -49;
};
if ( askScanner( dev, "\x8b\x00\x01\x10\x3a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -50;
};
if ( askScanner( dev, "\x8b\x00\x01\x4a\x2e\x0b\x06\xa5\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -51;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xfb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x88\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -51;
};
/* enable sensor */
if ( askScanner( dev, anInitCommand, 0x00, NULL ) < 0 ) {
return -52;
if (finger_present(data, IMAGE_SIZE, upekdev->sum_threshold)) {
/* finger present, start capturing */
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
} else {
/* no finger, poll for a new histogram */
start_finger_detection(dev);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_cmd_cb(struct libusb_transfer *t)
{
struct libusb_transfer *transfer;
unsigned char *data;
int r;
struct fp_img_dev *dev = t->user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("req transfer status %d\n", t->status);
fpi_imgdev_session_error(dev, -EIO);
goto exit_free_transfer;
} else if (t->length != t->actual_length) {
fp_dbg("expected %d, sent %d bytes", t->length, t->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
goto exit_free_transfer;
}
transfer = fpi_usb_alloc();
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_in, data, IMAGE_SIZE,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
exit_free_transfer:
libusb_free_transfer(t);
}
static void start_finger_detection(struct fp_img_dev *dev)
{
int r;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
struct libusb_transfer *transfer;
G_DEBUG_HERE();
if (upekdev->deactivating) {
complete_deactivation(dev);
return;
}
transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
finger_det_cmd_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
}
/****** CAPTURE ******/
enum capture_states {
CAPTURE_WRITE_CMD,
CAPTURE_READ_DATA,
CAPTURE_NUM_STATES,
};
static void capture_cmd_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_read_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
unsigned char *data = transfer->buffer;
struct fp_img *img;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("request is not completed, %d", transfer->status);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fp_dbg("expected %d, sent %d bytes", transfer->length, transfer->actual_length);
fpi_ssm_mark_failed(ssm, -EPROTO);
goto out;
}
img = fpi_img_new(IMAGE_SIZE);
memcpy(img->data, data, IMAGE_SIZE);
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(_dev);
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_CMD:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
capture_cmd_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, -ENOMEM);
}
}
break;
case CAPTURE_READ_DATA:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_in, data, IMAGE_SIZE,
capture_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
break;
};
}
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(_dev);
fp_dbg("Capture completed");
if (upekdev->deactivating)
complete_deactivation(dev);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static void start_capture(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
if (upekdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
G_DEBUG_HERE();
fpi_ssm_start(ssm, capture_sm_complete);
}
static int dev_activate(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
upekdev->init_idx = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static int DetectFinger( struct fp_img_dev *dev ) {
sint32 nRet = 0;
uint8 *pnData = NULL;
static void dev_deactivate(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
pnData = g_malloc( SENSOR_FULL_IMAGE );
nRet = askScanner( dev, anScanCommand, SENSOR_FULL_IMAGE, pnData );
if ( nRet != SENSOR_FULL_IMAGE ) {
nRet = 0;
goto end;
}
nRet = ValidScan( pnData );
end:
g_free( pnData );
return nRet;
upekdev->deactivating = TRUE;
}
static int awaitFingerOn( struct fp_img_dev *dev ) {
int nRet = 0;
int nCount = WAIT_COUNT;
static void complete_deactivation(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
/* wait until a finger is present */
do {
nRet = DetectFinger( dev );
} while ( nRet == 0 );
/* give user time to scan his full finger */
while ( nCount-- ) {
nRet = DetectFinger( dev );
}
return nRet != 1 ? nRet : 0;
upekdev->deactivating = FALSE;
fpi_imgdev_deactivate_complete(dev);
}
static int capture( struct fp_img_dev *dev, gboolean unconditional, struct fp_img **ppsRet ) {
struct fp_img *psImg = NULL;
uint8 *pnData = NULL;
sint32 nRet = 0;
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct upektc_dev *upekdev;
psImg = fpi_img_new_for_imgdev( dev );
pnData = g_malloc( SENSOR_FULL_IMAGE );
nRet = askScanner( dev, anScanCommand, SENSOR_FULL_IMAGE, pnData );
if ( nRet == SENSOR_FULL_IMAGE ) {
memcpy( psImg -> data, pnData, SENSOR_FULL_IMAGE );
*ppsRet = psImg;
nRet = 0;
} else {
nRet = -1;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
g_free( pnData );
return nRet;
}
static int dev_init( struct fp_img_dev *dev, unsigned long driver_data ) {
int nResult;
nResult = libusb_claim_interface(dev->udev, 0);
if ( nResult < 0 ) {
fp_err( "could not claim interface 0" );
return nResult;
upekdev = g_malloc0(sizeof(struct upektc_dev));
fp_dev_set_instance_data(FP_DEV(dev), upekdev);
switch (driver_data) {
case UPEKTC_2015:
upekdev->ep_in = UPEKTC_EP_IN;
upekdev->ep_out = UPEKTC_EP_OUT;
upekdev->setup_commands = upektc_setup_commands;
upekdev->setup_commands_len = G_N_ELEMENTS(upektc_setup_commands);
upekdev->sum_threshold = UPEKTC_SUM_THRESHOLD;
break;
case UPEKTC_3001:
upekdev->ep_in = UPEKET_EP_IN;
upekdev->ep_out = UPEKET_EP_OUT;
upekdev->setup_commands = upeket_setup_commands;
upekdev->setup_commands_len = G_N_ELEMENTS(upeket_setup_commands);
upekdev->sum_threshold = UPEKET_SUM_THRESHOLD;
break;
default:
fp_err("Device variant %lu is not known\n", driver_data);
g_free(upekdev);
fp_dev_set_instance_data(FP_DEV(dev), NULL);
return -ENODEV;
break;
}
nResult = SetupSensor( dev );
return nResult;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_exit( struct fp_img_dev *dev ) {
libusb_release_interface(dev->udev, 0);
static void dev_deinit(struct fp_img_dev *dev)
{
void *user_data;
user_data = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(user_data);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x0483, .product = 0x2015 },
{ .vendor = 0x0483, .product = 0x2015, .driver_data = UPEKTC_2015 },
{ .vendor = 0x147e, .product = 0x3001, .driver_data = UPEKTC_3001 },
{ 0, 0, 0, },
};
struct fp_img_driver upektc_driver = {
.driver = {
.id = 5,
.id = UPEKTC_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchChip",
.full_name = "UPEK TouchChip/Eikon Touch 300",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE,
.img_height = 288,
.img_width = 208,
.flags = 0,
.img_height = IMAGE_HEIGHT,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 30,
.init = dev_init,
.exit = dev_exit,
.await_finger_on = awaitFingerOn,
.capture = capture,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

1939
libfprint/drivers/upektc.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,647 @@
/*
* UPEK TouchChip driver for libfprint
* Copyright (C) 2013 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 "upektc_img"
#include "drivers_api.h"
#include "upek_proto.h"
#include "aeslib.h"
#include "upektc_img.h"
static void start_capture(struct fp_img_dev *dev);
static void start_deactivation(struct fp_img_dev *dev);
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define CTRL_TIMEOUT 4000
#define BULK_TIMEOUT 4000
#define IMAGE_WIDTH 144
#define IMAGE_HEIGHT 384
#define IMAGE_SIZE (IMAGE_WIDTH * IMAGE_HEIGHT)
#define MAX_CMD_SIZE 64
#define MAX_RESPONSE_SIZE 2052
#define SHORT_RESPONSE_SIZE 64
struct upektc_img_dev {
unsigned char cmd[MAX_CMD_SIZE];
unsigned char response[MAX_RESPONSE_SIZE];
unsigned char image_bits[IMAGE_SIZE * 2];
unsigned char seq;
size_t image_size;
size_t response_rest;
gboolean deactivating;
};
/****** HELPERS ******/
static void upektc_img_cmd_fix_seq(unsigned char *cmd_buf, unsigned char seq)
{
uint8_t byte;
byte = cmd_buf[5];
byte &= 0x0f;
byte |= (seq << 4);
cmd_buf[5] = byte;
}
static void upektc_img_cmd_update_crc(unsigned char *cmd_buf, size_t size)
{
/* CRC does not cover Ciao prefix (4 bytes) and CRC location (2 bytes) */
uint16_t crc = udf_crc(cmd_buf + 4, size - 6);
cmd_buf[size - 2] = (crc & 0x00ff);
cmd_buf[size - 1] = (crc & 0xff00) >> 8;
}
static void
upektc_img_submit_req(fpi_ssm *ssm,
struct fp_img_dev *dev,
const unsigned char *buf,
size_t buf_size,
unsigned char seq,
libusb_transfer_cb_fn cb)
{
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
struct libusb_transfer *transfer = fpi_usb_alloc();
int r;
BUG_ON(buf_size > MAX_CMD_SIZE);
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
memcpy(upekdev->cmd, buf, buf_size);
upektc_img_cmd_fix_seq(upekdev->cmd, seq);
upektc_img_cmd_update_crc(upekdev->cmd, buf_size);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT, upekdev->cmd, buf_size,
cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
static void
upektc_img_read_data(fpi_ssm *ssm,
struct fp_img_dev *dev,
size_t buf_size,
size_t buf_offset,
libusb_transfer_cb_fn cb)
{
struct libusb_transfer *transfer = fpi_usb_alloc();
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
int r;
BUG_ON(buf_size > MAX_RESPONSE_SIZE);
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, upekdev->response + buf_offset, buf_size,
cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
/****** CAPTURE ******/
enum capture_states {
CAPTURE_INIT_CAPTURE,
CAPTURE_READ_DATA,
CAPTURE_READ_DATA_TERM,
CAPTURE_ACK_00_28,
CAPTURE_ACK_08,
CAPTURE_ACK_FRAME,
CAPTURE_ACK_00_28_TERM,
CAPTURE_NUM_STATES,
};
static void capture_reqs_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_ACK_00_28_TERM:
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA_TERM);
break;
default:
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
break;
}
}
static int upektc_img_process_image_frame(unsigned char *image_buf, unsigned char *cmd_res)
{
int offset = 8;
int len = ((cmd_res[5] & 0x0f) << 8) | (cmd_res[6]);
len -= 1;
if (cmd_res[7] == 0x2c) {
len -= 10;
offset += 10;
}
if (cmd_res[7] == 0x20) {
len -= 4;
}
memcpy(image_buf, cmd_res + offset, len);
return len;
}
static void capture_read_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = upekdev->response;
struct fp_img *img;
size_t response_size;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("request is not completed, %d", transfer->status);
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
if (upekdev->deactivating) {
fp_dbg("Deactivate requested\n");
fpi_ssm_mark_completed(ssm);
return;
}
fp_dbg("request completed, len: %.4x", transfer->actual_length);
if (transfer->actual_length == 0) {
fpi_ssm_jump_to_state(ssm, fpi_ssm_get_cur_state(ssm));
return;
}
if (fpi_ssm_get_cur_state(ssm) == CAPTURE_READ_DATA_TERM) {
fp_dbg("Terminating SSM\n");
fpi_ssm_mark_completed(ssm);
return;
}
if (!upekdev->response_rest) {
response_size = ((data[5] & 0x0f) << 8) + data[6];
response_size += 9; /* 7 bytes for header, 2 for CRC */
if (response_size > transfer->actual_length) {
fp_dbg("response_size is %lu, actual_length is %d\n",
response_size, transfer->actual_length);
fp_dbg("Waiting for rest of transfer");
BUG_ON(upekdev->response_rest);
upekdev->response_rest = response_size - transfer->actual_length;
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
return;
}
}
upekdev->response_rest = 0;
switch (data[4]) {
case 0x00:
switch (data[7]) {
/* No finger */
case 0x28:
fp_dbg("18th byte is %.2x\n", data[18]);
switch (data[18]) {
case 0x0c:
/* no finger */
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28);
break;
case 0x00:
/* finger is present! */
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28);
break;
case 0x1e:
/* short scan */
fp_err("short scan, aborting\n");
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_TOO_SHORT);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28_TERM);
break;
case 0x1d:
/* too much horisontal movement */
fp_err("too much horisontal movement, aborting\n");
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_CENTER_FINGER);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28_TERM);
break;
default:
/* some error happened, cancel scan */
fp_err("something bad happened, stop scan\n");
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28_TERM);
break;
}
break;
/* Image frame with additional info */
case 0x2c:
fpi_imgdev_report_finger_status(dev, TRUE);
/* Plain image frame */
case 0x24:
upekdev->image_size +=
upektc_img_process_image_frame(upekdev->image_bits + upekdev->image_size,
data);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_FRAME);
break;
/* Last image frame */
case 0x20:
upekdev->image_size +=
upektc_img_process_image_frame(upekdev->image_bits + upekdev->image_size,
data);
BUG_ON(upekdev->image_size != IMAGE_SIZE);
fp_dbg("Image size is %lu\n", upekdev->image_size);
img = fpi_img_new(IMAGE_SIZE);
img->flags = FP_IMG_PARTIAL;
memcpy(img->data, upekdev->image_bits, IMAGE_SIZE);
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);
break;
default:
fp_err("Unknown response!\n");
fpi_ssm_mark_failed(ssm, -EIO);
break;
}
break;
case 0x08:
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_08);
break;
default:
fp_err("Not handled response!\n");
fpi_ssm_mark_failed(ssm, -EIO);
}
}
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_INIT_CAPTURE:
upektc_img_submit_req(ssm, dev, upek2020_init_capture, sizeof(upek2020_init_capture),
upekdev->seq, capture_reqs_cb);
upekdev->seq++;
break;
case CAPTURE_READ_DATA:
case CAPTURE_READ_DATA_TERM:
if (!upekdev->response_rest)
upektc_img_read_data(ssm, dev, SHORT_RESPONSE_SIZE, 0, capture_read_data_cb);
else
upektc_img_read_data(ssm, dev, MAX_RESPONSE_SIZE - SHORT_RESPONSE_SIZE,
SHORT_RESPONSE_SIZE, capture_read_data_cb);
break;
case CAPTURE_ACK_00_28:
case CAPTURE_ACK_00_28_TERM:
upektc_img_submit_req(ssm, dev, upek2020_ack_00_28, sizeof(upek2020_ack_00_28),
upekdev->seq, capture_reqs_cb);
upekdev->seq++;
break;
case CAPTURE_ACK_08:
upektc_img_submit_req(ssm, dev, upek2020_ack_08, sizeof(upek2020_ack_08),
0, capture_reqs_cb);
break;
case CAPTURE_ACK_FRAME:
upektc_img_submit_req(ssm, dev, upek2020_ack_frame, sizeof(upek2020_ack_frame),
upekdev->seq, capture_reqs_cb);
upekdev->seq++;
break;
};
}
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(_dev);
int err = fpi_ssm_get_error(ssm);
fp_dbg("Capture completed, %d", err);
fpi_ssm_free(ssm);
if (upekdev->deactivating)
start_deactivation(dev);
else if (err)
fpi_imgdev_session_error(dev, err);
else
start_capture(dev);
}
static void start_capture(struct fp_img_dev *dev)
{
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
upekdev->image_size = 0;
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
enum deactivate_states {
DEACTIVATE_DEINIT,
DEACTIVATE_READ_DEINIT_DATA,
DEACTIVATE_NUM_STATES,
};
static void deactivate_reqs_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
}
/* TODO: process response properly */
static void deactivate_read_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
}
static void deactivate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case DEACTIVATE_DEINIT:
upektc_img_submit_req(ssm, dev, upek2020_deinit, sizeof(upek2020_deinit),
upekdev->seq, deactivate_reqs_cb);
upekdev->seq++;
break;
case DEACTIVATE_READ_DEINIT_DATA:
upektc_img_read_data(ssm, dev, SHORT_RESPONSE_SIZE, 0, deactivate_read_data_cb);
break;
};
}
static void deactivate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(_dev);
int err = fpi_ssm_get_error(ssm);
fp_dbg("Deactivate completed");
fpi_ssm_free(ssm);
if (err) {
fpi_imgdev_session_error(dev, err);
return;
}
upekdev->deactivating = FALSE;
fpi_imgdev_deactivate_complete(dev);
}
static void start_deactivation(struct fp_img_dev *dev)
{
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
upekdev->image_size = 0;
ssm = fpi_ssm_new(FP_DEV(dev), deactivate_run_state, DEACTIVATE_NUM_STATES, dev);
fpi_ssm_start(ssm, deactivate_sm_complete);
}
enum activate_states {
ACTIVATE_CONTROL_REQ_1,
ACTIVATE_READ_CTRL_RESP_1,
ACTIVATE_INIT_1,
ACTIVATE_READ_INIT_1_RESP,
ACTIVATE_INIT_2,
ACTIVATE_READ_INIT_2_RESP,
ACTIVATE_CONTROL_REQ_2,
ACTIVATE_READ_CTRL_RESP_2,
ACTIVATE_INIT_3,
ACTIVATE_READ_INIT_3_RESP,
ACTIVATE_INIT_4,
ACTIVATE_READ_INIT_4_RESP,
ACTIVATE_NUM_STATES,
};
static void init_reqs_ctrl_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
}
static void init_reqs_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
}
/* TODO: process response properly */
static void init_read_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
}
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
struct libusb_transfer *transfer;
struct fp_img_dev *idev = user_data;
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(dev);
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case ACTIVATE_CONTROL_REQ_1:
case ACTIVATE_CONTROL_REQ_2:
{
unsigned char *data;
transfer = fpi_usb_alloc();
transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER |
LIBUSB_TRANSFER_FREE_TRANSFER;
data = g_malloc0(LIBUSB_CONTROL_SETUP_SIZE + 1);
libusb_fill_control_setup(data,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, 0x0c, 0x100, 0x0400, 1);
libusb_fill_control_transfer(transfer, fpi_dev_get_usb_dev(dev), data,
init_reqs_ctrl_cb, ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
break;
case ACTIVATE_INIT_1:
upektc_img_submit_req(ssm, idev, upek2020_init_1, sizeof(upek2020_init_1),
0, init_reqs_cb);
break;
case ACTIVATE_INIT_2:
upektc_img_submit_req(ssm, idev, upek2020_init_2, sizeof(upek2020_init_2),
0, init_reqs_cb);
break;
case ACTIVATE_INIT_3:
upektc_img_submit_req(ssm, idev, upek2020_init_3, sizeof(upek2020_init_3),
0, init_reqs_cb);
break;
case ACTIVATE_INIT_4:
upektc_img_submit_req(ssm, idev, upek2020_init_4, sizeof(upek2020_init_4),
upekdev->seq, init_reqs_cb);
/* Seq should be updated after 4th init */
upekdev->seq++;
break;
case ACTIVATE_READ_CTRL_RESP_1:
case ACTIVATE_READ_CTRL_RESP_2:
case ACTIVATE_READ_INIT_1_RESP:
case ACTIVATE_READ_INIT_2_RESP:
case ACTIVATE_READ_INIT_3_RESP:
case ACTIVATE_READ_INIT_4_RESP:
upektc_img_read_data(ssm, idev, SHORT_RESPONSE_SIZE, 0, init_read_data_cb);
break;
}
}
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
int err = fpi_ssm_get_error(ssm);
fpi_ssm_free(ssm);
fp_dbg("%s status %d", __func__, err);
fpi_imgdev_activate_complete(dev, err);
if (!err)
start_capture(dev);
}
static int dev_activate(struct fp_img_dev *dev)
{
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
upekdev->seq = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
upekdev->deactivating = TRUE;
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct upektc_img_dev *upekdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
upekdev = g_malloc0(sizeof(struct upektc_img_dev));
fp_dev_set_instance_data(FP_DEV(dev), upekdev);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(upekdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static int discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
{
if (dsc->idProduct == 0x2020 && dsc->bcdDevice == 1)
return 1;
if (dsc->idProduct == 0x2016 && dsc->bcdDevice == 2)
return 1;
return 0;
}
static const struct usb_id id_table[] = {
{ .vendor = 0x147e, .product = 0x2016 },
{ .vendor = 0x147e, .product = 0x2020 },
{ 0, 0, 0, },
};
struct fp_img_driver upektc_img_driver = {
.driver = {
.id = UPEKTC_IMG_ID,
.name = FP_COMPONENT,
.full_name = "Upek TouchChip Fingerprint Coprocessor",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
.discover = discover,
},
.flags = 0,
.img_height = IMAGE_HEIGHT,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 20,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

@@ -0,0 +1,144 @@
/*
* Upek TouchChip Fingerprint Coprocessor definitions
* Copyright (c) 2013 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
*/
#ifndef __UPEKTC_IMG_H
#define __UPEKTC_IMG_H
static const unsigned char upek2020_init_1[] = {
'C', 'i', 'a', 'o',
0x04,
0x00, 0x0d,
0x01, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x00,
0xda, 0xc1
};
static const unsigned char upek2020_init_2[] = {
0x43, 0x69, 0x61, 0x6f,
0x07,
0x00, 0x01,
0x01,
0x3d, 0x72
};
static const unsigned char upek2020_init_3[] = {
'C', 'i', 'a', 'o',
0x04,
0x00, 0x0d,
0x01, 0x00, 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x00,
0x55, 0x2f
};
static const unsigned char upek2020_init_4[] = {
'C', 'i', 'a', 'o',
0x00,
0x00, 0x07,
0x28, 0x04, 0x00, 0x00, 0x00, 0x06, 0x04,
0xc0, 0xd6
};
static const unsigned char upek2020_deinit[] = {
'C', 'i', 'a', 'o',
0x07,
0x00, 0x01,
0x01,
0x3d,
0x72
};
static const unsigned char upek2020_init_capture[] = {
'C', 'i', 'a', 'o',
0x00,
0x00, 0x0e, /* Seq = 7, len = 0x00e */
0x28, /* CMD = 0x28 */
0x0b, 0x00, /* Inner len = 0x000b */
0x00, 0x00,
0x0e, /* SUBCMD = 0x0e */
0x02,
0xfe, 0xff, 0xff, 0xff, /* timeout = -2 = 0xfffffffe = infinite time */
0x02,
0x00, /* Wait for acceptable finger */
0x02,
0x14, 0x9a /* CRC */
};
#if 0
static const unsigned char finger_status[] = {
'C', 'i', 'a', 'o',
0x00,
0x70, 0x14, /* Seq = 7, len = 0x014 */
0x28, /* CMD = 0x28 */
0x11, 0x00, /* Inner len = 0x0011 */
0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
0x26, 0x03, /* CRC */
0x00, 0x00, 0x00, /* Rest is garbage */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif
static const unsigned char upek2020_ack_00_28[] = {
'C', 'i', 'a', 'o',
0x00,
0x80, 0x08, /* Seq = 8, len = 0x008 */
0x28, /* CMD = 0x28 */
0x05, 0x00, /* Inner len = 0x0005 */
0x00, 0x00, 0x00, 0x30, 0x01,
0x6a, 0xc4 /* CRC */
};
#if 0
/* No seq should be tracked here */
static const unsigned char got_finger[] = {
'C', 'i', 'a', 'o',
0x08,
0x00, 0x00, /* Seq = 0, len = 0x000 */
0xa1, 0xa9, /* CRC */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Rest is garbage */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif
/* No seq should be put in there */
static const unsigned char upek2020_ack_08[] = {
'C', 'i', 'a', 'o',
0x09,
0x00, 0x00, /* Seq = 0, len = 0x0 */
0x91, 0x9e /* CRC */
};
static const unsigned char upek2020_ack_frame[] = {
'C', 'i', 'a', 'o',
0x00,
0x50, 0x01, /* Seq = 5, len = 0x001 */
0x30,
0xac, 0x5b /* CRC */
};
#endif

View File

@@ -27,13 +27,9 @@
#define FP_COMPONENT "upekts"
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <libusb.h>
#include <fp_internal.h>
#include "drivers_api.h"
#include "fpi-async.h"
#include "upek_proto.h"
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
@@ -49,49 +45,7 @@ struct upekts_dev {
uint8_t seq; /* FIXME: improve/automate seq handling */
};
static const uint16_t crc_table[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
static uint16_t udf_crc(unsigned char *buffer, size_t size)
{
uint16_t crc = 0;
while (size--)
crc = (uint16_t) ((crc << 8) ^
crc_table[((crc >> 8) & 0x00ff) ^ *buffer++]);
return crc;
}
/*
* MESSAGE FORMAT
@@ -137,17 +91,15 @@ static struct libusb_transfer *alloc_send_cmd_transfer(struct fp_dev *dev,
unsigned char seq_a, unsigned char seq_b, const unsigned char *data,
uint16_t len, libusb_transfer_cb_fn callback, void *user_data)
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct libusb_transfer *transfer = fpi_usb_alloc();
uint16_t crc;
const char *ciao = "Ciao";
/* 9 bytes extra for: 4 byte 'Ciao', 1 byte A, 1 byte B | lenHI,
* 1 byte lenLO, 2 byte CRC */
size_t urblen = len + 9;
unsigned char *buf;
if (!transfer)
return NULL;
if (!data && len > 0) {
fp_err("len>0 but no data?");
return NULL;
@@ -156,7 +108,7 @@ static struct libusb_transfer *alloc_send_cmd_transfer(struct fp_dev *dev,
buf = g_malloc(urblen);
/* Write header */
strncpy(buf, "Ciao", 4);
memcpy(buf, ciao, strlen(ciao));
len = GUINT16_TO_LE(len);
buf[4] = seq_a;
buf[5] = seq_b | ((len & 0xf00) >> 8);
@@ -171,7 +123,7 @@ static struct libusb_transfer *alloc_send_cmd_transfer(struct fp_dev *dev,
buf[urblen - 2] = crc >> 8;
buf[urblen - 1] = crc & 0xff;
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, buf, urblen,
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(dev), EP_OUT, buf, urblen,
callback, user_data, TIMEOUT);
return transfer;
}
@@ -183,7 +135,7 @@ static struct libusb_transfer *alloc_send_cmd28_transfer(struct fp_dev *dev,
uint16_t _innerlen = innerlen;
size_t len = innerlen + 6;
unsigned char *buf = g_malloc0(len);
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
uint8_t seq = upekdev->seq + CMD_SEQ_INCREMENT;
struct libusb_transfer *ret;
@@ -413,16 +365,13 @@ static void read_msg_cb(struct libusb_transfer *transfer)
* to read the remainder. This is handled below. */
if (len > MAX_DATA_IN_READ_BUF) {
int needed = len - MAX_DATA_IN_READ_BUF;
struct libusb_transfer *etransfer = libusb_alloc_transfer(0);
struct libusb_transfer *etransfer = fpi_usb_alloc();
int r;
if (!transfer)
goto err;
fp_dbg("didn't fit in buffer, need to extend by %d bytes", needed);
data = g_realloc((gpointer) data, MSG_READ_BUF_SIZE + needed);
libusb_fill_bulk_transfer(etransfer, udata->dev->udev, EP_IN,
libusb_fill_bulk_transfer(etransfer, fpi_dev_get_usb_dev(udata->dev), EP_IN,
data + MSG_READ_BUF_SIZE, needed, read_msg_extend_cb, udata,
TIMEOUT);
@@ -453,15 +402,10 @@ out:
static int __read_msg_async(struct read_msg_data *udata)
{
unsigned char *buf = g_malloc(MSG_READ_BUF_SIZE);
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct libusb_transfer *transfer = fpi_usb_alloc();
int r;
if (!transfer) {
g_free(buf);
return -ENOMEM;
}
libusb_fill_bulk_transfer(transfer, udata->dev->udev, EP_IN, buf,
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(udata->dev), EP_IN, buf,
MSG_READ_BUF_SIZE, read_msg_cb, udata, TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
@@ -530,27 +474,30 @@ enum initsm_states {
INITSM_NUM_STATES,
};
static void initsm_read_msg_response_cb(struct fpi_ssm *ssm,
enum read_msg_status status, uint8_t seq,
unsigned char expect_subcmd, unsigned char subcmd)
static void
initsm_read_msg_response_cb(fpi_ssm *ssm,
struct fp_dev *dev,
enum read_msg_status status,
uint8_t seq,
unsigned char expect_subcmd,
unsigned char subcmd)
{
struct fp_dev *dev = ssm->dev;
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
if (status != READ_MSG_RESPONSE) {
fp_err("expected response, got %d seq=%x in state %d", status, seq,
ssm->cur_state);
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_failed(ssm, -1);
} else if (subcmd != expect_subcmd) {
fp_warn("expected response to subcmd 0x%02x, got response to %02x in "
"state %d", expect_subcmd, subcmd, ssm->cur_state);
fpi_ssm_mark_aborted(ssm, -1);
"state %d", expect_subcmd, subcmd, fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_failed(ssm, -1);
} else if (seq != upekdev->seq) {
fp_err("expected response to cmd seq=%02x, got response to %02x "
"in state %d", upekdev->seq, seq, ssm->cur_state);
fpi_ssm_mark_aborted(ssm, -1);
"in state %d", upekdev->seq, seq, fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_failed(ssm, -1);
} else {
fp_dbg("state %d completed", ssm->cur_state);
fp_dbg("state %d completed", fpi_ssm_get_cur_state(ssm));
fpi_ssm_next_state(ssm);
}
}
@@ -559,7 +506,7 @@ static void read28_0b_cb(struct fp_dev *dev, enum read_msg_status status,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq,
initsm_read_msg_response_cb((fpi_ssm *) user_data, dev, status, seq,
0x0b, subcmd);
}
@@ -567,7 +514,7 @@ static void read28_0c_cb(struct fp_dev *dev, enum read_msg_status status,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq,
initsm_read_msg_response_cb((fpi_ssm *) user_data, dev, status, seq,
0x0c, subcmd);
}
@@ -575,7 +522,7 @@ static void read28_08_cb(struct fp_dev *dev, enum read_msg_status status,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq,
initsm_read_msg_response_cb((fpi_ssm *) user_data, dev, status, seq,
0x08, subcmd);
}
@@ -583,7 +530,7 @@ static void read28_07_cb(struct fp_dev *dev, enum read_msg_status status,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq,
initsm_read_msg_response_cb((fpi_ssm *) user_data, dev, status, seq,
0x07, subcmd);
}
@@ -591,30 +538,33 @@ static void read28_06_cb(struct fp_dev *dev, enum read_msg_status status,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
initsm_read_msg_response_cb((struct fpi_ssm *) user_data, status, seq,
initsm_read_msg_response_cb((fpi_ssm *) user_data, dev, status, seq,
0x06, subcmd);
}
static void initsm_read_msg_cmd_cb(struct fpi_ssm *ssm,
enum read_msg_status status, uint8_t expect_seq, uint8_t seq)
static void
initsm_read_msg_cmd_cb(fpi_ssm *ssm,
struct fp_dev *dev,
enum read_msg_status status,
uint8_t expect_seq,
uint8_t seq)
{
struct fp_dev *dev = ssm->dev;
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
if (status == READ_MSG_ERROR) {
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_mark_failed(ssm, -1);
return;
} else if (status != READ_MSG_CMD) {
fp_err("expected command, got %d seq=%x in state %d", status, seq,
ssm->cur_state);
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_failed(ssm, -1);
return;
}
upekdev->seq = seq;
if (seq != expect_seq) {
fp_err("expected seq=%x, got %x in state %d", expect_seq, seq,
ssm->cur_state);
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_failed(ssm, -1);
return;
}
@@ -625,114 +575,113 @@ static void read_msg05_cb(struct fp_dev *dev, enum read_msg_status status,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
initsm_read_msg_cmd_cb((struct fpi_ssm *) user_data, status, 5, seq);
initsm_read_msg_cmd_cb((fpi_ssm *) user_data, dev, status, 5, seq);
}
static void read_msg03_cb(struct fp_dev *dev, enum read_msg_status status,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
initsm_read_msg_cmd_cb((struct fpi_ssm *) user_data, status, 3, seq);
initsm_read_msg_cmd_cb((fpi_ssm *) user_data, dev, status, 3, seq);
}
static void ctrl400_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
fpi_ssm *ssm = transfer->user_data;
/* FIXME check length? */
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_next_state(ssm);
else
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_mark_failed(ssm, -1);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void initsm_read_msg_handler(struct fpi_ssm *ssm,
read_msg_cb_fn callback)
static void
initsm_read_msg_handler(fpi_ssm *ssm,
struct fp_dev *dev,
read_msg_cb_fn callback)
{
int r = read_msg_async(ssm->dev, callback, ssm);
int r = read_msg_async(dev, callback, ssm);
if (r < 0) {
fp_err("async read msg failed in state %d", ssm->cur_state);
fpi_ssm_mark_aborted(ssm, r);
fp_err("async read msg failed in state %d", fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_failed(ssm, r);
}
}
static void initsm_send_msg_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED
&& transfer->length == transfer->actual_length) {
fp_dbg("state %d completed", ssm->cur_state);
fp_dbg("state %d completed", fpi_ssm_get_cur_state(ssm));
fpi_ssm_next_state(ssm);
} else {
fp_err("failed, state=%d rqlength=%d actual_length=%d", ssm->cur_state,
fp_err("failed, state=%d rqlength=%d actual_length=%d", fpi_ssm_get_cur_state(ssm),
transfer->length, transfer->actual_length);
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_mark_failed(ssm, -1);
}
libusb_free_transfer(transfer);
}
static void initsm_send_msg28_handler(struct fpi_ssm *ssm,
unsigned char subcmd, const unsigned char *data, uint16_t innerlen)
static void
initsm_send_msg28_handler(fpi_ssm *ssm,
struct fp_dev *dev,
unsigned char subcmd,
const unsigned char *data,
uint16_t innerlen)
{
struct fp_dev *dev = ssm->dev;
struct libusb_transfer *transfer;
int r;
transfer = alloc_send_cmd28_transfer(dev, subcmd, data, innerlen,
initsm_send_msg_cb, ssm);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
fpi_ssm_mark_failed(ssm, -ENOMEM);
return;
}
r = libusb_submit_transfer(transfer);
if (r < 0) {
fp_err("urb submission failed error %d in state %d", r, ssm->cur_state);
fp_err("urb submission failed error %d in state %d", r, fpi_ssm_get_cur_state(ssm));
g_free(transfer->buffer);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
}
}
static void initsm_run_state(struct fpi_ssm *ssm)
static void initsm_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
struct fp_dev *dev = ssm->dev;
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
struct libusb_transfer *transfer;
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_CTRL400: ;
unsigned char *data;
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
transfer = fpi_usb_alloc();
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE + 1);
libusb_fill_control_setup(data,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, 0x0c, 0x100, 0x0400, 1);
libusb_fill_control_transfer(transfer, ssm->dev->udev, data,
libusb_fill_control_transfer(transfer, fpi_dev_get_usb_dev(dev), data,
ctrl400_cb, ssm, TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
}
break;
case READ_MSG03:
initsm_read_msg_handler(ssm, read_msg03_cb);
initsm_read_msg_handler(ssm, dev, read_msg03_cb);
break;
case SEND_RESP03: ;
transfer = alloc_send_cmdresponse_transfer(dev, ++upekdev->seq,
init_resp03, sizeof(init_resp03), initsm_send_msg_cb, ssm);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
fpi_ssm_mark_failed(ssm, -ENOMEM);
break;
}
@@ -740,51 +689,52 @@ static void initsm_run_state(struct fpi_ssm *ssm)
if (r < 0) {
g_free(transfer->buffer);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
}
break;
case READ_MSG05:
initsm_read_msg_handler(ssm, read_msg05_cb);
initsm_read_msg_handler(ssm, dev, read_msg05_cb);
break;
case SEND28_06: ;
unsigned char dummy28_06 = 0x04;
upekdev->seq = 0xf0;
initsm_send_msg28_handler(ssm, 0x06, &dummy28_06, 1);
initsm_send_msg28_handler(ssm, dev, 0x06, &dummy28_06, 1);
break;
case READ28_06:
initsm_read_msg_handler(ssm, read28_06_cb);
initsm_read_msg_handler(ssm, dev, read28_06_cb);
break;
case SEND28_07: ;
unsigned char dummy28_07 = 0x04;
initsm_send_msg28_handler(ssm, 0x07, &dummy28_07, 1);
initsm_send_msg28_handler(ssm, dev, 0x07, &dummy28_07, 1);
break;
case READ28_07:
initsm_read_msg_handler(ssm, read28_07_cb);
initsm_read_msg_handler(ssm, dev, read28_07_cb);
break;
case SEND28_08:
initsm_send_msg28_handler(ssm, 0x08, init28_08, sizeof(init28_08));
initsm_send_msg28_handler(ssm, dev, 0x08, init28_08, sizeof(init28_08));
break;
case READ28_08:
initsm_read_msg_handler(ssm, read28_08_cb);
initsm_read_msg_handler(ssm, dev, read28_08_cb);
break;
case SEND28_0C:
initsm_send_msg28_handler(ssm, 0x0c, init28_0c, sizeof(init28_0c));
initsm_send_msg28_handler(ssm, dev, 0x0c, init28_0c, sizeof(init28_0c));
break;
case READ28_0C:
initsm_read_msg_handler(ssm, read28_0c_cb);
initsm_read_msg_handler(ssm, dev, read28_0c_cb);
break;
case SEND28_0B:
initsm_send_msg28_handler(ssm, 0x0b, init28_0b, sizeof(init28_0b));
initsm_send_msg28_handler(ssm, dev, 0x0b, init28_0b, sizeof(init28_0b));
break;
case READ28_0B:
initsm_read_msg_handler(ssm, read28_0b_cb);
initsm_read_msg_handler(ssm, dev, read28_0b_cb);
break;
}
}
static struct fpi_ssm *initsm_new(struct fp_dev *dev)
static fpi_ssm *initsm_new(struct fp_dev *dev,
void *user_data)
{
return fpi_ssm_new(dev, initsm_run_state, INITSM_NUM_STATES);
return fpi_ssm_new(dev, initsm_run_state, INITSM_NUM_STATES, user_data);
}
enum deinitsm_states {
@@ -795,11 +745,11 @@ enum deinitsm_states {
static void send_resp07_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
else if (transfer->length != transfer->actual_length)
fpi_ssm_mark_aborted(ssm, -EPROTO);
fpi_ssm_mark_failed(ssm, -EPROTO);
else
fpi_ssm_next_state(ssm);
libusb_free_transfer(transfer);
@@ -809,33 +759,32 @@ static void read_msg01_cb(struct fp_dev *dev, enum read_msg_status status,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct upekts_dev *upekdev = dev->priv;
fpi_ssm *ssm = user_data;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
if (status == READ_MSG_ERROR) {
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_mark_failed(ssm, -1);
return;
} else if (status != READ_MSG_CMD) {
fp_err("expected command, got %d seq=%x", status, seq);
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_mark_failed(ssm, -1);
return;
}
upekdev->seq = seq;
if (seq != 1) {
fp_err("expected seq=1, got %x", seq);
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_mark_failed(ssm, -1);
return;
}
fpi_ssm_next_state(ssm);
}
static void deinitsm_state_handler(struct fpi_ssm *ssm)
static void deinitsm_state_handler(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
struct fp_dev *dev = ssm->dev;
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case SEND_RESP07: ;
struct libusb_transfer *transfer;
unsigned char dummy = 0;
@@ -843,7 +792,7 @@ static void deinitsm_state_handler(struct fpi_ssm *ssm)
transfer = alloc_send_cmdresponse_transfer(dev, 0x07, &dummy, 1,
send_resp07_cb, ssm);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
fpi_ssm_mark_failed(ssm, -ENOMEM);
break;
}
@@ -851,20 +800,20 @@ static void deinitsm_state_handler(struct fpi_ssm *ssm)
if (r < 0) {
g_free(transfer->buffer);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
}
break;
case READ_MSG01: ;
r = read_msg_async(dev, read_msg01_cb, ssm);
if (r < 0)
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
break;
}
}
static struct fpi_ssm *deinitsm_new(struct fp_dev *dev)
static fpi_ssm *deinitsm_new(struct fp_dev *dev)
{
return fpi_ssm_new(dev, deinitsm_state_handler, DEINITSM_NUM_STATES);
return fpi_ssm_new(dev, deinitsm_state_handler, DEINITSM_NUM_STATES, NULL);
}
static int dev_init(struct fp_dev *dev, unsigned long driver_data)
@@ -872,14 +821,16 @@ static int dev_init(struct fp_dev *dev, unsigned long driver_data)
struct upekts_dev *upekdev = NULL;
int r;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
r = libusb_claim_interface(fpi_dev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
upekdev = g_malloc(sizeof(*upekdev));
upekdev->seq = 0xf0; /* incremented to 0x00 before first cmd */
dev->priv = upekdev;
dev->nr_enroll_stages = 3;
fp_dev_set_instance_data(dev, upekdev);
fpi_dev_set_nr_enroll_stages(dev, 3);
fpi_drvcb_open_complete(dev, 0);
return 0;
@@ -887,8 +838,10 @@ static int dev_init(struct fp_dev *dev, unsigned long driver_data)
static void dev_exit(struct fp_dev *dev)
{
libusb_release_interface(dev->udev, 0);
g_free(dev->priv);
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
libusb_release_interface(fpi_dev_get_usb_dev(dev), 0);
g_free(upekdev);
fpi_drvcb_close_complete(dev);
}
@@ -910,14 +863,14 @@ enum enroll_start_sm_states {
};
/* Called when the device initialization state machine completes */
static void enroll_start_sm_cb_initsm(struct fpi_ssm *initsm)
static void enroll_start_sm_cb_initsm(fpi_ssm *initsm, struct fp_dev *_dev, void *user_data)
{
struct fpi_ssm *enroll_start_ssm = initsm->priv;
int error = initsm->error;
fpi_ssm *enroll_start_ssm = user_data;
int error = fpi_ssm_get_error(initsm);
fpi_ssm_free(initsm);
if (error)
fpi_ssm_mark_aborted(enroll_start_ssm, error);
fpi_ssm_mark_failed(enroll_start_ssm, error);
else
fpi_ssm_next_state(enroll_start_ssm);
}
@@ -925,11 +878,11 @@ static void enroll_start_sm_cb_initsm(struct fpi_ssm *initsm)
/* called when enroll init URB has completed */
static void enroll_start_sm_cb_init(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
else if (transfer->length != transfer->actual_length)
fpi_ssm_mark_aborted(ssm, -EPROTO);
fpi_ssm_mark_failed(ssm, -EPROTO);
else
fpi_ssm_next_state(ssm);
libusb_free_transfer(transfer);
@@ -939,34 +892,32 @@ static void enroll_start_sm_cb_msg28(struct fp_dev *dev,
enum read_msg_status status, uint8_t seq, unsigned char subcmd,
unsigned char *data, size_t data_len, void *user_data)
{
struct upekts_dev *upekdev = dev->priv;
struct fpi_ssm *ssm = user_data;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
fpi_ssm *ssm = user_data;
if (status != READ_MSG_RESPONSE) {
fp_err("expected response, got %d seq=%x", status, seq);
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_mark_failed(ssm, -1);
} else if (subcmd != 0) {
fp_warn("expected response to subcmd 0, got response to %02x",
subcmd);
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_mark_failed(ssm, -1);
} else if (seq != upekdev->seq) {
fp_err("expected response to cmd seq=%02x, got response to %02x",
upekdev->seq, seq);
fpi_ssm_mark_aborted(ssm, -1);
fpi_ssm_mark_failed(ssm, -1);
} else {
fpi_ssm_next_state(ssm);
}
}
static void enroll_start_sm_run_state(struct fpi_ssm *ssm)
static void enroll_start_sm_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
struct fp_dev *dev = ssm->dev;
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case RUN_INITSM: ;
struct fpi_ssm *initsm = initsm_new(dev);
initsm->priv = ssm;
fpi_ssm *initsm = initsm_new(dev, ssm);
fpi_ssm_start(initsm, enroll_start_sm_cb_initsm);
break;
case ENROLL_INIT: ;
@@ -974,7 +925,7 @@ static void enroll_start_sm_run_state(struct fpi_ssm *ssm)
transfer = alloc_send_cmd28_transfer(dev, 0x02, enroll_init,
sizeof(enroll_init), enroll_start_sm_cb_init, ssm);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
fpi_ssm_mark_failed(ssm, -ENOMEM);
break;
}
@@ -982,7 +933,7 @@ static void enroll_start_sm_run_state(struct fpi_ssm *ssm)
if (r < 0) {
g_free(transfer->buffer);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
}
break;
case READ_ENROLL_MSG28: ;
@@ -992,7 +943,7 @@ static void enroll_start_sm_run_state(struct fpi_ssm *ssm)
* include a 30 01 poll somewhere? */
r = read_msg_async(dev, enroll_start_sm_cb_msg28, ssm);
if (r < 0)
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
break;
}
}
@@ -1002,12 +953,12 @@ static void enroll_iterate(struct fp_dev *dev);
static void e_handle_resp00(struct fp_dev *dev, unsigned char *data,
size_t data_len)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
unsigned char status;
int result = 0;
if (data_len != 14) {
fp_err("received 3001 poll response of %d bytes?", data_len);
fp_err("received 3001 poll response of %lu bytes?", data_len);
fpi_drvcb_enroll_stage_completed(dev, -EPROTO, NULL, NULL);
return;
}
@@ -1019,6 +970,9 @@ static void e_handle_resp00(struct fp_dev *dev, unsigned char *data,
case 0x0c:
case 0x0d:
case 0x0e:
case 0x26:
case 0x27:
case 0x2e:
/* if we previously completed a non-last enrollment stage, we'll
* get this code to indicate successful stage completion */
if (upekdev->enroll_passed) {
@@ -1075,17 +1029,20 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
size_t data_len)
{
struct fp_print_data *fdata = NULL;
struct fp_print_data_item *item = NULL;
int result = -EPROTO;
if (data_len < sizeof(scan_comp)) {
fp_err("fingerprint data too short (%d bytes)", data_len);
fp_err("fingerprint data too short (%lu bytes)", data_len);
} else if (memcmp(data, scan_comp, sizeof(scan_comp)) != 0) {
fp_err("unrecognised data prefix %x %x %x %x %x",
data[0], data[1], data[2], data[3], data[4]);
} else {
fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp));
memcpy(fdata->data, data + sizeof(scan_comp),
fdata = fpi_print_data_new(dev);
item = fpi_print_data_item_new(data_len - sizeof(scan_comp));
memcpy(item->data, data + sizeof(scan_comp),
data_len - sizeof(scan_comp));
fpi_print_data_add_item(fdata, item);
result = FP_ENROLL_COMPLETE;
}
@@ -1148,12 +1105,11 @@ static void enroll_iterate(struct fp_dev *dev)
}
}
static void enroll_started(struct fpi_ssm *ssm)
static void enroll_started(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
struct fp_dev *dev = ssm->dev;
fpi_drvcb_enroll_started(dev, ssm->error);
fpi_drvcb_enroll_started(dev, fpi_ssm_get_error(ssm));
if (!ssm->error)
if (!fpi_ssm_get_error(ssm))
enroll_iterate(dev);
fpi_ssm_free(ssm);
@@ -1161,41 +1117,41 @@ static void enroll_started(struct fpi_ssm *ssm)
static int enroll_start(struct fp_dev *dev)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
/* do_init state machine first */
struct fpi_ssm *ssm = fpi_ssm_new(dev, enroll_start_sm_run_state,
ENROLL_START_NUM_STATES);
fpi_ssm *ssm = fpi_ssm_new(dev, enroll_start_sm_run_state,
ENROLL_START_NUM_STATES, NULL);
upekdev->enroll_passed = FALSE;
fpi_ssm_start(ssm, enroll_started);
return 0;
}
static void enroll_stop_deinit_cb(struct fpi_ssm *ssm)
static void enroll_stop_deinit_cb(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
/* don't really care about errors */
fpi_drvcb_enroll_stopped(ssm->dev);
fpi_drvcb_enroll_stopped(dev);
fpi_ssm_free(ssm);
}
static int enroll_stop(struct fp_dev *dev)
{
struct fpi_ssm *ssm = deinitsm_new(dev);
fpi_ssm *ssm = deinitsm_new(dev);
fpi_ssm_start(ssm, enroll_stop_deinit_cb);
return 0;
}
static void verify_stop_deinit_cb(struct fpi_ssm *ssm)
static void verify_stop_deinit_cb(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
/* don't really care about errors */
fpi_drvcb_verify_stopped(ssm->dev);
fpi_drvcb_verify_stopped(dev);
fpi_ssm_free(ssm);
}
static void do_verify_stop(struct fp_dev *dev)
{
struct fpi_ssm *ssm = deinitsm_new(dev);
fpi_ssm *ssm = deinitsm_new(dev);
fpi_ssm_start(ssm, verify_stop_deinit_cb);
}
@@ -1212,11 +1168,14 @@ enum {
};
/* Called when the device initialization state machine completes */
static void verify_start_sm_cb_initsm(struct fpi_ssm *initsm)
static void verify_start_sm_cb_initsm(fpi_ssm *initsm, struct fp_dev *_dev, void *user_data)
{
struct fpi_ssm *verify_start_ssm = initsm->priv;
if (initsm->error)
fpi_ssm_mark_aborted(verify_start_ssm, initsm->error);
fpi_ssm *verify_start_ssm = user_data;
int err;
err = fpi_ssm_get_error(initsm);
if (err)
fpi_ssm_mark_failed(verify_start_ssm, err);
else
fpi_ssm_next_state(verify_start_ssm);
fpi_ssm_free(initsm);
@@ -1224,40 +1183,39 @@ static void verify_start_sm_cb_initsm(struct fpi_ssm *initsm)
static void verify_init_2803_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
else if (transfer->length != transfer->actual_length)
fpi_ssm_mark_aborted(ssm, -EPROTO);
fpi_ssm_mark_failed(ssm, -EPROTO);
else
fpi_ssm_next_state(ssm);
libusb_free_transfer(transfer);
}
static void verify_start_sm_run_state(struct fpi_ssm *ssm)
static void verify_start_sm_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
struct fp_dev *dev = ssm->dev;
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case VERIFY_RUN_INITSM: ;
struct fpi_ssm *initsm = initsm_new(dev);
initsm->priv = ssm;
fpi_ssm *initsm = initsm_new(dev, ssm);
fpi_ssm_start(initsm, verify_start_sm_cb_initsm);
break;
case VERIFY_INIT: ;
struct fp_print_data *print = dev->verify_data;
size_t data_len = sizeof(verify_hdr) + print->length;
struct fp_print_data *print = fpi_dev_get_verify_data(dev);
struct fp_print_data_item *item = fpi_print_data_get_item(print);
size_t data_len = sizeof(verify_hdr) + item->length;
unsigned char *data = g_malloc(data_len);
struct libusb_transfer *transfer;
memcpy(data, verify_hdr, sizeof(verify_hdr));
memcpy(data + sizeof(verify_hdr), print->data, print->length);
memcpy(data + sizeof(verify_hdr), item->data, item->length);
transfer = alloc_send_cmd28_transfer(dev, 0x03, data, data_len,
verify_init_2803_cb, ssm);
g_free(data);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
fpi_ssm_mark_failed(ssm, -ENOMEM);
break;
}
@@ -1265,7 +1223,7 @@ static void verify_start_sm_run_state(struct fpi_ssm *ssm)
if (r < 0) {
g_free(transfer->buffer);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
}
break;
}
@@ -1280,7 +1238,7 @@ static void v_handle_resp00(struct fp_dev *dev, unsigned char *data,
int r = 0;
if (data_len != 14) {
fp_err("received 3001 poll response of %d bytes?", data_len);
fp_err("received 3001 poll response of %lu bytes?", data_len);
r = -EPROTO;
goto out;
}
@@ -1351,7 +1309,7 @@ static void verify_rd2800_cb(struct fp_dev *dev, enum read_msg_status msgstat,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
if (msgstat != READ_MSG_RESPONSE) {
fp_err("expected response, got %d seq=%x", msgstat, seq);
@@ -1390,7 +1348,7 @@ static void verify_wr2800_cb(struct libusb_transfer *transfer)
static void verify_iterate(struct fp_dev *dev)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
if (upekdev->stop_verify) {
do_verify_stop(dev);
@@ -1423,13 +1381,12 @@ static void verify_iterate(struct fp_dev *dev)
}
}
static void verify_started(struct fpi_ssm *ssm)
static void verify_started(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
{
struct fp_dev *dev = ssm->dev;
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
fpi_drvcb_verify_started(dev, ssm->error);
if (!ssm->error) {
fpi_drvcb_verify_started(dev, fpi_ssm_get_error(ssm));
if (!fpi_ssm_get_error(ssm)) {
upekdev->first_verify_iteration = TRUE;
verify_iterate(dev);
}
@@ -1439,9 +1396,9 @@ static void verify_started(struct fpi_ssm *ssm)
static int verify_start(struct fp_dev *dev)
{
struct upekts_dev *upekdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev, verify_start_sm_run_state,
VERIFY_NUM_STATES);
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
fpi_ssm *ssm = fpi_ssm_new(dev, verify_start_sm_run_state,
VERIFY_NUM_STATES, NULL);
upekdev->stop_verify = FALSE;
fpi_ssm_start(ssm, verify_started);
return 0;
@@ -1449,7 +1406,7 @@ static int verify_start(struct fp_dev *dev)
static int verify_stop(struct fp_dev *dev, gboolean iterating)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = FP_INSTANCE_DATA(dev);
if (!iterating)
do_verify_stop(dev);
@@ -1464,7 +1421,7 @@ static const struct usb_id id_table[] = {
};
struct fp_driver upekts_driver = {
.id = 1,
.id = UPEKTS_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchStrip",
.id_table = id_table,

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,8 @@
#define FP_COMPONENT "vcom5s"
#include "drivers_api.h"
/* TODO:
* calibration?
* image size: windows gets 300x300 through vpas enrollment util?
@@ -26,14 +28,6 @@
* powerdown? does windows do anything special on exit?
*/
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <libusb.h>
#include <fp_internal.h>
#define CTRL_IN 0xc0
#define CTRL_OUT 0x40
#define CTRL_TIMEOUT 1000
@@ -80,10 +74,10 @@ enum v5s_cmd {
static void sm_write_reg_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
else
fpi_ssm_next_state(ssm);
@@ -91,38 +85,35 @@ static void sm_write_reg_cb(struct libusb_transfer *transfer)
libusb_free_transfer(transfer);
}
static void sm_write_reg(struct fpi_ssm *ssm, unsigned char reg,
unsigned char value)
static void
sm_write_reg(fpi_ssm *ssm,
struct fp_img_dev *dev,
unsigned char reg,
unsigned char value)
{
struct fp_img_dev *dev = ssm->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
fp_dbg("set %02x=%02x", reg, value);
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(data, CTRL_OUT, reg, value, 0, 0);
libusb_fill_control_transfer(transfer, dev->udev, data, sm_write_reg_cb,
libusb_fill_control_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), data, sm_write_reg_cb,
ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
}
}
static void sm_exec_cmd_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
else
fpi_ssm_next_state(ssm);
@@ -130,29 +121,26 @@ static void sm_exec_cmd_cb(struct libusb_transfer *transfer)
libusb_free_transfer(transfer);
}
static void sm_exec_cmd(struct fpi_ssm *ssm, unsigned char cmd,
unsigned char param)
static void
sm_exec_cmd(fpi_ssm *ssm,
struct fp_img_dev *dev,
unsigned char cmd,
unsigned char param)
{
struct fp_img_dev *dev = ssm->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
fp_dbg("cmd %02x param %02x", cmd, param);
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(data, CTRL_IN, cmd, param, 0, 0);
libusb_fill_control_transfer(transfer, dev->udev, data, sm_exec_cmd_cb,
libusb_fill_control_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), data, sm_exec_cmd_cb,
ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
}
}
@@ -193,16 +181,16 @@ static gboolean finger_is_present(unsigned char *data)
/***** IMAGE ACQUISITION *****/
static void capture_iterate(struct fpi_ssm *ssm);
static void capture_iterate(fpi_ssm *ssm, struct fp_img_dev *dev);
static void capture_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_aborted(ssm, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
@@ -217,47 +205,44 @@ static void capture_cb(struct libusb_transfer *transfer)
fpi_imgdev_image_captured(dev, img);
fpi_ssm_next_state(ssm);
} else {
capture_iterate(ssm);
capture_iterate(ssm, dev);
}
out:
libusb_free_transfer(transfer);
}
static void capture_iterate(struct fpi_ssm *ssm)
static void
capture_iterate(fpi_ssm *ssm,
struct fp_img_dev *dev)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
int iteration = vdev->capture_iteration;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct libusb_transfer *transfer = fpi_usb_alloc();
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN,
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN,
vdev->capture_img->data + (RQ_SIZE * iteration), RQ_SIZE,
capture_cb, ssm, CTRL_TIMEOUT);
transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
fpi_ssm_mark_failed(ssm, r);
}
}
static void sm_do_capture(struct fpi_ssm *ssm)
static void
sm_do_capture(fpi_ssm *ssm,
struct fp_img_dev *dev)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
fp_dbg("");
G_DEBUG_HERE();
vdev->capture_img = fpi_img_new_for_imgdev(dev);
vdev->capture_iteration = 0;
capture_iterate(ssm);
capture_iterate(ssm, dev);
}
/***** CAPTURE LOOP *****/
@@ -271,27 +256,27 @@ enum loop_states {
LOOP_NUM_STATES,
};
static void loop_run_state(struct fpi_ssm *ssm)
static void loop_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
struct fp_img_dev *dev = user_data;
struct v5s_dev *vdev = FP_INSTANCE_DATA(_dev);
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case LOOP_SET_CONTRAST:
sm_write_reg(ssm, REG_CONTRAST, 0x01);
sm_write_reg(ssm, dev, REG_CONTRAST, 0x01);
break;
case LOOP_SET_GAIN:
sm_write_reg(ssm, REG_GAIN, 0x29);
sm_write_reg(ssm, dev, REG_GAIN, 0x29);
break;
case LOOP_CMD_SCAN:
if (vdev->deactivating) {
fp_dbg("deactivating, marking completed");
fpi_ssm_mark_completed(ssm);
} else
sm_exec_cmd(ssm, CMD_SCAN, 0x00);
sm_exec_cmd(ssm, dev, CMD_SCAN, 0x00);
break;
case LOOP_CAPTURE:
sm_do_capture(ssm);
sm_do_capture(ssm, dev);
break;
case LOOP_CAPTURE_DONE:
fpi_ssm_jump_to_state(ssm, LOOP_CMD_SCAN);
@@ -299,11 +284,11 @@ static void loop_run_state(struct fpi_ssm *ssm)
}
}
static void loopsm_complete(struct fpi_ssm *ssm)
static void loopsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
int r = ssm->error;
struct fp_img_dev *dev = user_data;
struct v5s_dev *vdev = FP_INSTANCE_DATA(_dev);
int r = fpi_ssm_get_error(ssm);
fpi_ssm_free(ssm);
fp_img_free(vdev->capture_img);
@@ -317,12 +302,11 @@ static void loopsm_complete(struct fpi_ssm *ssm)
fpi_imgdev_deactivate_complete(dev);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *dev)
{
struct v5s_dev *vdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, loop_run_state,
LOOP_NUM_STATES);
ssm->priv = dev;
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), loop_run_state,
LOOP_NUM_STATES, dev);
vdev->deactivating = FALSE;
fpi_ssm_start(ssm, loopsm_complete);
vdev->loop_running = TRUE;
@@ -332,7 +316,7 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static void dev_deactivate(struct fp_img_dev *dev)
{
struct v5s_dev *vdev = dev->priv;
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (vdev->loop_running)
vdev->deactivating = TRUE;
else
@@ -342,11 +326,14 @@ static void dev_deactivate(struct fp_img_dev *dev)
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
dev->priv = g_malloc0(sizeof(struct v5s_dev));
struct v5s_dev *v5s_dev;
r = libusb_claim_interface(dev->udev, 0);
v5s_dev = g_malloc0(sizeof(struct v5s_dev));
fp_dev_set_instance_data(FP_DEV(dev), v5s_dev);
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0)
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
if (r == 0)
fpi_imgdev_open_complete(dev, 0);
@@ -356,8 +343,10 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
static void dev_deinit(struct fp_img_dev *dev)
{
g_free(dev->priv);
libusb_release_interface(dev->udev, 0);
struct v5s_dev *v5s_dev;
v5s_dev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(v5s_dev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
@@ -368,7 +357,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver vcom5s_driver = {
.driver = {
.id = 8,
.id = VCOM5S_ID,
.name = FP_COMPONENT,
.full_name = "Veridicom 5thSense",
.id_table = id_table,

791
libfprint/drivers/vfs0050.c Normal file
View File

@@ -0,0 +1,791 @@
/*
* Validity VFS0050 driver for libfprint
* Copyright (C) 2015-2016 Konstantin Semenov <zemen17@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 "vfs0050"
#include "drivers_api.h"
#include "vfs0050.h"
/* USB functions */
/* Callback for async_write */
static void async_write_callback(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
int transferred = transfer->actual_length, error =
transfer->status, len = transfer->length;
if (error != 0) {
fp_err("USB write transfer: %s", libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
if (transferred != len) {
fp_err("Written only %d of %d bytes", transferred, len);
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
fpi_ssm_next_state(ssm);
}
/* Send data to EP1, the only out endpoint */
static void
async_write(fpi_ssm *ssm,
struct fp_img_dev *dev,
void *data,
int len)
{
struct libusb_device_handle *usb_dev = fpi_dev_get_usb_dev(FP_DEV(dev));
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
vdev->transfer = fpi_usb_alloc();
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_bulk_transfer(vdev->transfer, usb_dev, 0x01, data, len,
async_write_callback, ssm, VFS_USB_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
}
/* Callback for async_read */
static void async_read_callback(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
int transferred = transfer->actual_length, error =
transfer->status, len = transfer->length;
int ep = transfer->endpoint;
if (error != 0) {
fp_err("USB read transfer on endpoint %d: %s", ep - 0x80,
libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
if (transferred != len) {
fp_err("Received %d instead of %d bytes", transferred, len);
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
fpi_ssm_next_state(ssm);
}
/* Receive data from the given ep and compare with expected */
static void
async_read(fpi_ssm *ssm,
struct fp_img_dev *dev,
int ep,
void *data,
int len)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct libusb_device_handle *usb_dev = fpi_dev_get_usb_dev(FP_DEV(idev));
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
ep |= LIBUSB_ENDPOINT_IN;
vdev->transfer = fpi_usb_alloc();
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
/* 0x83 is the only interrupt endpoint */
if (ep == EP3_IN)
libusb_fill_interrupt_transfer(vdev->transfer, usb_dev, ep, data,
len, async_read_callback, ssm,
VFS_USB_TIMEOUT);
else
libusb_fill_bulk_transfer(vdev->transfer, usb_dev, ep, data, len,
async_read_callback, ssm,
VFS_USB_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
}
/* Callback for async_read */
static void async_abort_callback(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
int transferred = transfer->actual_length, error = transfer->status;
int ep = transfer->endpoint;
/* In normal case endpoint is empty */
if (error == LIBUSB_TRANSFER_TIMED_OUT) {
fpi_ssm_next_state(ssm);
return;
}
if (error != 0) {
fp_err("USB write transfer: %s", libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
/* Don't stop process, only print warning */
if (transferred > 0)
fp_warn("Endpoint %d had extra %d bytes", ep - 0x80,
transferred);
fpi_ssm_jump_to_state(ssm, fpi_ssm_get_cur_state(ssm));
}
/* Receive data from the given ep and compare with expected */
static void async_abort(fpi_ssm *ssm, int ep)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct libusb_device_handle *usb_dev = fpi_dev_get_usb_dev(FP_DEV(idev));
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
int len = VFS_USB_BUFFER_SIZE;
unsigned char *data = g_malloc(VFS_USB_BUFFER_SIZE);
ep |= LIBUSB_ENDPOINT_IN;
vdev->transfer = fpi_usb_alloc();
vdev->transfer->flags |=
LIBUSB_TRANSFER_FREE_TRANSFER | LIBUSB_TRANSFER_FREE_BUFFER;
/* 0x83 is the only interrupt endpoint */
if (ep == EP3_IN)
libusb_fill_interrupt_transfer(vdev->transfer, usb_dev, ep, data,
len, async_abort_callback, ssm,
VFS_USB_ABORT_TIMEOUT);
else
libusb_fill_bulk_transfer(vdev->transfer, usb_dev, ep, data, len,
async_abort_callback, ssm,
VFS_USB_ABORT_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
}
/* Image processing functions */
/* Pixel getter for fpi_assemble_lines */
static unsigned char vfs0050_get_pixel(struct fpi_line_asmbl_ctx *ctx,
GSList * line, unsigned int x)
{
return ((struct vfs_line *)line->data)->data[x];
}
/* Deviation getter for fpi_assemble_lines */
static int vfs0050_get_difference(struct fpi_line_asmbl_ctx *ctx,
GSList * line_list_1, GSList * line_list_2)
{
struct vfs_line *line1 = line_list_1->data;
struct vfs_line *line2 = line_list_2->data;
const int shift = (VFS_IMAGE_WIDTH - VFS_NEXT_LINE_WIDTH) / 2 - 1;
int res = 0;
for (int i = 0; i < VFS_NEXT_LINE_WIDTH; ++i) {
int x =
(int)line1->next_line_part[i] - (int)line2->data[shift + i];
res += x * x;
}
return res;
}
#define VFS_NOISE_THRESHOLD 40
/* Checks whether line is noise or not using hardware parameters */
static char is_noise(struct vfs_line *line)
{
int val1 = line->noise_hash_1;
int val2 = line->noise_hash_2;
if (val1 > VFS_NOISE_THRESHOLD
&& val1 < 256 - VFS_NOISE_THRESHOLD
&& val2 > VFS_NOISE_THRESHOLD && val2 < 256 - VFS_NOISE_THRESHOLD)
return 1;
return 0;
}
/* Parameters for fpi_assemble_lines */
static struct fpi_line_asmbl_ctx assembling_ctx = {
.line_width = VFS_IMAGE_WIDTH,
.max_height = VFS_MAX_HEIGHT,
.resolution = 10,
.median_filter_size = 25,
.max_search_offset = 100,
.get_deviation = vfs0050_get_difference,
.get_pixel = vfs0050_get_pixel,
};
/* Processes image before submitting */
static struct fp_img *prepare_image(struct vfs_dev_t *vdev)
{
int height = vdev->bytes / VFS_LINE_SIZE;
/* Noise cleaning. IMHO, it works pretty well
I've not detected cases when it doesn't work or cuts a part of the finger
Noise arises at the end of scan when some water remains on the scanner */
while (height > 0) {
if (!is_noise(vdev->lines_buffer + height - 1))
break;
--height;
}
if (height > VFS_MAX_HEIGHT)
height = VFS_MAX_HEIGHT;
/* If image is not good enough */
if (height < VFS_IMAGE_WIDTH)
return NULL;
/* Building GSList */
GSList *lines = NULL;
for (int i = height - 1; i >= 0; --i)
lines = g_slist_prepend(lines, vdev->lines_buffer + i);
/* Perform line assembling */
struct fp_img *img = fpi_assemble_lines(&assembling_ctx, lines, height);
g_slist_free(lines);
return img;
}
/* Processes and submits image after fingerprint received */
static void submit_image(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
/* We were not asked to submit image actually */
if (!vdev->active)
return;
struct fp_img *img = prepare_image(vdev);
if (!img)
fpi_imgdev_abort_scan(idev, FP_VERIFY_RETRY_TOO_SHORT);
else
fpi_imgdev_image_captured(idev, img);
/* Finger not on the scanner */
fpi_imgdev_report_finger_status(idev, 0);
}
/* Proto functions */
/* SSM loop for clear_ep2 */
static void
clear_ep2_ssm(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *idev = user_data;
short result;
char command04 = 0x04;
switch (fpi_ssm_get_cur_state(ssm)) {
case SUBSM1_COMMAND_04:
async_write(ssm, idev, &command04, sizeof(command04));
break;
case SUBSM1_RETURN_CODE:
async_read(ssm, idev, 1, &result, sizeof(result));
break;
case SUBSM1_ABORT_2:
async_abort(ssm, 2);
break;
default:
fp_err("Unknown SUBSM1 state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
}
}
/* Send command to clear EP2 */
static void
clear_ep2(fpi_ssm *ssm,
struct fp_img_dev *idev)
{
fpi_ssm *subsm =
fpi_ssm_new(FP_DEV(idev), clear_ep2_ssm, SUBSM1_STATES, idev);
fpi_ssm_start_subsm(ssm, subsm);
}
static void send_control_packet_ssm(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *idev = user_data;
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(_dev);
short result;
unsigned char *commit_result = NULL;
switch (fpi_ssm_get_cur_state(ssm)) {
case SUBSM2_SEND_CONTROL:
async_write(ssm, idev, vdev->control_packet, VFS_CONTROL_PACKET_SIZE);
break;
case SUBSM2_RETURN_CODE:
async_read(ssm, idev, 1, &result, sizeof(result));
break;
case SUBSM2_SEND_COMMIT:
/* next_receive_* packets could be sent only in pair */
if (vdev->control_packet == next_receive_1) {
vdev->control_packet = next_receive_2;
fpi_ssm_jump_to_state(ssm, SUBSM2_SEND_CONTROL);
break;
}
/* commit_out in Windows differs in each commit, but I send the same each time */
async_write(ssm, idev, commit_out, sizeof(commit_out));
break;
case SUBSM2_COMMIT_RESPONSE:
commit_result = g_malloc(VFS_COMMIT_RESPONSE_SIZE);
async_read(ssm, idev, 1, commit_result, VFS_COMMIT_RESPONSE_SIZE);
break;
case SUBSM2_READ_EMPTY_INTERRUPT:
/* I don't know how to check result, it could be different */
g_free(commit_result);
async_read(ssm, idev, 3, vdev->interrupt, VFS_INTERRUPT_SIZE);
break;
case SUBSM2_ABORT_3:
/* Check that interrupt is empty */
if (memcmp
(vdev->interrupt, empty_interrupt, VFS_INTERRUPT_SIZE)) {
fp_err("Unknown SUBSM2 state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
break;
}
async_abort(ssm, 3);
break;
case SUBSM2_CLEAR_EP2:
/* After turn_on Windows doesn't clear EP2 */
if (vdev->control_packet != turn_on)
clear_ep2(ssm, idev);
else
fpi_ssm_next_state(ssm);
break;
default:
fp_err("Unknown SUBSM2 state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
}
}
/* Send device state control packet */
static void
send_control_packet(fpi_ssm *ssm,
struct fp_img_dev *idev)
{
fpi_ssm *subsm =
fpi_ssm_new(FP_DEV(idev), send_control_packet_ssm, SUBSM2_STATES, idev);
fpi_ssm_start_subsm(ssm, subsm);
}
/* Clears all fprint data */
static void clear_data(struct vfs_dev_t *vdev)
{
g_free(vdev->lines_buffer);
vdev->lines_buffer = NULL;
vdev->memory = vdev->bytes = 0;
}
/* After receiving interrupt from EP3 */
static void interrupt_callback(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
char *interrupt = vdev->interrupt;
int error = transfer->status, transferred = transfer->actual_length;
vdev->wait_interrupt = 0;
/* When we have cancelled transfer, error is ok actually */
if (!vdev->active && error == LIBUSB_TRANSFER_CANCELLED)
return;
if (error != 0) {
fp_err("USB read interrupt transfer: %s",
libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
/* Interrupt size is VFS_INTERRUPT_SIZE bytes in all known cases */
if (transferred != VFS_INTERRUPT_SIZE) {
fp_err("Unknown interrupt size %d", transferred);
/* Abort ssm */
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
/* Standard interrupts */
if (memcmp(interrupt, interrupt1, VFS_INTERRUPT_SIZE) == 0 ||
memcmp(interrupt, interrupt2, VFS_INTERRUPT_SIZE) == 0 ||
memcmp(interrupt, interrupt3, VFS_INTERRUPT_SIZE) == 0) {
/* Go to the next ssm stage */
fpi_ssm_next_state(ssm);
return;
}
/* When finger is on the scanner before turn_on */
if (interrupt[0] == 0x01) {
fp_warn("Finger is already on the scanner");
/* Go to the next ssm stage */
fpi_ssm_next_state(ssm);
return;
}
/* Unknown interrupt; abort the session */
fp_err("Unknown interrupt '%02x:%02x:%02x:%02x:%02x'!",
interrupt[0] & 0xff, interrupt[1] & 0xff, interrupt[2] & 0xff,
interrupt[3] & 0xff, interrupt[4] & 0xff);
/* Abort ssm */
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
}
static void receive_callback(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
int transferred = transfer->actual_length, error = transfer->status;
if (error != 0 && error != LIBUSB_TRANSFER_TIMED_OUT) {
fp_err("USB read transfer: %s", libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
return;
}
/* Check if fingerprint data is over */
if (transferred == 0) {
fpi_ssm_next_state(ssm);
} else {
vdev->bytes += transferred;
/* We need more data */
fpi_ssm_jump_to_state(ssm, fpi_ssm_get_cur_state(ssm));
}
}
/* Stub to keep SSM alive when waiting an interrupt */
static void
wait_interrupt(struct fp_dev *dev,
void *data)
{
fpi_ssm *ssm = data;
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(dev);
/* Keep sleeping while this flag is on */
if (vdev->wait_interrupt)
fpi_ssm_jump_to_state(ssm, fpi_ssm_get_cur_state(ssm));
}
/* SSM stub to prepare device to another scan after orange light was on */
static void
another_scan(struct fp_dev *dev,
void *data)
{
fpi_ssm *ssm = data;
fpi_ssm_jump_to_state(ssm, SSM_TURN_ON);
}
/* Main SSM loop */
static void activate_ssm(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *idev = user_data;
struct libusb_device_handle *usb_dev = fpi_dev_get_usb_dev(FP_DEV(idev));
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case SSM_INITIAL_ABORT_1:
async_abort(ssm, 1);
break;
case SSM_INITIAL_ABORT_2:
async_abort(ssm, 2);
break;
case SSM_INITIAL_ABORT_3:
async_abort(ssm, 3);
break;
case SSM_CLEAR_EP2:
clear_ep2(ssm, idev);
break;
case SSM_TURN_OFF:
/* Set control_packet argument */
vdev->control_packet = turn_off;
send_control_packet(ssm, idev);
break;
case SSM_TURN_ON:
if (!vdev->active) {
/* The only correct exit */
fpi_ssm_mark_completed(ssm);
if (vdev->need_report) {
fpi_imgdev_deactivate_complete(idev);
vdev->need_report = 0;
}
break;
}
/* Set control_packet argument */
vdev->control_packet = turn_on;
send_control_packet(ssm, idev);
break;
case SSM_ASK_INTERRUPT:
/* Activated, light must be blinking now */
/* If we first time here, report that activate completed */
if (vdev->need_report) {
fpi_imgdev_activate_complete(idev, 0);
vdev->need_report = 0;
}
/* Asynchronously enquire an interrupt */
vdev->transfer = fpi_usb_alloc();
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_interrupt_transfer(vdev->transfer, usb_dev, 0x83,
vdev->interrupt,
VFS_INTERRUPT_SIZE,
interrupt_callback, ssm, 0);
libusb_submit_transfer(vdev->transfer);
/* This flag could be turned off only in callback function */
vdev->wait_interrupt = 1;
/* I've put it here to be sure that data is cleared */
clear_data(vdev);
fpi_ssm_next_state(ssm);
break;
case SSM_WAIT_INTERRUPT:
/* Check if user had interrupted the process */
if (!vdev->active) {
libusb_cancel_transfer(vdev->transfer);
fpi_ssm_jump_to_state(ssm, SSM_CLEAR_EP2);
break;
}
if (vdev->wait_interrupt)
fpi_timeout_add(VFS_SSM_TIMEOUT, wait_interrupt, _dev, ssm);
break;
case SSM_RECEIVE_FINGER:
if (vdev->memory == 0) {
/* Initialize fingerprint buffer */
g_free(vdev->lines_buffer);
vdev->memory = VFS_USB_BUFFER_SIZE;
vdev->lines_buffer = g_malloc(vdev->memory);
vdev->bytes = 0;
/* Finger is on the scanner */
fpi_imgdev_report_finger_status(idev, 1);
}
/* Increase buffer size while it's insufficient */
while (vdev->bytes + VFS_USB_BUFFER_SIZE > vdev->memory) {
vdev->memory <<= 1;
vdev->lines_buffer =
(struct vfs_line *)g_realloc(vdev->lines_buffer,
vdev->memory);
}
/* Receive chunk of data */
vdev->transfer = fpi_usb_alloc();
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_bulk_transfer(vdev->transfer, usb_dev, 0x82,
(void *)vdev->lines_buffer +
vdev->bytes, VFS_USB_BUFFER_SIZE,
receive_callback, ssm,
VFS_USB_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
break;
case SSM_SUBMIT_IMAGE:
submit_image(idev);
clear_data(vdev);
/* Wait for probable vdev->active changing */
fpi_timeout_add(VFS_SSM_TIMEOUT, fpi_ssm_next_state_timeout_cb, _dev, ssm);
break;
case SSM_NEXT_RECEIVE:
if (!vdev->active) {
/* It's the last scan */
fpi_ssm_jump_to_state(ssm, SSM_CLEAR_EP2);
break;
}
/* Set control_packet argument */
vdev->control_packet = next_receive_1;
send_control_packet(ssm, idev);
break;
case SSM_WAIT_ANOTHER_SCAN:
/* Orange light is on now */
fpi_timeout_add(VFS_SSM_ORANGE_TIMEOUT, another_scan, _dev, ssm);
break;
default:
fp_err("Unknown state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
}
}
/* Driver functions */
/* Callback for dev_activate ssm */
static void dev_activate_callback(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(_dev);
vdev->ssm_active = 0;
fpi_ssm_free(ssm);
}
/* Activate device */
static int dev_activate(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
/* Initialize flags */
vdev->active = 1;
vdev->need_report = 1;
vdev->ssm_active = 1;
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(idev), activate_ssm, SSM_STATES, idev);
fpi_ssm_start(ssm, dev_activate_callback);
return 0;
}
/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
if (!vdev->ssm_active) {
fpi_imgdev_deactivate_complete(idev);
return;
}
/* Initialize flags */
vdev->active = 0;
vdev->need_report = 1;
}
/* Callback for dev_open ssm */
static void dev_open_callback(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
/* Notify open complete */
fpi_imgdev_open_complete(user_data, 0);
fpi_ssm_free(ssm);
}
/* Open device */
static int dev_open(struct fp_img_dev *idev, unsigned long driver_data)
{
struct vfs_dev_t *vdev;
/* Claim usb interface */
int error = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(idev)), 0);
if (error < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
return error;
}
/* Initialize private structure */
vdev = g_malloc0(sizeof(struct vfs_dev_t));
fp_dev_set_instance_data(FP_DEV(idev), vdev);
/* Clearing previous device state */
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(idev), activate_ssm, SSM_STATES, idev);
fpi_ssm_start(ssm, dev_open_callback);
return 0;
}
/* Close device */
static void dev_close(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev;
/* Release private structure */
vdev = FP_INSTANCE_DATA(FP_DEV(idev));
g_free(vdev);
/* Release usb interface */
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(idev)), 0);
/* Notify close complete */
fpi_imgdev_close_complete(idev);
}
/* Usb id table of device */
static const struct usb_id id_table[] = {
{.vendor = 0x138a,.product = 0x0050},
{0, 0, 0,},
};
/* Device driver definition */
struct fp_img_driver vfs0050_driver = {
/* Driver specification */
.driver = {
.id = VFS0050_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS0050",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
/* Image specification */
.flags = 0,
.img_width = VFS_IMAGE_WIDTH,
.img_height = -1,
.bz3_threshold = 24,
/* Routine specification */
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

382
libfprint/drivers/vfs0050.h Normal file
View File

@@ -0,0 +1,382 @@
/*
* Validity VFS0050 driver for libfprint
* Copyright (C) 2015-2016 Konstantin Semenov <zemen17@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
*/
/* Timeout for all send/recv operations, except interrupt waiting and abort */
#define VFS_USB_TIMEOUT 100
/* Timeout for usb abort */
#define VFS_USB_ABORT_TIMEOUT 20
/* Default timeout for SSM timers */
#define VFS_SSM_TIMEOUT 100
/* Timeout for orange light */
#define VFS_SSM_ORANGE_TIMEOUT 400
/* Buffer size for abort and fprint receiving */
#define VFS_USB_BUFFER_SIZE 65536
/* Line size from scanner including metainformation: line number, narrow stripe from the center, etc */
#define VFS_LINE_SIZE 148
/* Width of narrow stripe from the center */
#define VFS_NEXT_LINE_WIDTH 32
/* Image width from scanner */
#define VFS_IMAGE_WIDTH 100
/* Maximum image height after assembling */
#define VFS_MAX_HEIGHT 3000
/* Size of control packets: turn_on, turn_off, next_receive_* */
#define VFS_CONTROL_PACKET_SIZE 125
/* Size of result of commit */
#define VFS_COMMIT_RESPONSE_SIZE 1106
/* Size of interrupt from EP3 */
#define VFS_INTERRUPT_SIZE 5
/* EP3 endpoint */
#define EP3_IN 0x83
/* Fingerprint horizontal line */
struct vfs_line {
/* It must be always 0x01 */
unsigned char _0x01;
/* It must be always 0xfe */
unsigned char _0xfe;
/* line number starting from some number in Little-Endian */
unsigned short id;
/* Some hashes which are useful to detect noise */
unsigned char noise_hash_1;
unsigned char noise_hash_2;
/* The first byte of _somedata is always 0x00, the second is strange useless cyclic line number */
unsigned short _somedata;
/* Fingerprint image */
unsigned char data[VFS_IMAGE_WIDTH];
/* Narrow fingerprint part from the center used for variable speed lines assembling */
unsigned char next_line_part[VFS_NEXT_LINE_WIDTH];
/* scan_data is 0xfb except some rare cases, it's skipped */
unsigned char scan_data[8];
} __attribute__ ((__packed__));
/* The main driver structure */
struct vfs_dev_t {
/* One if we were asked to read fingerprint, zero otherwise */
char active;
/* Control packet parameter for send_control_packet */
unsigned char *control_packet;
/* For dev_deactivate to check whether ssm still running or not */
char ssm_active;
/* Current async transfer */
struct libusb_transfer *transfer;
/* Should we call fpi_imgdev_activate_complete or fpi_imgdev_deactivate_complete */
char need_report;
/* Should we wait more for interrupt */
char wait_interrupt;
/* Received fingerprint raw lines */
struct vfs_line *lines_buffer;
/* Current number of received bytes and current memory used by data */
int bytes, memory;
/* USB buffer for fingerprint */
char *usb_buffer;
/* Received interrupt data */
unsigned char interrupt[8];
};
/* SSM states for clear_ep2 */
enum SUBSM1 {
SUBSM1_COMMAND_04,
SUBSM1_RETURN_CODE,
SUBSM1_ABORT_2,
SUBSM1_STATES,
};
/* SSM states for control */
enum SUBSM2 {
SUBSM2_SEND_CONTROL,
SUBSM2_RETURN_CODE, /* If next_receive, send another control packet */
SUBSM2_SEND_COMMIT,
SUBSM2_COMMIT_RESPONSE,
SUBSM2_READ_EMPTY_INTERRUPT,
SUBSM2_ABORT_3,
SUBSM2_CLEAR_EP2,
SUBSM2_STATES,
};
/* SSM states for activate_ssm */
enum SSM_STATE {
SSM_INITIAL_ABORT_1,
SSM_INITIAL_ABORT_2,
SSM_INITIAL_ABORT_3,
SSM_CLEAR_EP2,
SSM_TURN_OFF,
/* Here the device is turned off; if not active, complete ssm */
SSM_TURN_ON,
SSM_ASK_INTERRUPT,
SSM_WAIT_INTERRUPT,
SSM_RECEIVE_FINGER,
SSM_SUBMIT_IMAGE,
/* If not active, jump to CLEAR_EP2 */
SSM_NEXT_RECEIVE,
SSM_WAIT_ANOTHER_SCAN,
/* Jump to TURN_ON */
SSM_STATES
};
/* Blocks of data from USB sniffer */
/* Turns on the light */
static unsigned char turn_on[] = {
0x39, 0x20, 0xBF, 0x02, 0x00, 0xF4, 0x01, 0x00, 0x00, 0x01, 0xD1, 0x00,
0x20, 0xD1, 0xD1, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x01, 0x00,
0x00, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF4, 0x01, 0x00,
0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xF4, 0x01, 0x00, 0x00, 0x02, 0xD1, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
};
/* Power off */
static unsigned char turn_off[] = {
0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
};
/* Turns on orange light */
static unsigned char next_receive_1[] = {
0x39, 0xB8, 0x0B, 0x00, 0x00, 0xB8, 0x0B, 0x00, 0x00, 0x01, 0xD1, 0x00,
0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xB8, 0x0B, 0x00, 0x00, 0x02, 0xD1, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
};
/* Packet directly after next_receive_1 */
static unsigned char next_receive_2[] = {
0x39, 0xE8, 0x03, 0x00, 0x00, 0xE8, 0x03, 0x00, 0x00, 0x01, 0xD1, 0x00,
0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xE8, 0x03, 0x00, 0x00, 0x02, 0xD1, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
};
/* Commit message */
static unsigned char commit_out[] = {
0x02, 0x94, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x2C, 0x03, 0x00,
0x30, 0x1B, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x03, 0x00, 0x30, 0x3D, 0x10, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x18, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x24, 0x03, 0x00,
0x30, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x28, 0x03, 0x00,
0x30, 0x08, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x30, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x38, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x3C, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x44, 0x03, 0x00,
0x30, 0x14, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x48, 0x03, 0x00, 0x30, 0x01, 0x04, 0x02,
0x00, 0x20, 0x00, 0x08,
0x00, 0x4C, 0x03, 0x00, 0x30, 0x01, 0x0C, 0x02, 0x00, 0x20, 0x00, 0x08,
0x00, 0x54, 0x03, 0x00,
0x30, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x5C, 0x03, 0x00,
0x30, 0x90, 0x01, 0x02,
0x00, 0x20, 0x00, 0x08, 0x00, 0x60, 0x03, 0x00, 0x30, 0x2C, 0x01, 0x19,
0x00, 0x20, 0x00, 0x08,
0x00, 0x64, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x6C, 0x03, 0x00,
0x30, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x70, 0x03, 0x00,
0x30, 0x21, 0x80, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x78, 0x03, 0x00, 0x30, 0x09, 0x00, 0x02,
0x00, 0x20, 0x00, 0x08,
0x00, 0x7C, 0x03, 0x00, 0x30, 0x0B, 0x00, 0x19, 0x00, 0x20, 0x00, 0x08,
0x00, 0x80, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x84, 0x03, 0x00,
0x30, 0x3A, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x88, 0x03, 0x00, 0x30, 0x14, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x8C, 0x03, 0x00, 0x30, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x90, 0x03, 0x00,
0x30, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x94, 0x03, 0x00,
0x30, 0x08, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x98, 0x03, 0x00, 0x30, 0x00, 0x00, 0xA1,
0x01, 0x20, 0x00, 0x08,
0x00, 0x9C, 0x03, 0x00, 0x30, 0x00, 0x00, 0xA1, 0x01, 0x20, 0x00, 0x08,
0x00, 0xA8, 0x03, 0x00,
0x30, 0x64, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0xAC, 0x03, 0x00,
0x30, 0x64, 0x01, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0xB0, 0x03, 0x00, 0x30, 0x00, 0x01, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0xB4, 0x03, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0xB8, 0x03, 0x00,
0x30, 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0xBC, 0x03, 0x00,
0x30, 0x05, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0xC0, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x84, 0x03, 0x00, 0x30, 0x3B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x08, 0x07, 0x00,
0x30, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x0C, 0x07, 0x00,
0x30, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x14, 0x07, 0x00, 0x30, 0x20, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x1C, 0x07, 0x00, 0x30, 0x1A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x70, 0x0D, 0x00,
0x30, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x28, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x90, 0x00, 0x00, 0x00, 0x2B, 0xFF, 0x2B, 0xFF, 0x2B,
0xED, 0x00, 0x00, 0x2B,
0xFB, 0x00, 0x00, 0x2B, 0xC5, 0x00, 0x00, 0x2B, 0x05, 0x80, 0x70, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x24, 0xD3, 0x2E, 0xC0, 0x2C, 0x3B, 0x08, 0xF0, 0x3B, 0x09, 0x24,
0xBB, 0x3B, 0x0B, 0x24,
0xAA, 0x3B, 0x1F, 0xF8, 0x00, 0x3B, 0x3F, 0xF0, 0x00, 0x3B, 0x35, 0xC0,
0x00, 0x38, 0x80, 0x2C,
0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x38, 0x80, 0x2C, 0x70, 0x00,
0x00, 0x00, 0x00, 0xC0,
0x3A, 0x80, 0x2C, 0x70, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x3B, 0x0A, 0x80,
0x2E, 0x83, 0x24, 0xDB,
0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x2C, 0x31, 0x83, 0x2C, 0x70,
0x00, 0x00, 0x00, 0x00,
0xCB, 0x33, 0x1B, 0x83, 0x2C, 0x70, 0x00, 0x00, 0x00, 0x00, 0xCB, 0x31,
0x83, 0x2C, 0x70, 0x00,
0x00, 0x00, 0x00, 0xCB, 0x00, 0x33, 0x1E, 0x83, 0x2E, 0x25, 0xFF, 0xC4,
0x00, 0x2F, 0x06, 0x84,
0x2E, 0x00, 0x00, 0x10, 0x20, 0x29, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00,
0x00, 0x23, 0x00, 0x00,
0x00, 0x21, 0x00, 0x10, 0x00, 0x48, 0x03, 0x00, 0x30, 0xFF, 0xF0, 0xFF,
0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x21, 0x00, 0x10, 0x00, 0x4C, 0x03, 0x00,
0x30, 0xFF, 0xF0, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x21, 0x00, 0x10,
0x00, 0x20, 0x03, 0x00,
0x30, 0x7F, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x24, 0x03, 0x00, 0x30, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x1C, 0x07, 0x00,
0x30, 0x1A, 0x00, 0x00, 0x00, 0x21, 0x00, 0x10, 0x00, 0x20, 0x03, 0x00,
0x30, 0xC3, 0xFF, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x80, 0x03, 0x00,
0x30, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x84, 0x00, 0x31, 0x65, 0x77,
0x77, 0x77, 0x78, 0x88,
0x77, 0x77, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x77, 0x67,
0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x67, 0x66, 0x66, 0x66, 0x66,
0x66, 0x77, 0x66, 0x66,
0x66, 0x66, 0x67, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x76, 0x66, 0x56,
0x66, 0x66, 0x56, 0x55,
0x65, 0x66, 0x66, 0x66, 0x65, 0x66, 0x66, 0x55, 0x66, 0x66, 0x65, 0x66,
0x76, 0x76, 0x77, 0x77,
0x66, 0x66, 0x66, 0x76, 0x67, 0x66, 0x77, 0x67, 0x66, 0x66, 0x66, 0x56,
0x65, 0x66, 0x65, 0x66,
0x66, 0x55, 0x55, 0x54, 0x55, 0x65, 0x66, 0x66, 0x66, 0x76, 0x77, 0x87,
0x88, 0x77, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x65, 0x66, 0x55, 0x55, 0x65, 0x56, 0x55,
0x55, 0x55, 0x54, 0x45,
0x54, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77,
0x66, 0x26, 0x00, 0x28,
0x00, 0xFF, 0x00, 0x0F, 0x00, 0xF0, 0xF0, 0x0F, 0x00, 0x20, 0x00, 0x00,
0x00, 0x30, 0x01, 0x02,
0x00, 0x2C, 0x01, 0x28, 0x00, 0x20, 0x80, 0x00, 0x00, 0x0A, 0x00, 0x02,
0x00, 0x0B, 0x00, 0x19,
0x00, 0x40, 0x1F, 0x10, 0x27, 0x00, 0x0F, 0x03, 0x00,
};
/* Known interrupts */
static unsigned char empty_interrupt[] = {
0x00, 0x00, 0x00, 0x00, 0x00,
};
static unsigned char interrupt1[] = {
0x02, 0x00, 0x0E, 0x00, 0xF0,
};
static unsigned char interrupt2[] = {
0x02, 0x04, 0x0A, 0x00, 0xF0,
};
static unsigned char interrupt3[] = {
0x02, 0x00, 0x0A, 0x00, 0xF0,
};

1547
libfprint/drivers/vfs101.c Normal file

File diff suppressed because it is too large Load Diff

289
libfprint/drivers/vfs301.c Normal file
View File

@@ -0,0 +1,289 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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 "vfs301"
#include "drivers_api.h"
#include "vfs301_proto.h"
/************************** GENERIC STUFF *************************************/
/* Submit asynchronous sleep */
static void
async_sleep(unsigned int msec,
fpi_ssm *ssm,
struct fp_img_dev *dev)
{
/* Add timeout */
if (fpi_timeout_add(msec, fpi_ssm_next_state_timeout_cb, FP_DEV(dev), ssm) == NULL) {
/* Failed to add timeout */
fp_err("failed to add timeout");
fpi_imgdev_session_error(dev, -ETIME);
fpi_ssm_mark_failed(ssm, -ETIME);
}
}
static int
submit_image(fpi_ssm *ssm,
struct fp_img_dev *dev)
{
vfs301_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
int height;
struct fp_img *img;
#if 0
/* XXX: This is probably handled by libfprint automagically? */
if (vdev->scanline_count < 20) {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
return 0;
}
#endif
img = fpi_img_new(VFS301_FP_OUTPUT_WIDTH * vdev->scanline_count);
if (img == NULL)
return 0;
vfs301_extract_image(vdev, img->data, &height);
/* TODO: how to detect flip? should the resulting image be
* oriented so that it is equal e.g. to a fingerprint on a paper,
* or to the finger when I look at it?) */
img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED;
img->width = VFS301_FP_OUTPUT_WIDTH;
img->height = height;
img = fpi_img_realloc(img, img->height * img->width);
fpi_imgdev_image_captured(dev, img);
return 1;
}
/* Loop ssm states */
enum
{
/* Step 0 - Scan finger */
M_REQUEST_PRINT,
M_WAIT_PRINT,
M_CHECK_PRINT,
M_READ_PRINT_START,
M_READ_PRINT_WAIT,
M_READ_PRINT_POLL,
M_SUBMIT_PRINT,
/* Number of states */
M_LOOP_NUM_STATES,
};
/* Exec loop sequential state machine */
static void m_loop_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
vfs301_dev_t *vdev = FP_INSTANCE_DATA(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case M_REQUEST_PRINT:
vfs301_proto_request_fingerprint(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
fpi_ssm_next_state(ssm);
break;
case M_WAIT_PRINT:
/* Wait fingerprint scanning */
async_sleep(200, ssm, dev);
break;
case M_CHECK_PRINT:
if (!vfs301_proto_peek_event(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev))
fpi_ssm_jump_to_state(ssm, M_WAIT_PRINT);
else
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_START:
fpi_imgdev_report_finger_status(dev, TRUE);
vfs301_proto_process_event_start(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_WAIT:
/* Wait fingerprint scanning */
async_sleep(200, ssm, dev);
break;
case M_READ_PRINT_POLL:
{
int rv = vfs301_proto_process_event_poll(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
g_assert(rv != VFS301_FAILURE);
if (rv == VFS301_ONGOING)
fpi_ssm_jump_to_state(ssm, M_READ_PRINT_WAIT);
else
fpi_ssm_next_state(ssm);
}
break;
case M_SUBMIT_PRINT:
if (submit_image(ssm, dev)) {
fpi_ssm_mark_completed(ssm);
/* NOTE: finger off is expected only after submitting image... */
fpi_imgdev_report_finger_status(dev, FALSE);
} else {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
}
break;
}
}
/* Complete loop sequential state machine */
static void m_loop_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
/* Free sequential state machine */
fpi_ssm_free(ssm);
}
/* Exec init sequential state machine */
static void m_init_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
vfs301_dev_t *vdev = FP_INSTANCE_DATA(_dev);
g_assert(fpi_ssm_get_cur_state(ssm) == 0);
vfs301_proto_init(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
fpi_ssm_mark_completed(ssm);
}
/* Complete init sequential state machine */
static void m_init_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
fpi_ssm *ssm_loop;
if (!fpi_ssm_get_error(ssm)) {
/* Notify activate complete */
fpi_imgdev_activate_complete(dev, 0);
/* Start loop ssm */
ssm_loop = fpi_ssm_new(FP_DEV(dev), m_loop_state, M_LOOP_NUM_STATES, dev);
fpi_ssm_start(ssm_loop, m_loop_complete);
}
/* Free sequential state machine */
fpi_ssm_free(ssm);
}
/* Activate device */
static int dev_activate(struct fp_img_dev *dev)
{
fpi_ssm *ssm;
/* Start init ssm */
ssm = fpi_ssm_new(FP_DEV(dev), m_init_state, 1, dev);
fpi_ssm_start(ssm, m_init_complete);
return 0;
}
/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *dev)
{
vfs301_dev_t *vdev;
vdev = FP_INSTANCE_DATA(FP_DEV(dev));
vfs301_proto_deinit(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
fpi_imgdev_deactivate_complete(dev);
}
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
vfs301_dev_t *vdev = NULL;
int r;
/* Claim usb interface */
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
/* Initialize private structure */
vdev = g_malloc0(sizeof(vfs301_dev_t));
fp_dev_set_instance_data(FP_DEV(dev), vdev);
vdev->scanline_buf = malloc(0);
vdev->scanline_count = 0;
/* Notify open complete */
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_close(struct fp_img_dev *dev)
{
vfs301_dev_t *vdev;
/* Release private structure */
vdev = FP_INSTANCE_DATA(FP_DEV(dev));
free(vdev->scanline_buf);
g_free(vdev);
/* Release usb interface */
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
/* Notify close complete */
fpi_imgdev_close_complete(dev);
}
/* Usb id table of device */
static const struct usb_id id_table[] =
{
{ .vendor = 0x138a, .product = 0x0005 /* vfs301 */ },
{ .vendor = 0x138a, .product = 0x0008 /* vfs300 */ },
{ 0, 0, 0, },
};
/* Device driver definition */
struct fp_img_driver vfs301_driver =
{
/* Driver specification */
.driver =
{
.id = VFS301_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS301",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
/* Image specification */
.flags = 0,
.img_width = VFS301_FP_WIDTH,
.img_height = -1,
.bz3_threshold = 24,
/* Routine specification */
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

@@ -0,0 +1,631 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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
*/
/*
* TODO:
* - async communication everywhere :)
* - protocol decyphering
* - what is needed and what is redundant
* - is some part of the initial data the firmware?
* - describe some interesting structures better
*/
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <glib.h>
#include "fpi-usb.h"
#include "vfs301_proto.h"
#include "vfs301_proto_fragments.h"
/************************** USB STUFF *****************************************/
#ifdef DEBUG
static void usb_print_packet(int dir, int rv, const unsigned char *data, int length)
{
fprintf(stderr, "%s, rv %d, len %d\n", dir ? "send" : "recv", rv, length);
#ifdef PRINT_VERBOSE
int i;
for (i = 0; i < MIN(length, 128); i++) {
fprintf(stderr, "%.2X ", data[i]);
if (i % 8 == 7)
fprintf(stderr, " ");
if (i % 32 == 31)
fprintf(stderr, "\n");
}
#endif
fprintf(stderr, "\n");
}
#endif
static int usb_recv(
vfs301_dev_t *dev,
struct libusb_device_handle *devh, unsigned char endpoint, int max_bytes)
{
g_assert(max_bytes <= sizeof(dev->recv_buf));
int r = libusb_bulk_transfer(
devh, endpoint,
dev->recv_buf, max_bytes,
&dev->recv_len, VFS301_DEFAULT_WAIT_TIMEOUT
);
#ifdef DEBUG
usb_print_packet(0, r, dev->recv_buf, dev->recv_len);
#endif
if (r < 0)
return r;
return 0;
}
static int usb_send(
struct libusb_device_handle *devh, const unsigned char *data, int length)
{
int transferred = 0;
int r = libusb_bulk_transfer(
devh, VFS301_SEND_ENDPOINT,
(unsigned char *)data, length, &transferred, VFS301_DEFAULT_WAIT_TIMEOUT
);
#ifdef DEBUG
usb_print_packet(1, r, data, length);
#endif
g_assert(r == 0);
if (r < 0)
return r;
if (transferred < length)
return r;
return 0;
}
/************************** OUT MESSAGES GENERATION ***************************/
static void vfs301_proto_generate_0B(int subtype, unsigned char *data, int *len)
{
*data = 0x0B;
*len = 1;
data++;
memset(data, 0, 39);
*len += 38;
data[20] = subtype;
switch (subtype) {
case 0x04:
data[34] = 0x9F;
break;
case 0x05:
data[34] = 0xAB;
len++;
break;
default:
g_assert_not_reached();
break;
}
}
#define HEX_TO_INT(c) \
(((c) >= '0' && (c) <= '9') ? ((c) - '0') : ((c) - 'A' + 10))
static void translate_str(const char **srcL, unsigned char *data, int *len)
{
const char *src;
unsigned char *dataOrig = data;
while (*srcL != NULL) {
src = *srcL;
while (*src != '\0') {
g_assert(*src != '\0');
g_assert(*(src +1) != '\0');
*data = (unsigned char)((HEX_TO_INT(*src) << 4) | (HEX_TO_INT(*(src + 1))));
data++;
src += 2;
}
srcL++;
}
*len = data - dataOrig;
}
static void vfs301_proto_generate(int type, int subtype, unsigned char *data, int *len)
{
switch (type) {
case 0x01:
case 0x04:
/* After cmd 0x04 is sent, a data is received on VALIDITY_RECEIVE_ENDPOINT_CTRL.
* If it is 0x0000:
* additional 64B and 224B are read from _DATA, then vfs301_next_scan_FA00 is
* sent, 0000 received from _CTRL, and then continue with wait loop
* If it is 0x1204:
* => reinit?
*/
case 0x17:
case 0x19:
case 0x1A:
*data = type;
*len = 1;
break;
case 0x0B:
vfs301_proto_generate_0B(subtype, data, len);
break;
case 0x02D0:
{
const char **dataLs[] = {
vfs301_02D0_01,
vfs301_02D0_02,
vfs301_02D0_03,
vfs301_02D0_04,
vfs301_02D0_05,
vfs301_02D0_06,
vfs301_02D0_07,
};
g_assert((int)subtype <= (int)(sizeof(dataLs) / sizeof(dataLs[0])));
translate_str(dataLs[subtype - 1], data, len);
}
break;
case 0x0220:
switch (subtype) {
case 1:
translate_str(vfs301_0220_01, data, len);
break;
case 2:
translate_str(vfs301_0220_02, data, len);
break;
case 3:
translate_str(vfs301_0220_03, data, len);
break;
case 0xFA00:
case 0x2C01:
case 0x5E01:
translate_str(vfs301_next_scan_template, data, len);
unsigned char *field = data + *len - (sizeof(S4_TAIL) - 1) / 2 - 4;
g_assert(*field == 0xDE);
g_assert(*(field + 1) == 0xAD);
g_assert(*(field + 2) == 0xDE);
g_assert(*(field + 3) == 0xAD);
*field = (unsigned char)((subtype >> 8) & 0xFF);
*(field + 1) = (unsigned char)(subtype & 0xFF);
*(field + 2) = *field;
*(field + 3) = *(field + 1);
break;
default:
g_assert(0);
break;
}
break;
case 0x06:
g_assert_not_reached();
break;
default:
g_assert_not_reached();
break;
}
}
/************************** SCAN IMAGE PROCESSING *****************************/
#ifdef SCAN_FINISH_DETECTION
static int img_is_finished_scan(fp_line_t *lines, int no_lines)
{
int i;
int j;
int rv = 1;
for (i = no_lines - VFS301_FP_SUM_LINES; i < no_lines; i++) {
/* check the line for fingerprint data */
for (j = 0; j < sizeof(lines[i].sum2); j++) {
if (lines[i].sum2[j] > (VFS301_FP_SUM_MEDIAN + VFS301_FP_SUM_EMPTY_RANGE))
rv = 0;
}
}
return rv;
}
#endif
static int scanline_diff(const unsigned char *scanlines, int prev, int cur)
{
const unsigned char *line1 = scanlines + prev * VFS301_FP_OUTPUT_WIDTH;
const unsigned char *line2 = scanlines + cur * VFS301_FP_OUTPUT_WIDTH;
int i;
int diff;
#ifdef OUTPUT_RAW
/* We only need the image, not the surrounding stuff. */
line1 = ((vfs301_line_t*)line1)->scan;
line2 = ((vfs301_line_t*)line2)->scan;
#endif
/* TODO: This doesn't work too well when there are parallel lines in the
* fingerprint. */
for (diff = 0, i = 0; i < VFS301_FP_WIDTH; i++) {
if (*line1 > *line2)
diff += *line1 - *line2;
else
diff += *line2 - *line1;
line1++;
line2++;
}
return ((diff / VFS301_FP_WIDTH) > VFS301_FP_LINE_DIFF_THRESHOLD);
}
/** Transform the input data to a normalized fingerprint scan */
void vfs301_extract_image(
vfs301_dev_t *vfs, unsigned char *output, int *output_height
)
{
const unsigned char *scanlines = vfs->scanline_buf;
int last_line;
int i;
g_assert(vfs->scanline_count >= 1);
*output_height = 1;
memcpy(output, scanlines, VFS301_FP_OUTPUT_WIDTH);
last_line = 0;
/* The following algorithm is quite trivial - it just picks lines that
* differ more than VFS301_FP_LINE_DIFF_THRESHOLD.
* TODO: A nicer approach would be to pick those lines and then do some kind
* of bi/tri-linear resampling to get the output (so that we don't get so
* many false edges etc.).
*/
for (i = 1; i < vfs->scanline_count; i++) {
if (scanline_diff(scanlines, last_line, i)) {
memcpy(
output + VFS301_FP_OUTPUT_WIDTH * (*output_height),
scanlines + VFS301_FP_OUTPUT_WIDTH * i,
VFS301_FP_OUTPUT_WIDTH
);
last_line = i;
(*output_height)++;
}
}
}
static int img_process_data(
int first_block, vfs301_dev_t *dev, const unsigned char *buf, int len
)
{
vfs301_line_t *lines = (vfs301_line_t*)buf;
int no_lines = len / sizeof(vfs301_line_t);
int i;
/*int no_nonempty;*/
unsigned char *cur_line;
int last_img_height;
#ifdef SCAN_FINISH_DETECTION
int finished_scan;
#endif
if (first_block) {
last_img_height = 0;
dev->scanline_count = no_lines;
} else {
last_img_height = dev->scanline_count;
dev->scanline_count += no_lines;
}
dev->scanline_buf = realloc(dev->scanline_buf, dev->scanline_count * VFS301_FP_OUTPUT_WIDTH);
g_assert(dev->scanline_buf != NULL);
for (cur_line = dev->scanline_buf + last_img_height * VFS301_FP_OUTPUT_WIDTH, i = 0;
i < no_lines;
i++, cur_line += VFS301_FP_OUTPUT_WIDTH
) {
#ifndef OUTPUT_RAW
memcpy(cur_line, lines[i].scan, VFS301_FP_OUTPUT_WIDTH);
#else
memcpy(cur_line, &lines[i], VFS301_FP_OUTPUT_WIDTH);
#endif
}
#ifdef SCAN_FINISH_DETECTION
finished_scan = img_is_finished_scan(lines, no_lines);
return !finished_scan;
#else /* SCAN_FINISH_DETECTION */
return 1; /* Just continue until data is coming */
#endif
}
/************************** PROTOCOL STUFF ************************************/
static unsigned char usb_send_buf[0x2000];
#define USB_RECV(from, len) \
usb_recv(dev, devh, from, len)
#define USB_SEND(type, subtype) \
{ \
int len; \
vfs301_proto_generate(type, subtype, usb_send_buf, &len); \
usb_send(devh, usb_send_buf, len); \
}
#define RAW_DATA(x) x, sizeof(x)
#define IS_VFS301_FP_SEQ_START(b) ((b[0] == 0x01) && (b[1] == 0xfe))
static int vfs301_proto_process_data(int first_block, vfs301_dev_t *dev)
{
int i;
const unsigned char *buf = dev->recv_buf;
int len = dev->recv_len;
if (first_block) {
g_assert(len >= VFS301_FP_FRAME_SIZE);
/* Skip bytes until start_sequence is found */
for (i = 0; i < VFS301_FP_FRAME_SIZE; i++, buf++, len--) {
if (IS_VFS301_FP_SEQ_START(buf))
break;
}
}
return img_process_data(first_block, dev, buf, len);
}
void vfs301_proto_request_fingerprint(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
USB_SEND(0x0220, 0xFA00);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 000000000000 */
}
int vfs301_proto_peek_event(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
const char no_event[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char got_event[] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
USB_SEND(0x17, -1);
g_assert(USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7) == 0);
if (memcmp(dev->recv_buf, no_event, sizeof(no_event)) == 0) {
return 0;
} else if (memcmp(dev->recv_buf, got_event, sizeof(no_event)) == 0) {
return 1;
} else {
g_assert_not_reached();
}
}
#define VARIABLE_ORDER(a, b) \
{ \
int _rv = a;\
b; \
if (_rv == -7) \
a; \
}
static void vfs301_proto_process_event_cb(struct libusb_transfer *transfer)
{
vfs301_dev_t *dev = transfer->user_data;
struct libusb_device_handle *devh = transfer->dev_handle;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
dev->recv_progress = VFS301_FAILURE;
goto end;
} else if (transfer->actual_length < dev->recv_exp_amt) {
/* TODO: process the data anyway? */
dev->recv_progress = VFS301_ENDED;
goto end;
} else {
dev->recv_len = transfer->actual_length;
if (!vfs301_proto_process_data(dev->recv_exp_amt == VFS301_FP_RECV_LEN_1, dev)) {
dev->recv_progress = VFS301_ENDED;
goto end;
}
dev->recv_exp_amt = VFS301_FP_RECV_LEN_2;
libusb_fill_bulk_transfer(
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
dev->recv_buf, dev->recv_exp_amt,
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
if (libusb_submit_transfer(transfer) < 0) {
printf("cb::continue fail\n");
dev->recv_progress = VFS301_FAILURE;
goto end;
}
return;
}
end:
libusb_free_transfer(transfer);
}
void vfs301_proto_process_event_start(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
struct libusb_transfer *transfer;
/*
* Notes:
*
* seen next_scan order:
* o FA00
* o FA00
* o 2C01
* o FA00
* o FA00
* o 2C01
* o FA00
* o FA00
* o 2C01
* o 5E01 !?
* o FA00
* o FA00
* o 2C01
* o FA00
* o FA00
* o 2C01
*/
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 64);
/* now read the fingerprint data, while there are some */
transfer = fpi_usb_alloc();
dev->recv_progress = VFS301_ONGOING;
dev->recv_exp_amt = VFS301_FP_RECV_LEN_1;
libusb_fill_bulk_transfer(
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
dev->recv_buf, dev->recv_exp_amt,
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
if (libusb_submit_transfer(transfer) < 0) {
libusb_free_transfer(transfer);
dev->recv_progress = VFS301_FAILURE;
return;
}
}
int /* vfs301_dev_t::recv_progress */ vfs301_proto_process_event_poll(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
if (dev->recv_progress != VFS301_ENDED)
return dev->recv_progress;
/* Finish the scan process... */
USB_SEND(0x04, -1);
/* the following may come in random order, data may not come at all, don't
* try for too long... */
VARIABLE_ORDER(
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 1204 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 16384)
);
USB_SEND(0x0220, 2);
VARIABLE_ORDER(
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760), /* seems to always come */
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2) /* 0000 */
);
return dev->recv_progress;
}
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x0B, 0x04);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 6); /* 000000000000 */
USB_SEND(0x0B, 0x05);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7); /* 00000000000000 */
USB_SEND(0x19, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 64);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 4); /* 6BB4D0BC */
usb_send(devh, RAW_DATA(vfs301_06_1));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_2));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x0220, 1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 256);
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 32);
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_3));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x02D0, 1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 11648); /* 56 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 2);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 53248); /* 2 * 128 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 3);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 19968); /* 96 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 4);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5824); /* 28 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 5);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 6);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 7);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 832);
usb_send(devh, RAW_DATA(vfs301_12));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_2));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x0220, 2);
VARIABLE_ORDER(
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760)
);
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_1));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_4));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_24)); /* turns on white */
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x0220, 3);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2368);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 36);
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760);
}
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
}

View File

@@ -0,0 +1,137 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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 <libusb-1.0/libusb.h>
enum {
VFS301_DEFAULT_WAIT_TIMEOUT = 300,
VFS301_SEND_ENDPOINT = 0x01,
VFS301_RECEIVE_ENDPOINT_CTRL = 0x81,
VFS301_RECEIVE_ENDPOINT_DATA = 0x82
};
#define VFS301_FP_RECV_LEN_1 (84032)
#define VFS301_FP_RECV_LEN_2 (84096)
typedef struct {
/* buffer for received data */
unsigned char recv_buf[0x20000];
int recv_len;
/* buffer to hold raw scanlines */
unsigned char *scanline_buf;
int scanline_count;
enum {
VFS301_ONGOING = 0,
VFS301_ENDED = 1,
VFS301_FAILURE = -1
} recv_progress;
int recv_exp_amt;
} vfs301_dev_t;
enum {
/* Width of the scanned data in px */
VFS301_FP_WIDTH = 200,
/* sizeof(fp_line_t) */
VFS301_FP_FRAME_SIZE = 288,
/* Width of output line */
#ifndef OUTPUT_RAW
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_WIDTH,
#else
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_FRAME_SIZE,
#endif
VFS301_FP_SUM_LINES = 3,
#ifdef SCAN_FINISH_DETECTION
/* TODO: The following changes (seen ~60 and ~80) In that
* case we'll need to calibrate this from empty data somehow... */
VFS301_FP_SUM_MEDIAN = 60,
VFS301_FP_SUM_EMPTY_RANGE = 5,
#endif
/* Minimum average difference between returned lines */
VFS301_FP_LINE_DIFF_THRESHOLD = 15,
/* Maximum waiting time for a single fingerprint frame */
VFS301_FP_RECV_TIMEOUT = 2000
};
/* Arrays of this structure is returned during the initialization as a response
* to the 0x02D0 messages.
* It seems to be always the same - what is it for? Some kind of confirmation?
*/
typedef struct {
unsigned char sync_0x01;
unsigned char sync_0xfe;
unsigned char counter_lo;
unsigned char counter_hi; /* FIXME ? */
unsigned char flags[3];
unsigned char sync_0x00;
unsigned char scan[VFS301_FP_WIDTH];
} vfs301_init_line_t;
typedef struct {
unsigned char sync_0x01;
unsigned char sync_0xfe;
unsigned char counter_lo;
unsigned char counter_hi;
unsigned char sync_0x08[2]; /* XXX: always? 0x08 0x08 */
/* 0x08 | 0x18 - Looks like 0x08 marks good quality lines */
unsigned char flag_1;
unsigned char sync_0x00;
unsigned char scan[VFS301_FP_WIDTH];
/* A offsetted, stretched, inverted copy of scan... probably could
* serve finger motion speed detection?
* Seems to be subdivided to some 10B + 53B + 1B blocks */
unsigned char mirror[64];
/* Some kind of sum of the scan, very low contrast */
unsigned char sum1[2];
unsigned char sum2[11];
unsigned char sum3[3];
} vfs301_line_t;
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_request_fingerprint(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
/** returns 0 if no event is ready, or 1 if there is one... */
int vfs301_proto_peek_event(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_process_event_start(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
int vfs301_proto_process_event_poll(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_extract_image(vfs301_dev_t *vfs, unsigned char *output, int *output_height);

File diff suppressed because it is too large Load Diff

907
libfprint/drivers/vfs5011.c Normal file
View File

@@ -0,0 +1,907 @@
/*
* Validity Sensors, Inc. VFS5011 Fingerprint Reader driver for libfprint
* Copyright (C) 2013 Arseniy Lartsev <arseniy@chalmers.se>
* AceLan Kao <acelan.kao@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
*/
#include "drivers_api.h"
#include "vfs5011_proto.h"
/* =================== sync/async USB transfer sequence ==================== */
enum {
ACTION_SEND,
ACTION_RECEIVE,
};
struct usb_action {
int type;
const char *name;
int endpoint;
int size;
unsigned char *data;
int correct_reply_size;
};
#define SEND(ENDPOINT, COMMAND) \
{ \
.type = ACTION_SEND, \
.endpoint = ENDPOINT, \
.name = #COMMAND, \
.size = sizeof(COMMAND), \
.data = COMMAND \
},
#define RECV(ENDPOINT, SIZE) \
{ \
.type = ACTION_RECEIVE, \
.endpoint = ENDPOINT, \
.size = SIZE, \
.data = NULL \
},
#define RECV_CHECK(ENDPOINT, SIZE, EXPECTED) \
{ \
.type = ACTION_RECEIVE, \
.endpoint = ENDPOINT, \
.size = SIZE, \
.data = EXPECTED, \
.correct_reply_size = sizeof(EXPECTED) \
},
struct usbexchange_data {
int stepcount;
struct fp_img_dev *device;
struct usb_action *actions;
void *receive_buf;
int timeout;
};
static void start_scan(struct fp_img_dev *dev);
static void async_send_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct usbexchange_data *data = fpi_ssm_get_user_data(ssm);
struct usb_action *action;
if (fpi_ssm_get_cur_state(ssm) >= data->stepcount) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
goto out;
}
action = &data->actions[fpi_ssm_get_cur_state(ssm)];
if (action->type != ACTION_SEND) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
goto out;
}
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
/* Transfer not completed, return IO error */
fp_err("transfer not completed, status = %d", transfer->status);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
if (transfer->length != transfer->actual_length) {
/* Data sended mismatch with expected, return protocol error */
fp_err("length mismatch, got %d, expected %d",
transfer->actual_length, transfer->length);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
/* success */
fpi_ssm_next_state(ssm);
out:
libusb_free_transfer(transfer);
}
static void async_recv_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct usbexchange_data *data = fpi_ssm_get_user_data(ssm);
struct usb_action *action;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
/* Transfer not completed, return IO error */
fp_err("transfer not completed, status = %d", transfer->status);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
if (fpi_ssm_get_cur_state(ssm) >= data->stepcount) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
goto out;
}
action = &data->actions[fpi_ssm_get_cur_state(ssm)];
if (action->type != ACTION_RECEIVE) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
goto out;
}
if (action->data != NULL) {
if (transfer->actual_length != action->correct_reply_size) {
fp_err("Got %d bytes instead of %d",
transfer->actual_length,
action->correct_reply_size);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
if (memcmp(transfer->buffer, action->data,
action->correct_reply_size) != 0) {
fp_dbg("Wrong reply:");
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
} else
fp_dbg("Got %d bytes out of %d", transfer->actual_length,
transfer->length);
fpi_ssm_next_state(ssm);
out:
libusb_free_transfer(transfer);
}
static void usbexchange_loop(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct usbexchange_data *data = user_data;
if (fpi_ssm_get_cur_state(ssm) >= data->stepcount) {
fp_err("Bug detected: state %d out of range, only %d steps",
fpi_ssm_get_cur_state(ssm), data->stepcount);
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
return;
}
struct usb_action *action = &data->actions[fpi_ssm_get_cur_state(ssm)];
struct libusb_transfer *transfer;
int ret = -EINVAL;
switch (action->type) {
case ACTION_SEND:
fp_dbg("Sending %s", action->name);
transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(data->device)),
action->endpoint, action->data,
action->size, async_send_cb, ssm,
data->timeout);
ret = libusb_submit_transfer(transfer);
break;
case ACTION_RECEIVE:
fp_dbg("Receiving %d bytes", action->size);
transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(data->device)),
action->endpoint, data->receive_buf,
action->size, async_recv_cb, ssm,
data->timeout);
ret = libusb_submit_transfer(transfer);
break;
default:
fp_err("Bug detected: invalid action %d", action->type);
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_failed(ssm, -EINVAL);
return;
}
if (ret != 0) {
fp_err("USB transfer error: %s", strerror(ret));
fpi_imgdev_session_error(data->device, ret);
fpi_ssm_mark_failed(ssm, ret);
}
}
static void usb_exchange_async(fpi_ssm *ssm,
struct usbexchange_data *data)
{
fpi_ssm *subsm = fpi_ssm_new(FP_DEV(data->device),
usbexchange_loop,
data->stepcount,
data);
fpi_ssm_start_subsm(ssm, subsm);
}
/* ====================== utils ======================= */
/* Calculade squared standand deviation of sum of two lines */
static int vfs5011_get_deviation2(struct fpi_line_asmbl_ctx *ctx, GSList *row1, GSList *row2)
{
unsigned char *buf1, *buf2;
int res = 0, mean = 0, i;
const int size = 64;
buf1 = row1->data + 56;
buf2 = row2->data + 168;
for (i = 0; i < size; i++)
mean += (int)buf1[i] + (int)buf2[i];
mean /= size;
for (i = 0; i < size; i++) {
int dev = (int)buf1[i] + (int)buf2[i] - mean;
res += dev*dev;
}
return res / size;
}
static unsigned char vfs5011_get_pixel(struct fpi_line_asmbl_ctx *ctx,
GSList *row,
unsigned x)
{
unsigned char *data = row->data + 8;
return data[x];
}
/* ====================== main stuff ======================= */
enum {
CAPTURE_LINES = 256,
MAXLINES = 2000,
MAX_CAPTURE_LINES = 100000,
};
static struct fpi_line_asmbl_ctx assembling_ctx = {
.line_width = VFS5011_IMAGE_WIDTH,
.max_height = MAXLINES,
.resolution = 10,
.median_filter_size = 25,
.max_search_offset = 30,
.get_deviation = vfs5011_get_deviation2,
.get_pixel = vfs5011_get_pixel,
};
struct vfs5011_data {
unsigned char *total_buffer;
unsigned char *capture_buffer;
unsigned char *row_buffer;
unsigned char *lastline;
GSList *rows;
int lines_captured, lines_recorded, empty_lines;
int max_lines_captured, max_lines_recorded;
int lines_total, lines_total_allocated;
gboolean loop_running;
gboolean deactivating;
struct usbexchange_data init_sequence;
struct libusb_transfer *flying_transfer;
};
enum {
DEV_ACTIVATE_REQUEST_FPRINT,
DEV_ACTIVATE_INIT_COMPLETE,
DEV_ACTIVATE_READ_DATA,
DEV_ACTIVATE_DATA_COMPLETE,
DEV_ACTIVATE_PREPARE_NEXT_CAPTURE,
DEV_ACTIVATE_NUM_STATES
};
enum {
DEV_OPEN_START,
DEV_OPEN_NUM_STATES
};
static void capture_init(struct vfs5011_data *data, int max_captured,
int max_recorded)
{
fp_dbg("capture_init");
data->lastline = NULL;
data->lines_captured = 0;
data->lines_recorded = 0;
data->empty_lines = 0;
data->lines_total = 0;
data->lines_total_allocated = 0;
data->total_buffer = NULL;
data->max_lines_captured = max_captured;
data->max_lines_recorded = max_recorded;
}
static int process_chunk(struct vfs5011_data *data, int transferred)
{
enum {
DEVIATION_THRESHOLD = 15*15,
DIFFERENCE_THRESHOLD = 600,
STOP_CHECK_LINES = 50
};
fp_dbg("process_chunk: got %d bytes", transferred);
int lines_captured = transferred/VFS5011_LINE_SIZE;
int i;
for (i = 0; i < lines_captured; i++) {
unsigned char *linebuf = data->capture_buffer
+ i * VFS5011_LINE_SIZE;
if (fpi_std_sq_dev(linebuf + 8, VFS5011_IMAGE_WIDTH)
< DEVIATION_THRESHOLD) {
if (data->lines_captured == 0)
continue;
else
data->empty_lines++;
} else
data->empty_lines = 0;
if (data->empty_lines >= STOP_CHECK_LINES) {
fp_dbg("process_chunk: got %d empty lines, finishing",
data->empty_lines);
return 1;
}
data->lines_captured++;
if (data->lines_captured > data->max_lines_captured) {
fp_dbg("process_chunk: captured %d lines, finishing",
data->lines_captured);
return 1;
}
if ((data->lastline == NULL)
|| (fpi_mean_sq_diff_norm(
data->lastline + 8,
linebuf + 8,
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) {
data->lastline = g_malloc(VFS5011_LINE_SIZE);
data->rows = g_slist_prepend(data->rows, data->lastline);
memmove(data->lastline, linebuf, VFS5011_LINE_SIZE);
data->lines_recorded++;
if (data->lines_recorded >= data->max_lines_recorded) {
fp_dbg("process_chunk: recorded %d lines, finishing",
data->lines_recorded);
return 1;
}
}
}
return 0;
}
static void
submit_image(fpi_ssm *ssm,
struct vfs5011_data *data,
struct fp_img_dev *dev)
{
struct fp_img *img;
if (data->lines_recorded == 0) {
/* == FP_ENROLL_RETRY_TOO_SHORT */
fpi_imgdev_session_error(dev, FP_VERIFY_RETRY_TOO_SHORT);
return;
}
g_assert (data->rows != NULL);
data->rows = g_slist_reverse(data->rows);
img = fpi_assemble_lines(&assembling_ctx, data->rows, data->lines_recorded);
g_slist_free_full(data->rows, g_free);
data->rows = NULL;
fp_dbg("Image captured, committing");
fpi_imgdev_image_captured(dev, img);
}
static void chunk_capture_callback(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = (fpi_ssm *)transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(FP_DEV(dev));
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) ||
(transfer->status == LIBUSB_TRANSFER_TIMED_OUT)) {
if (transfer->actual_length > 0)
fpi_imgdev_report_finger_status(dev, TRUE);
if (process_chunk(data, transfer->actual_length))
fpi_ssm_jump_to_state(ssm, DEV_ACTIVATE_DATA_COMPLETE);
else
fpi_ssm_jump_to_state(ssm, DEV_ACTIVATE_READ_DATA);
} else {
if (!data->deactivating) {
fp_err("Failed to capture data");
fpi_ssm_mark_failed(ssm, -1);
} else {
fpi_ssm_mark_completed(ssm);
}
}
libusb_free_transfer(transfer);
data->flying_transfer = NULL;
}
static int capture_chunk_async(struct vfs5011_data *data,
libusb_device_handle *handle, int nline,
int timeout, fpi_ssm *ssm)
{
fp_dbg("capture_chunk_async: capture %d lines, already have %d",
nline, data->lines_recorded);
enum {
DEVIATION_THRESHOLD = 15*15,
DIFFERENCE_THRESHOLD = 600,
STOP_CHECK_LINES = 50
};
data->flying_transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(data->flying_transfer, handle, VFS5011_IN_ENDPOINT_DATA,
data->capture_buffer,
nline * VFS5011_LINE_SIZE,
chunk_capture_callback, ssm, timeout);
return libusb_submit_transfer(data->flying_transfer);
}
/*
* Device initialization. Windows driver only does it when the device is
* plugged in, but it doesn't harm to do this every time before scanning the
* image.
*/
struct usb_action vfs5011_initialization[] = {
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_01)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_19)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64) /* B5C457F9 */
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_00)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64) /* 0000FFFFFFFF */
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_01)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64) /* 0000FFFFFFFFFF */
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_02)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_01)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_03)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_04)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 256)
RECV(VFS5011_IN_ENDPOINT_DATA, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_05)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_01)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_06)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 17216)
RECV(VFS5011_IN_ENDPOINT_DATA, 32)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_07)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 45056)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_08)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 16896)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_09)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 4928)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_10)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 5632)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_11)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 5632)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_12)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 3328)
RECV(VFS5011_IN_ENDPOINT_DATA, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_13)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_03)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_14)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 4800)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_02)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_27)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_15)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_16)
RECV(VFS5011_IN_ENDPOINT_CTRL, 2368)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
RECV(VFS5011_IN_ENDPOINT_DATA, 4800)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_17)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_18)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
/*
* Windows driver does this and it works
* But in this driver this call never returns...
* RECV(VFS5011_IN_ENDPOINT_CTRL2, 8) //00D3054000
*/
};
/* Initiate recording the image */
struct usb_action vfs5011_initiate_capture[] = {
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_04)
RECV(VFS5011_IN_ENDPOINT_DATA, 64)
RECV(VFS5011_IN_ENDPOINT_DATA, 84032)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_00)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_01)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_02)
RECV(VFS5011_IN_ENDPOINT_CTRL, 2368)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
RECV(VFS5011_IN_ENDPOINT_DATA, 4800)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_03)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
/*
* Windows driver does this and it works
* But in this driver this call never returns...
* RECV(VFS5011_IN_ENDPOINT_CTRL2, 8);
*/
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_04)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 2368, VFS5011_NORMAL_CONTROL_REPLY)
/*
* Windows driver does this and it works
* But in this driver this call never returns...
* RECV(VFS5011_IN_ENDPOINT_CTRL2, 8);
*/
};
/* ====================== lifprint interface ======================= */
static void activate_loop(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
enum {READ_TIMEOUT = 0};
struct fp_img_dev *dev = user_data;
struct vfs5011_data *data;
int r;
fpi_timeout *timeout;
data = FP_INSTANCE_DATA(_dev);
fp_dbg("main_loop: state %d", fpi_ssm_get_cur_state(ssm));
if (data->deactivating) {
fp_dbg("deactivating, marking completed");
fpi_ssm_mark_completed(ssm);
return;
}
switch (fpi_ssm_get_cur_state(ssm)) {
case DEV_ACTIVATE_REQUEST_FPRINT:
data->init_sequence.stepcount =
G_N_ELEMENTS(vfs5011_initiate_capture);
data->init_sequence.actions = vfs5011_initiate_capture;
data->init_sequence.device = dev;
if (data->init_sequence.receive_buf == NULL)
data->init_sequence.receive_buf =
g_malloc0(VFS5011_RECEIVE_BUF_SIZE);
data->init_sequence.timeout = 1000;
usb_exchange_async(ssm, &data->init_sequence);
break;
case DEV_ACTIVATE_INIT_COMPLETE:
if (data->init_sequence.receive_buf != NULL)
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
capture_init(data, MAX_CAPTURE_LINES, MAXLINES);
fpi_imgdev_activate_complete(dev, 0);
fpi_ssm_next_state(ssm);
break;
case DEV_ACTIVATE_READ_DATA:
r = capture_chunk_async(data, fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_LINES,
READ_TIMEOUT, ssm);
if (r != 0) {
fp_err("Failed to capture data");
fpi_imgdev_session_error(dev, r);
fpi_ssm_mark_failed(ssm, r);
}
break;
case DEV_ACTIVATE_DATA_COMPLETE:
timeout = fpi_timeout_add(1, fpi_ssm_next_state_timeout_cb, _dev, ssm);
if (timeout == NULL) {
/* Failed to add timeout */
fp_err("failed to add timeout");
fpi_imgdev_session_error(dev, -1);
fpi_ssm_mark_failed(ssm, -1);
}
break;
case DEV_ACTIVATE_PREPARE_NEXT_CAPTURE:
data->init_sequence.stepcount =
G_N_ELEMENTS(vfs5011_initiate_capture);
data->init_sequence.actions = vfs5011_initiate_capture;
data->init_sequence.device = dev;
if (data->init_sequence.receive_buf == NULL)
data->init_sequence.receive_buf =
g_malloc0(VFS5011_RECEIVE_BUF_SIZE);
data->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT;
usb_exchange_async(ssm, &data->init_sequence);
break;
}
}
static void activate_loop_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct vfs5011_data *data;
int r = fpi_ssm_get_error(ssm);
data = FP_INSTANCE_DATA(_dev);
fp_dbg("finishing");
if (data->init_sequence.receive_buf != NULL)
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
if (!data->deactivating && !r) {
submit_image(ssm, data, dev);
fpi_imgdev_report_finger_status(dev, FALSE);
}
fpi_ssm_free(ssm);
data->loop_running = FALSE;
if (data->deactivating) {
fpi_imgdev_deactivate_complete(dev);
} else if (r) {
fpi_imgdev_session_error(dev, r);
} else {
start_scan(dev);
}
}
static void open_loop(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case DEV_OPEN_START:
data->init_sequence.stepcount =
G_N_ELEMENTS(vfs5011_initialization);
data->init_sequence.actions = vfs5011_initialization;
data->init_sequence.device = dev;
data->init_sequence.receive_buf =
g_malloc0(VFS5011_RECEIVE_BUF_SIZE);
data->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT;
usb_exchange_async(ssm, &data->init_sequence);
break;
};
}
static void open_loop_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(_dev);
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
fpi_imgdev_open_complete(dev, 0);
fpi_ssm_free(ssm);
}
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
struct vfs5011_data *data;
int r;
data = (struct vfs5011_data *)g_malloc0(sizeof(*data));
data->capture_buffer =
(unsigned char *)g_malloc0(CAPTURE_LINES * VFS5011_LINE_SIZE);
fp_dev_set_instance_data(FP_DEV(dev), data);
r = libusb_reset_device(fpi_dev_get_usb_dev(FP_DEV(dev)));
if (r != 0) {
fp_err("Failed to reset the device");
return r;
}
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r != 0) {
fp_err("Failed to claim interface: %s", libusb_error_name(r));
return r;
}
fpi_ssm *ssm;
ssm = fpi_ssm_new(FP_DEV(dev), open_loop, DEV_OPEN_NUM_STATES, dev);
fpi_ssm_start(ssm, open_loop_complete);
return 0;
}
static void dev_close(struct fp_img_dev *dev)
{
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(FP_DEV(dev));
if (data != NULL) {
g_free(data->capture_buffer);
g_slist_free_full(data->rows, g_free);
g_free(data);
}
fpi_imgdev_close_complete(dev);
}
static void start_scan(struct fp_img_dev *dev)
{
struct vfs5011_data *data;
fpi_ssm *ssm;
data = FP_INSTANCE_DATA(FP_DEV(dev));
data->loop_running = TRUE;
fp_dbg("creating ssm");
ssm = fpi_ssm_new(FP_DEV(dev), activate_loop, DEV_ACTIVATE_NUM_STATES, dev);
fp_dbg("starting ssm");
fpi_ssm_start(ssm, activate_loop_complete);
fp_dbg("ssm done, getting out");
}
static int dev_activate(struct fp_img_dev *dev)
{
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(FP_DEV(dev));
fp_dbg("device initialized");
data->deactivating = FALSE;
start_scan(dev);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
int r;
struct vfs5011_data *data;
data = FP_INSTANCE_DATA(FP_DEV(dev));
if (data->loop_running) {
data->deactivating = TRUE;
if (data->flying_transfer) {
r = libusb_cancel_transfer(data->flying_transfer);
if (r < 0)
fp_dbg("cancel failed error %d", r);
}
} else
fpi_imgdev_deactivate_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x138a, .product = 0x0010 /* Validity device from some Toshiba laptops */ },
{ .vendor = 0x138a, .product = 0x0011 /* vfs5011 */ },
{ .vendor = 0x138a, .product = 0x0015 /* Validity device from Lenovo Preferred Pro USB Fingerprint Keyboard KUF1256 */ },
{ .vendor = 0x138a, .product = 0x0017 /* Validity device from Lenovo T440 laptops */ },
{ .vendor = 0x138a, .product = 0x0018 /* one more Validity device */ },
{ 0, 0, 0, },
};
struct fp_img_driver vfs5011_driver = {
.driver = {
.id = VFS5011_ID,
.name = "vfs5011",
.full_name = "Validity VFS5011",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_width = VFS5011_IMAGE_WIDTH,
.img_height = -1,
.bz3_threshold = 20,
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

File diff suppressed because it is too large Load Diff

39
libfprint/drivers_api.h Normal file
View File

@@ -0,0 +1,39 @@
/*
* Driver API definitions
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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
*/
#ifndef __DRIVERS_API_H__
#define __DRIVERS_API_H__
#include <config.h>
#include "fprint.h"
#include "fpi-log.h"
#include "fpi-dev.h"
#include "fpi-dev-img.h"
#include "fpi-core.h"
#include "fpi-ssm.h"
#include "fpi-poll.h"
#include "fpi-dev.h"
#include "fpi-usb.h"
#include "fpi-img.h"
#include "fpi-assembling.h"
#include "drivers/driver_ids.h"
#endif

View File

@@ -1,171 +0,0 @@
/*
* Functions to assist with asynchronous driver <---> library communications
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 "drv"
#include <config.h>
#include <errno.h>
#include "fp_internal.h"
/* SSM: sequential state machine
* Asynchronous driver design encourages some kind of state machine behind it.
* In most cases, the state machine is entirely linear - you only go to the
* next state, you never jump or go backwards. The SSM functions help you
* implement such a machine.
*
* e.g. S1 --> S2 --> S3 --> S4
* S1 is the start state
* There is also an implicit error state and an implicit accepting state
* (both with implicit edges from every state).
*
* You can also jump to any arbitrary state (while marking completion of the
* current state) while the machine is running. In other words there are
* implicit edges linking one state to every other state. OK, we're stretching
* the "state machine" description at this point.
*
* To create a ssm, you pass a state handler function and the total number of
* states (4 in the above example).
*
* To start a ssm, you pass in a completion callback function which gets
* called when the ssm completes (both on error and on failure).
*
* To iterate to the next state, call fpi_ssm_next_state(). It is legal to
* attempt to iterate beyond the final state - this is equivalent to marking
* the ssm as successfully completed.
*
* To mark successful completion of a SSM, either iterate beyond the final
* state or call fpi_ssm_mark_completed() from any state.
*
* To mark failed completion of a SSM, call fpi_ssm_mark_aborted() from any
* state. You must pass a non-zero error code.
*
* Your state handling function looks at ssm->cur_state in order to determine
* the current state and hence which operations to perform (a switch statement
* is appropriate).
* Typically, the state handling function fires off an asynchronous libusb
* transfer, and the callback function iterates the machine to the next state
* upon success (or aborts the machine on transfer failure).
*
* Your completion callback should examine ssm->error in order to determine
* whether the ssm completed or failed. An error code of zero indicates
* successful completion.
*/
/* Allocate a new ssm */
struct fpi_ssm *fpi_ssm_new(struct fp_dev *dev, ssm_handler_fn handler,
int nr_states)
{
struct fpi_ssm *machine;
BUG_ON(nr_states < 1);
machine = g_malloc0(sizeof(*machine));
machine->handler = handler;
machine->nr_states = nr_states;
machine->dev = dev;
machine->completed = TRUE;
return machine;
}
/* Free a ssm */
void fpi_ssm_free(struct fpi_ssm *machine)
{
if (!machine)
return;
g_free(machine);
}
/* Invoke the state handler */
static void __ssm_call_handler(struct fpi_ssm *machine)
{
fp_dbg("%p entering state %d", machine, machine->cur_state);
machine->handler(machine);
}
/* Start a ssm. You can also restart a completed or aborted ssm. */
void fpi_ssm_start(struct fpi_ssm *ssm, ssm_completed_fn callback)
{
BUG_ON(!ssm->completed);
ssm->callback = callback;
ssm->cur_state = 0;
ssm->completed = FALSE;
ssm->error = 0;
__ssm_call_handler(ssm);
}
static void __subsm_complete(struct fpi_ssm *ssm)
{
struct fpi_ssm *parent = ssm->parentsm;
BUG_ON(!parent);
if (ssm->error)
fpi_ssm_mark_aborted(parent, ssm->error);
else
fpi_ssm_next_state(parent);
fpi_ssm_free(ssm);
}
/* start a SSM as a child of another. if the child completes successfully, the
* parent will be advanced to the next state. if the child aborts, the parent
* will be aborted with the same error code. the child will be automatically
* freed upon completion/abortion. */
void fpi_ssm_start_subsm(struct fpi_ssm *parent, struct fpi_ssm *child)
{
child->parentsm = parent;
fpi_ssm_start(child, __subsm_complete);
}
/* Mark a ssm as completed successfully. */
void fpi_ssm_mark_completed(struct fpi_ssm *machine)
{
BUG_ON(machine->completed);
machine->completed = TRUE;
fp_dbg("%p completed with status %d", machine, machine->error);
if (machine->callback)
machine->callback(machine);
}
/* Mark a ssm as aborted with error. */
void fpi_ssm_mark_aborted(struct fpi_ssm *machine, int error)
{
fp_dbg("error %d from state %d", error, machine->cur_state);
BUG_ON(error == 0);
machine->error = error;
fpi_ssm_mark_completed(machine);
}
/* Iterate to next state of a ssm */
void fpi_ssm_next_state(struct fpi_ssm *machine)
{
BUG_ON(machine->completed);
machine->cur_state++;
if (machine->cur_state == machine->nr_states) {
fpi_ssm_mark_completed(machine);
} else {
__ssm_call_handler(machine);
}
}
void fpi_ssm_jump_to_state(struct fpi_ssm *machine, int state)
{
BUG_ON(machine->completed);
BUG_ON(state >= machine->nr_states);
machine->cur_state = state;
__ssm_call_handler(machine);
}

1
libfprint/empty_file Normal file
View File

@@ -0,0 +1 @@

View File

@@ -21,56 +21,40 @@
#define __FPRINT_INTERNAL_H__
#include <config.h>
#include <stdint.h>
#include <stdint.h>
#include <errno.h>
#include <glib.h>
#include <libusb.h>
#include <fprint.h>
#include "nbis-helpers.h"
#include "fprint.h"
#include "fpi-dev.h"
#include "fpi-core.h"
#include "fpi-log.h"
#include "fpi-dev-img.h"
#include "fpi-data.h"
#include "fpi-img.h"
#include "drivers/driver_ids.h"
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/* Global variables */
extern libusb_context *fpi_usb_ctx;
extern GSList *opened_devices;
enum fpi_log_level {
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR,
/* fp_print_data structure definition */
enum fp_print_data_type {
PRINT_DATA_RAW = 0, /* memset-imposed default */
PRINT_DATA_NBIS_MINUTIAE
};
void fpi_log(enum fpi_log_level, const char *component, const char *function,
const char *format, ...);
#ifndef FP_COMPONENT
#define FP_COMPONENT NULL
#endif
#ifdef ENABLE_LOGGING
#define _fpi_log(level, fmt...) fpi_log(level, FP_COMPONENT, __FUNCTION__, fmt)
#else
#define _fpi_log(level, fmt...)
#endif
#ifdef ENABLE_DEBUG_LOGGING
#define fp_dbg(fmt...) _fpi_log(LOG_LEVEL_DEBUG, fmt)
#else
#define fp_dbg(fmt...)
#endif
#define fp_info(fmt...) _fpi_log(LOG_LEVEL_INFO, fmt)
#define fp_warn(fmt...) _fpi_log(LOG_LEVEL_WARNING, fmt)
#define fp_err(fmt...) _fpi_log(LOG_LEVEL_ERROR, fmt)
#ifndef NDEBUG
#define BUG_ON(condition) \
if ((condition)) fp_err("BUG at %s:%d", __FILE__, __LINE__)
#else
#define BUG_ON(condition)
#endif
#define BUG() BUG_ON(1)
struct fp_print_data {
uint16_t driver_id;
uint32_t devtype;
enum fp_print_data_type type;
GSList *prints;
};
/* fp_dev structure definition */
enum fp_dev_state {
DEV_STATE_INITIAL = 0,
DEV_STATE_ERROR,
@@ -89,191 +73,88 @@ enum fp_dev_state {
DEV_STATE_IDENTIFYING,
DEV_STATE_IDENTIFY_DONE,
DEV_STATE_IDENTIFY_STOPPING,
DEV_STATE_CAPTURE_STARTING,
DEV_STATE_CAPTURING,
DEV_STATE_CAPTURE_DONE,
DEV_STATE_CAPTURE_STOPPING,
};
struct fp_driver **fprint_get_drivers (void);
struct fp_dev {
struct fp_driver *drv;
libusb_device_handle *udev;
uint32_t devtype;
void *priv;
/* only valid if drv->type == DRIVER_IMAGING */
struct fp_img_dev *img_dev;
/* Link to the instance specific struct */
void *instance_data;
int nr_enroll_stages;
/* FIXME: This will eventually have a bus type */
libusb_device_handle *udev;
/* read-only to drivers */
struct fp_print_data *verify_data;
/* drivers should not mess with any of the below */
enum fp_dev_state state;
int __enroll_stage;
int unconditional_capture;
/* async I/O callbacks and data */
/* FIXME: convert this to generic state operational data mechanism? */
fp_dev_open_cb open_cb;
void *open_cb_data;
fp_dev_close_cb close_cb;
fp_operation_stop_cb close_cb;
void *close_cb_data;
fp_enroll_stage_cb enroll_stage_cb;
void *enroll_stage_cb_data;
fp_enroll_stop_cb enroll_stop_cb;
fp_operation_stop_cb enroll_stop_cb;
void *enroll_stop_cb_data;
fp_verify_cb verify_cb;
fp_img_operation_cb verify_cb;
void *verify_cb_data;
fp_verify_stop_cb verify_stop_cb;
fp_operation_stop_cb verify_stop_cb;
void *verify_stop_cb_data;
fp_identify_cb identify_cb;
void *identify_cb_data;
fp_identify_stop_cb identify_stop_cb;
fp_operation_stop_cb identify_stop_cb;
void *identify_stop_cb_data;
fp_img_operation_cb capture_cb;
void *capture_cb_data;
fp_operation_stop_cb capture_stop_cb;
void *capture_stop_cb_data;
/* FIXME: better place to put this? */
struct fp_print_data **identify_gallery;
};
enum fp_imgdev_state {
IMGDEV_STATE_INACTIVE,
IMGDEV_STATE_AWAIT_FINGER_ON,
IMGDEV_STATE_CAPTURE,
IMGDEV_STATE_AWAIT_FINGER_OFF,
};
enum fp_imgdev_action {
IMG_ACTION_NONE = 0,
IMG_ACTION_ENROLL,
IMG_ACTION_VERIFY,
IMG_ACTION_IDENTIFY,
};
enum fp_imgdev_enroll_state {
IMG_ACQUIRE_STATE_NONE = 0,
IMG_ACQUIRE_STATE_ACTIVATING,
IMG_ACQUIRE_STATE_AWAIT_FINGER_ON,
IMG_ACQUIRE_STATE_AWAIT_IMAGE,
IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF,
IMG_ACQUIRE_STATE_DONE,
IMG_ACQUIRE_STATE_DEACTIVATING,
};
enum fp_imgdev_verify_state {
IMG_VERIFY_STATE_NONE = 0,
IMG_VERIFY_STATE_ACTIVATING
};
/* fp_img_dev structure definition */
struct fp_img_dev {
struct fp_dev *dev;
libusb_device_handle *udev;
struct fp_dev *parent;
enum fp_imgdev_action action;
int action_state;
struct fp_print_data *acquire_data;
struct fp_print_data *enroll_data;
struct fp_img *acquire_img;
int enroll_stage;
int action_result;
/* FIXME: better place to put this? */
size_t identify_match_offset;
void *priv;
};
int fpi_imgdev_capture(struct fp_img_dev *imgdev, int unconditional,
struct fp_img **image);
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
struct usb_id {
uint16_t vendor;
uint16_t product;
unsigned long driver_data;
};
enum fp_driver_type {
DRIVER_PRIMITIVE = 0,
DRIVER_IMAGING = 1,
};
struct fp_driver {
const uint16_t id;
const char *name;
const char *full_name;
const struct usb_id * const id_table;
enum fp_driver_type type;
enum fp_scan_type scan_type;
void *priv;
/* Device operations */
int (*discover)(struct libusb_device_descriptor *dsc, uint32_t *devtype);
int (*open)(struct fp_dev *dev, unsigned long driver_data);
void (*close)(struct fp_dev *dev);
int (*enroll_start)(struct fp_dev *dev);
int (*enroll_stop)(struct fp_dev *dev);
int (*verify_start)(struct fp_dev *dev);
int (*verify_stop)(struct fp_dev *dev, gboolean iterating);
int (*identify_start)(struct fp_dev *dev);
int (*identify_stop)(struct fp_dev *dev, gboolean iterating);
};
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
/* flags for fp_img_driver.flags */
#define FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE (1 << 0)
struct fp_img_driver {
struct fp_driver driver;
uint16_t flags;
int img_width;
int img_height;
int bz3_threshold;
/* Device operations */
int (*open)(struct fp_img_dev *dev, unsigned long driver_data);
void (*close)(struct fp_img_dev *dev);
int (*activate)(struct fp_img_dev *dev, enum fp_imgdev_state state);
int (*change_state)(struct fp_img_dev *dev, enum fp_imgdev_state state);
void (*deactivate)(struct fp_img_dev *dev);
};
#ifdef ENABLE_UPEKTS
extern struct fp_driver upekts_driver;
#endif
#ifdef ENABLE_UPEKE2
extern struct fp_driver upeke2_driver;
#endif
#ifdef ENABLE_UPEKTC
extern struct fp_img_driver upektc_driver;
#endif
#ifdef ENABLE_UPEKSONLY
extern struct fp_img_driver upeksonly_driver;
#endif
#ifdef ENABLE_URU4000
extern struct fp_img_driver uru4000_driver;
#endif
#ifdef ENABLE_AES1610
extern struct fp_img_driver aes1610_driver;
#endif
#ifdef ENABLE_AES2501
extern struct fp_img_driver aes2501_driver;
#endif
#ifdef ENABLE_AES4000
extern struct fp_img_driver aes4000_driver;
#endif
#ifdef ENABLE_FDU2000
extern struct fp_img_driver fdu2000_driver;
#endif
#ifdef ENABLE_VCOM5S
extern struct fp_img_driver vcom5s_driver;
#endif
extern libusb_context *fpi_usb_ctx;
extern GSList *opened_devices;
void fpi_img_driver_setup(struct fp_img_driver *idriver);
/* fp_driver structure definition */
/* fp_img_driver structure definition */
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define fpi_driver_to_img_driver(drv) \
container_of((drv), struct fp_img_driver, driver)
/* fp_dscv_dev structure definition */
struct fp_dscv_dev {
struct libusb_device *udev;
struct fp_driver *drv;
@@ -281,6 +162,7 @@ struct fp_dscv_dev {
uint32_t devtype;
};
/* fp_dscv_print structure definition */
struct fp_dscv_print {
uint16_t driver_id;
uint32_t devtype;
@@ -288,146 +170,72 @@ struct fp_dscv_print {
char *path;
};
enum fp_print_data_type {
PRINT_DATA_RAW = 0, /* memset-imposed default */
PRINT_DATA_NBIS_MINUTIAE,
/* fp_minutia structure definition */
struct fp_minutia {
int x;
int y;
int ex;
int ey;
int direction;
double reliability;
int type;
int appearing;
int feature_id;
int *nbrs;
int *ridge_counts;
int num_nbrs;
};
struct fp_print_data {
uint16_t driver_id;
uint32_t devtype;
enum fp_print_data_type type;
size_t length;
unsigned char data[0];
};
struct fpi_print_data_fp1 {
char prefix[3];
uint16_t driver_id;
uint32_t devtype;
unsigned char data_type;
unsigned char data[0];
} __attribute__((__packed__));
void fpi_data_exit(void);
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length);
gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
enum fp_print_data_type type2);
/* fp_minutiae structure definition */
struct fp_minutiae {
int alloc;
int num;
struct fp_minutia **list;
};
/* bit values for fp_img.flags */
#define FP_IMG_V_FLIPPED (1<<0)
#define FP_IMG_H_FLIPPED (1<<1)
#define FP_IMG_COLORS_INVERTED (1<<2)
#define FP_IMG_BINARIZED_FORM (1<<3)
/* Defined in fpi-dev-img.c */
void fpi_img_driver_setup(struct fp_img_driver *idriver);
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
#define FP_IMG_STANDARDIZATION_FLAGS (FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED \
| FP_IMG_COLORS_INVERTED)
/* Exported for use in command-line tools
* Defined in fpi-core.c */
struct fp_driver **fprint_get_drivers (void);
struct fp_img {
int width;
int height;
size_t length;
uint16_t flags;
struct fp_minutiae *minutiae;
unsigned char *binarized;
unsigned char data[0];
};
/* Defined in fpi-core.c */
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
struct fp_img *fpi_img_new(size_t length);
struct fp_img *fpi_img_new_for_imgdev(struct fp_img_dev *dev);
struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize);
/* Defined in fpi-data.c */
void fpi_data_exit(void);
gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
enum fp_print_data_type type2);
/* Defined in fpi-img.c */
gboolean fpi_img_is_sane(struct fp_img *img);
int fpi_img_detect_minutiae(struct fp_img *img);
int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
struct fp_print_data **ret);
int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
struct fp_print_data *new_print);
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
struct fp_print_data **gallery, int match_threshold, size_t *match_offset);
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int factor);
/* polling and timeouts */
/* Defined in fpi-poll.c */
void fpi_timeout_cancel_all_for_dev(struct fp_dev *dev);
void fpi_poll_init(void);
void fpi_poll_exit(void);
typedef void (*fpi_timeout_fn)(void *data);
struct fpi_timeout;
struct fpi_timeout *fpi_timeout_add(unsigned int msec, fpi_timeout_fn callback,
void *data);
void fpi_timeout_cancel(struct fpi_timeout *timeout);
/* async drv <--> lib comms */
struct fpi_ssm;
typedef void (*ssm_completed_fn)(struct fpi_ssm *ssm);
typedef void (*ssm_handler_fn)(struct fpi_ssm *ssm);
/* sequential state machine: state machine that iterates sequentially over
* a predefined series of states. can be aborted by either completion or
* abortion error conditions. */
struct fpi_ssm {
struct fp_dev *dev;
struct fpi_ssm *parentsm;
void *priv;
int nr_states;
int cur_state;
gboolean completed;
int error;
ssm_completed_fn callback;
ssm_handler_fn handler;
};
/* for library and drivers */
struct fpi_ssm *fpi_ssm_new(struct fp_dev *dev, ssm_handler_fn handler,
int nr_states);
void fpi_ssm_free(struct fpi_ssm *machine);
void fpi_ssm_start(struct fpi_ssm *machine, ssm_completed_fn callback);
void fpi_ssm_start_subsm(struct fpi_ssm *parent, struct fpi_ssm *child);
int fpi_ssm_has_completed(struct fpi_ssm *machine);
/* for drivers */
void fpi_ssm_next_state(struct fpi_ssm *machine);
void fpi_ssm_jump_to_state(struct fpi_ssm *machine, int state);
void fpi_ssm_mark_completed(struct fpi_ssm *machine);
void fpi_ssm_mark_aborted(struct fpi_ssm *machine, int error);
void fpi_drvcb_open_complete(struct fp_dev *dev, int status);
void fpi_drvcb_close_complete(struct fp_dev *dev);
void fpi_drvcb_enroll_started(struct fp_dev *dev, int status);
void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
struct fp_print_data *data, struct fp_img *img);
void fpi_drvcb_enroll_stopped(struct fp_dev *dev);
void fpi_drvcb_verify_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
/* Defined in fpi-async.c */
void fpi_drvcb_capture_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
struct fp_img *img);
void fpi_drvcb_verify_stopped(struct fp_dev *dev);
void fpi_drvcb_capture_stopped(struct fp_dev *dev);
void fpi_drvcb_identify_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
size_t match_offset, struct fp_img *img);
void fpi_drvcb_identify_stopped(struct fp_dev *dev);
/* for image drivers */
void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status);
void fpi_imgdev_close_complete(struct fp_img_dev *imgdev);
void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status);
void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev);
void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
gboolean present);
void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img);
void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error);
#include "drivers_definitions.h"
#endif

487
libfprint/fpi-assembling.c Normal file
View File

@@ -0,0 +1,487 @@
/*
* Image assembling routines
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2013 Arseniy Lartsev <arseniy@chalmers.se>
* Copyright (C) 2015 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 "assembling"
#include "fp_internal.h"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <glib.h>
#include "fpi-assembling.h"
/**
* SECTION:fpi-assembling
* @title: Image frame assembly
* @short_description: Image frame assembly helpers
*
* Those are the helpers to manipulate capture data from fingerprint readers
* into a uniform image that can be further processed. This is usually used
* by drivers for devices which have a small sensor and thus need to capture
* data in small stripes.
*/
static unsigned int calc_error(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *first_frame,
struct fpi_frame *second_frame,
int dx,
int dy)
{
unsigned int width, height;
unsigned int x1, y1, x2, y2, err, i, j;
width = ctx->frame_width - (dx > 0 ? dx : -dx);
height = ctx->frame_height - dy;
y1 = 0;
y2 = dy;
i = 0;
err = 0;
do {
x1 = dx < 0 ? 0 : dx;
x2 = dx < 0 ? -dx : 0;
j = 0;
do {
unsigned char v1, v2;
v1 = ctx->get_pixel(ctx, first_frame, x1, y1);
v2 = ctx->get_pixel(ctx, second_frame, x2, y2);
err += v1 > v2 ? v1 - v2 : v2 - v1;
j++;
x1++;
x2++;
} while (j < width);
i++;
y1++;
y2++;
} while (i < height);
/* Normalize error */
err *= (ctx->frame_height * ctx->frame_width);
err /= (height * width);
if (err == 0)
return INT_MAX;
return err;
}
/* This function is rather CPU-intensive. It's better to use hardware
* to detect movement direction when possible.
*/
static void find_overlap(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *first_frame,
struct fpi_frame *second_frame,
unsigned int *min_error)
{
int dx, dy;
unsigned int err;
*min_error = 255 * ctx->frame_height * ctx->frame_width;
/* Seeking in horizontal and vertical dimensions,
* for horizontal dimension we'll check only 8 pixels
* in both directions. For vertical direction diff is
* rarely less than 2, so start with it.
*/
for (dy = 2; dy < ctx->frame_height; dy++) {
for (dx = -8; dx < 8; dx++) {
err = calc_error(ctx, first_frame, second_frame,
dx, dy);
if (err < *min_error) {
*min_error = err;
second_frame->delta_x = -dx;
second_frame->delta_y = dy;
}
}
}
}
static unsigned int do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes,
gboolean reverse)
{
GSList *list_entry = stripes;
GTimer *timer;
int frame = 1;
struct fpi_frame *prev_stripe = list_entry->data;
unsigned int min_error;
/* Max error is width * height * 255, for AES2501 which has the largest
* sensor its 192*16*255 = 783360. So for 32bit value it's ~5482 frame before
* we might get int overflow. Use 64bit value here to prevent integer overflow
*/
unsigned long long total_error = 0;
list_entry = g_slist_next(list_entry);
timer = g_timer_new();
do {
struct fpi_frame *cur_stripe = list_entry->data;
if (reverse) {
find_overlap(ctx, prev_stripe, cur_stripe, &min_error);
cur_stripe->delta_y = -cur_stripe->delta_y;
cur_stripe->delta_x = -cur_stripe->delta_x;
}
else
find_overlap(ctx, cur_stripe, prev_stripe, &min_error);
total_error += min_error;
frame++;
prev_stripe = cur_stripe;
list_entry = g_slist_next(list_entry);
} while (frame < num_stripes);
g_timer_stop(timer);
fp_dbg("calc delta completed in %f secs", g_timer_elapsed(timer, NULL));
g_timer_destroy(timer);
return total_error / num_stripes;
}
/**
* fpi_do_movement_estimation:
* @ctx: #fpi_frame_asmbl_ctx - frame assembling context
* @stripes: a singly-linked list of #fpi_frame
* @num_stripes: number of items in @stripes to process
*
* fpi_do_movement_estimation() estimates the movement between adjacent
* frames, populating @delta_x and @delta_y values for each #fpi_frame.
*
* This function is used for devices that don't do movement estimation
* in hardware. If hardware movement estimation is supported, the driver
* should populate @delta_x and @delta_y instead.
*
* Note that @num_stripes might be shorter than the length of the list,
* if some stripes should be skipped.
*/
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes)
{
int err, rev_err;
err = do_movement_estimation(ctx, stripes, num_stripes, FALSE);
rev_err = do_movement_estimation(ctx, stripes, num_stripes, TRUE);
fp_dbg("errors: %d rev: %d", err, rev_err);
if (err < rev_err) {
do_movement_estimation(ctx, stripes, num_stripes, FALSE);
}
}
static inline void aes_blit_stripe(struct fpi_frame_asmbl_ctx *ctx,
struct fp_img *img,
struct fpi_frame *stripe,
int x, int y)
{
unsigned int ix, iy;
unsigned int fx, fy;
unsigned int width, height;
/* Find intersection */
if (x < 0) {
width = ctx->frame_width + x;
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
width = ctx->frame_width;
}
if ((ix + width) > img->width)
width = img->width - ix;
if (y < 0) {
iy = 0;
fy = -y;
height = ctx->frame_height + y;
} else {
iy = y;
fy = 0;
height = ctx->frame_height;
}
if (fx > ctx->frame_width)
return;
if (fy > ctx->frame_height)
return;
if (ix > img->width)
return;
if (iy > img->height)
return;
if ((iy + height) > img->height)
height = img->height - iy;
for (; fy < height; fy++, iy++) {
if (x < 0) {
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
}
for (; fx < width; fx++, ix++) {
img->data[ix + (iy * img->width)] = ctx->get_pixel(ctx, stripe, fx, fy);
}
}
}
/**
* fpi_assemble_frames:
* @ctx: #fpi_frame_asmbl_ctx - frame assembling context
* @stripes: linked list of #fpi_frame
* @num_stripes: number of items in @stripes to process
*
* fpi_assemble_frames() assembles individual frames into a single image.
* It expects @delta_x and @delta_y of #fpi_frame to be populated.
*
* Note that @num_stripes might be shorter than the length of the list,
* if some stripes should be skipped.
*
* Returns: a newly allocated #fp_img.
*/
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes)
{
GSList *stripe;
struct fp_img *img;
int height = 0;
int i, y, x;
gboolean reverse = FALSE;
struct fpi_frame *fpi_frame;
//FIXME g_return_if_fail
BUG_ON(num_stripes == 0);
BUG_ON(ctx->image_width < ctx->frame_width);
/* Calculate height */
i = 0;
stripe = stripes;
/* No offset for 1st image */
fpi_frame = stripe->data;
fpi_frame->delta_x = 0;
fpi_frame->delta_y = 0;
do {
fpi_frame = stripe->data;
height += fpi_frame->delta_y;
i++;
stripe = g_slist_next(stripe);
} while (i < num_stripes);
fp_dbg("height is %d", height);
if (height < 0) {
reverse = TRUE;
height = -height;
}
/* For last frame */
height += ctx->frame_height;
/* Create buffer big enough for max image */
img = fpi_img_new(ctx->image_width * height);
img->flags = FP_IMG_COLORS_INVERTED;
img->flags |= reverse ? 0 : FP_IMG_H_FLIPPED | FP_IMG_V_FLIPPED;
img->width = ctx->image_width;
img->height = height;
/* Assemble stripes */
i = 0;
stripe = stripes;
y = reverse ? (height - ctx->frame_height) : 0;
x = (ctx->image_width - ctx->frame_width) / 2;
do {
fpi_frame = stripe->data;
if(reverse) {
y += fpi_frame->delta_y;
x += fpi_frame->delta_x;
}
aes_blit_stripe(ctx, img, fpi_frame, x, y);
if(!reverse) {
y += fpi_frame->delta_y;
x += fpi_frame->delta_x;
}
stripe = g_slist_next(stripe);
i++;
} while (i < num_stripes);
return img;
}
static int cmpint(const void *p1, const void *p2, gpointer data)
{
int a = *((int *)p1);
int b = *((int *)p2);
if (a < b)
return -1;
else if (a == b)
return 0;
else
return 1;
}
static void median_filter(int *data, int size, int filtersize)
{
int i;
int *result = (int *)g_malloc0(size*sizeof(int));
int *sortbuf = (int *)g_malloc0(filtersize*sizeof(int));
for (i = 0; i < size; i++) {
int i1 = i - (filtersize-1)/2;
int i2 = i + (filtersize-1)/2;
if (i1 < 0)
i1 = 0;
if (i2 >= size)
i2 = size-1;
memmove(sortbuf, data+i1, (i2-i1+1)*sizeof(int));
g_qsort_with_data(sortbuf, i2-i1+1, sizeof(int), cmpint, NULL);
result[i] = sortbuf[(i2-i1+1)/2];
}
memmove(data, result, size*sizeof(int));
g_free(result);
g_free(sortbuf);
}
static void interpolate_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *line1, float y1, GSList *line2,
float y2, unsigned char *output, float yi, int size)
{
int i;
unsigned char p1, p2;
if (!line1 || !line2)
return;
for (i = 0; i < size; i++) {
p1 = ctx->get_pixel(ctx, line1, i);
p2 = ctx->get_pixel(ctx, line2, i);
output[i] = (float)p1
+ (yi - y1)/(y2 - y1)*(p2 - p1);
}
}
/**
* fpi_assemble_lines:
* @ctx: #fpi_frame_asmbl_ctx - frame assembling context
* @lines: linked list of lines
* @num_lines: number of items in @lines to process
*
* #fpi_assemble_lines assembles individual lines into a single image.
* It also rescales image to account variable swiping speed.
*
* Note that @num_lines might be shorter than the length of the list,
* if some lines should be skipped.
*
* Returns: a newly allocated #fp_img.
*/
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *lines, size_t num_lines)
{
/* Number of output lines per distance between two scanners */
int i;
GSList *row1, *row2;
float y = 0.0;
int line_ind = 0;
int *offsets = (int *)g_malloc0((num_lines / 2) * sizeof(int));
unsigned char *output = g_malloc0(ctx->line_width * ctx->max_height);
struct fp_img *img;
g_return_val_if_fail (lines != NULL, NULL);
g_return_val_if_fail (num_lines >= 2, NULL);
fp_dbg("%"G_GINT64_FORMAT, g_get_real_time());
row1 = lines;
for (i = 0; (i < num_lines - 1) && row1; i += 2) {
int bestmatch = i;
int bestdiff = 0;
int j, firstrow, lastrow;
firstrow = i + 1;
lastrow = MIN(i + ctx->max_search_offset, num_lines - 1);
row2 = g_slist_next(row1);
for (j = firstrow; j <= lastrow; j++) {
int diff = ctx->get_deviation(ctx,
row1,
row2);
if ((j == firstrow) || (diff < bestdiff)) {
bestdiff = diff;
bestmatch = j;
}
row2 = g_slist_next(row2);
}
offsets[i / 2] = bestmatch - i;
fp_dbg("%d", offsets[i / 2]);
row1 = g_slist_next(row1);
if (row1)
row1 = g_slist_next(row1);
}
median_filter(offsets, (num_lines / 2) - 1, ctx->median_filter_size);
fp_dbg("offsets_filtered: %"G_GINT64_FORMAT, g_get_real_time());
for (i = 0; i <= (num_lines / 2) - 1; i++)
fp_dbg("%d", offsets[i]);
row1 = lines;
for (i = 0; i < num_lines - 1; i++, row1 = g_slist_next(row1)) {
int offset = offsets[i/2];
if (offset > 0) {
float ynext = y + (float)ctx->resolution / offset;
while (line_ind < ynext) {
if (line_ind > ctx->max_height - 1)
goto out;
interpolate_lines(ctx,
row1, y,
g_slist_next(row1),
ynext,
output + line_ind * ctx->line_width,
line_ind,
ctx->line_width);
line_ind++;
}
y = ynext;
}
}
out:
img = fpi_img_new(ctx->line_width * line_ind);
img->height = line_ind;
img->width = ctx->line_width;
img->flags = FP_IMG_V_FLIPPED;
memmove(img->data, output, ctx->line_width * line_ind);
g_free(offsets);
g_free(output);
return img;
}

115
libfprint/fpi-assembling.h Normal file
View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2015 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
*/
#ifndef __FPI_ASSEMBLING_H__
#define __FPI_ASSEMBLING_H__
#include <fprint.h>
/**
* fpi_frame:
* @delta_x: X offset of the frame
* @delta_y: Y offset of the frame
* @data: bitmap
*
* #fpi_frame is used to store frames for swipe sensors. Drivers should
* populate delta_x and delta_y if the device supports hardware movement
* estimation.
*/
struct fpi_frame {
int delta_x;
int delta_y;
unsigned char data[0];
};
/**
* fpi_frame_asmbl_ctx:
* @frame_width: width of the frame
* @frame_height: height of the frame
* @image_width: resulting image width
* @get_pixel: pixel accessor, returns pixel brightness at x,y of frame
*
* #fpi_frame_asmbl_ctx is a structure holding the context for frame
* assembling routines.
*
* Drivers should define their own #fpi_frame_asmbl_ctx depending on
* hardware parameters of scanner. @image_width is usually 25% wider than
* @frame_width to take horizontal movement into account.
*/
struct fpi_frame_asmbl_ctx {
unsigned int frame_width;
unsigned int frame_height;
unsigned int image_width;
unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y);
};
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes);
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes);
/**
* fpi_line_asmbl_ctx:
* @line_width: width of line
* @max_height: maximal height of assembled image
* @resolution: scale factor used for line assembling routines.
* @median_filter_size: size of median filter for movement estimation
* @max_search_offset: the number of lines to search for the next line
* @get_deviation: pointer to a function that returns the numerical difference
* between two lines
* @get_pixel: pixel accessor, returns pixel brightness at x of line
*
* #fpi_line_asmbl_ctx is a structure holding the context for line assembling
* routines.
*
* Drivers should define their own #fpi_line_asmbl_ctx depending on
* the hardware parameters of the scanner. Swipe scanners of this type usually
* return two lines, the second line is often narrower than first and is used
* for movement estimation.
*
* The @max_search_offset value indicates how many lines forward the assembling
* routines should look while searching for next line. This value depends on
* how fast the hardware sends frames.
*
* The function pointed to by @get_deviation should return the numerical difference
* between two lines. Higher values means lines are more different. If the reader
* returns two lines at a time, this function should be used to estimate the
* difference between pairs of lines.
*/
struct fpi_line_asmbl_ctx {
unsigned int line_width;
unsigned int max_height;
unsigned int resolution;
unsigned int median_filter_size;
unsigned int max_search_offset;
int (*get_deviation)(struct fpi_line_asmbl_ctx *ctx,
GSList *line1, GSList *line2);
unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx,
GSList *line,
unsigned int x);
};
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *lines, size_t num_lines);
#endif

View File

@@ -19,11 +19,21 @@
#define FP_COMPONENT "async"
#include "fp_internal.h"
#include "fpi-async.h"
#include <config.h>
#include <errno.h>
#include <glib.h>
#include "fp_internal.h"
/*
* SECTION:fpi-async
* @title: Asynchronous operations reporting
* @short_description: Asynchronous operations reporting functions
*
* Those functions are used by primitive drivers to report back their
* current status. Most drivers, imaging ones, do not need to use them.
*/
/* Drivers call this when device initialisation has completed */
void fpi_drvcb_open_complete(struct fp_dev *dev, int status)
@@ -36,15 +46,35 @@ void fpi_drvcb_open_complete(struct fp_dev *dev, int status)
dev->open_cb(dev, status, dev->open_cb_data);
}
API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb cb,
/**
* fp_async_dev_open:
* @ddev: the struct #fp_dscv_dev discovered device to open
* @callback: the callback to call when the device has been opened
* @user_data: user data to pass to the callback
*
* Opens and initialises a device. This is the function you call in order
* to convert a #fp_dscv_dev discovered device into an actual device handle
* that you can perform operations with.
*
* The error status of the opening will be provided as an argument to the
* #fp_dev_open_cb callback.
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb callback,
void *user_data)
{
struct fp_driver *drv = ddev->drv;
struct fp_driver *drv;
struct fp_dev *dev;
libusb_device_handle *udevh;
int r;
fp_dbg("");
g_return_val_if_fail(ddev != NULL, -ENODEV);
g_return_val_if_fail (callback != NULL, -EINVAL);
drv = ddev->drv;
G_DEBUG_HERE();
r = libusb_open(ddev->udev, &udevh);
if (r < 0) {
fp_err("usb_open failed, error %d", r);
@@ -56,7 +86,7 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb cb,
dev->udev = udevh;
dev->__enroll_stage = -1;
dev->state = DEV_STATE_INITIALIZING;
dev->open_cb = cb;
dev->open_cb = callback;
dev->open_cb_data = user_data;
if (!drv->open) {
@@ -78,19 +108,35 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb cb,
/* Drivers call this when device deinitialisation has completed */
void fpi_drvcb_close_complete(struct fp_dev *dev)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
dev->state = DEV_STATE_DEINITIALIZED;
fpi_timeout_cancel_all_for_dev(dev);
libusb_close(dev->udev);
if (dev->close_cb)
dev->close_cb(dev, dev->close_cb_data);
g_free(dev);
}
/**
* fp_async_dev_close:
* @dev: the struct #fp_dev device
* @callback: the callback to call when the device has been closed
* @user_data: user data to pass to the callback
*
* Closes a device. You must call this function when you have finished using
* a fingerprint device.
*/
API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
fp_dev_close_cb callback, void *user_data)
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
g_return_if_fail (dev != NULL);
drv = dev->drv;
g_return_if_fail (drv->close != NULL);
if (g_slist_index(opened_devices, (gconstpointer) dev) == -1)
fp_err("device %p not in opened list!", dev);
@@ -98,12 +144,6 @@ API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
dev->close_cb = callback;
dev->close_cb_data = user_data;
if (!drv->close) {
fpi_drvcb_close_complete(dev);
return;
}
dev->state = DEV_STATE_DEINITIALIZING;
drv->close(dev);
}
@@ -127,12 +167,29 @@ void fpi_drvcb_enroll_started(struct fp_dev *dev, int status)
}
}
/**
* fp_async_enroll_start:
* @dev: the struct #fp_dev device
* @callback: the callback to call for each stage of the enrollment
* @user_data: user data to pass to the callback
*
* Starts an enrollment and calls @callback for each enrollment stage.
* See [Enrolling](libfprint-Devices-operations.html#enrolling)
* for an explanation of enroll stages.
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_enroll_start(struct fp_dev *dev,
fp_enroll_stage_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
g_return_val_if_fail (callback != NULL, -EINVAL);
drv = dev->drv;
if (!dev->nr_enroll_stages || !drv->enroll_start) {
fp_err("driver %s has 0 enroll stages or no enroll func",
drv->name);
@@ -174,20 +231,34 @@ void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
/* Drivers call this when enrollment has stopped */
void fpi_drvcb_enroll_stopped(struct fp_dev *dev)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_ENROLL_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->enroll_stop_cb)
dev->enroll_stop_cb(dev, dev->enroll_stop_cb_data);
}
/**
* fp_async_enroll_stop:
* @dev: the struct #fp_dev device
* @callback: the callback to call when the enrollment has been cancelled
* @user_data: user data to pass to the callback
*
* Stops an ongoing enrollment started with fp_async_enroll_start().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_enroll_stop(struct fp_dev *dev,
fp_enroll_stop_cb callback, void *user_data)
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->enroll_start)
return -ENOTSUP;
@@ -210,13 +281,32 @@ API_EXPORTED int fp_async_enroll_stop(struct fp_dev *dev,
return r;
}
/**
* fp_async_verify_start:
* @dev: the struct #fp_dev device
* @data: the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan
* @callback: the callback to call when the verification has finished
* @user_data: user data to pass to the callback
*
* Starts a verification and calls @callback when the verification has
* finished. See fp_verify_finger_img() for the synchronous API. When the
* @callback has been called, you must call fp_async_verify_stop().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
struct fp_print_data *data, fp_verify_cb callback, void *user_data)
struct fp_print_data *data, fp_img_operation_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
g_return_val_if_fail (callback != NULL, -EINVAL);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->verify_start)
return -ENOTSUP;
@@ -237,7 +327,7 @@ API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
/* Drivers call this when verification has started */
void fpi_drvcb_verify_started(struct fp_dev *dev, int status)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_VERIFY_STARTING);
if (status) {
if (status > 0) {
@@ -271,21 +361,47 @@ void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
/* Drivers call this when verification has stopped */
void fpi_drvcb_verify_stopped(struct fp_dev *dev)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_VERIFY_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->verify_stop_cb)
dev->verify_stop_cb(dev, dev->verify_stop_cb_data);
}
/**
* fp_async_verify_stop:
* @dev: the struct #fp_dev device
* @callback: the callback to call to finish a verification
* @user_data: user data to pass to the callback
*
* Finishes an ongoing verification started with fp_async_verify_start().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
fp_verify_stop_cb callback, void *user_data)
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
gboolean iterating = (dev->state == DEV_STATE_VERIFYING);
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
G_DEBUG_HERE();
if (dev->state == DEV_STATE_VERIFY_STOPPING) {
fp_dbg ("Already stopping verification, returning -EINPROGRESS");
return -EINPROGRESS;
}
if (dev->state == DEV_STATE_INITIALIZED) {
if (callback)
callback(dev, user_data);
return 0;
}
drv = dev->drv;
BUG_ON(dev->state != DEV_STATE_ERROR
&& dev->state != DEV_STATE_VERIFYING
&& dev->state != DEV_STATE_VERIFY_DONE);
@@ -311,13 +427,32 @@ API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
return r;
}
/**
* fp_async_identify_start:
* @dev: the struct #fp_dev device
* @gallery: NULL-terminated array of pointers to the prints to
* identify against. Each one must have been previously enrolled with a device
* compatible to the device selected to perform the scan
* @callback: the callback to call when the identification has finished
* @user_data: user data to pass to the callback
*
* Performs a new scan and verifies it against a previously enrolled print.
* See also: fp_verify_finger_img()
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_identify_start(struct fp_dev *dev,
struct fp_print_data **gallery, fp_identify_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
g_return_val_if_fail (callback != NULL, -EINVAL);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->identify_start)
return -ENOTSUP;
dev->state = DEV_STATE_IDENTIFY_STARTING;
@@ -369,14 +504,40 @@ void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
fp_dbg("ignoring verify result as no callback is subscribed");
}
/**
* fp_async_identify_stop:
* @dev: the struct #fp_dev device
* @callback: the callback to call when the identification has stopped
* @user_data: user data to pass to the callback
*
* Stops an ongoing identification started with fp_async_identify_start().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
fp_identify_stop_cb callback, void *user_data)
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
gboolean iterating = (dev->state == DEV_STATE_IDENTIFYING);
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
G_DEBUG_HERE();
if (dev->state == DEV_STATE_IDENTIFY_STOPPING) {
fp_dbg ("Already stopping identification, returning -EINPROGRESS");
return -EINPROGRESS;
}
if (dev->state == DEV_STATE_INITIALIZED) {
if (callback)
callback(dev, user_data);
return 0;
}
drv = dev->drv;
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
&& dev->state != DEV_STATE_IDENTIFY_DONE);
@@ -405,10 +566,141 @@ API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
/* Drivers call this when identification has stopped */
void fpi_drvcb_identify_stopped(struct fp_dev *dev)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_IDENTIFY_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->identify_stop_cb)
dev->identify_stop_cb(dev, dev->identify_stop_cb_data);
}
/**
* fp_async_capture_start:
* @dev: the struct #fp_dev device
* @unconditional: whether to unconditionally capture an image, or to only capture when a finger is detected
* @callback: the callback to call when the capture has finished
* @user_data: user data to pass to the callback
*
* Start the capture of an #fp_img from a device. When the @callback has been called,
* you must call fp_async_capture_stop().
*
* Returns: 0 on success, non-zero on error. -ENOTSUP indicates that either the
* @unconditional flag was set but the device does not support this, or that the
* device does not support imaging
*/
API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional,
fp_img_operation_cb callback, void *user_data)
{
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
g_return_val_if_fail (callback != NULL, -EINVAL);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->capture_start)
return -ENOTSUP;
dev->state = DEV_STATE_CAPTURE_STARTING;
dev->capture_cb = callback;
dev->capture_cb_data = user_data;
dev->unconditional_capture = unconditional;
r = drv->capture_start(dev);
if (r < 0) {
dev->capture_cb = NULL;
dev->state = DEV_STATE_ERROR;
fp_err("failed to start capture, error %d", r);
}
return r;
}
/* Drivers call this when capture has started */
void fpi_drvcb_capture_started(struct fp_dev *dev, int status)
{
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_CAPTURE_STARTING);
if (status) {
if (status > 0) {
status = -status;
fp_dbg("adjusted to %d", status);
}
dev->state = DEV_STATE_ERROR;
if (dev->capture_cb)
dev->capture_cb(dev, status, NULL, dev->capture_cb_data);
} else {
dev->state = DEV_STATE_CAPTURING;
}
}
/* Drivers call this to report a capture result (which might mark completion) */
void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
struct fp_img *img)
{
fp_dbg("result %d", result);
BUG_ON(dev->state != DEV_STATE_CAPTURING);
if (result < 0 || result == FP_CAPTURE_COMPLETE)
dev->state = DEV_STATE_CAPTURE_DONE;
if (dev->capture_cb)
dev->capture_cb(dev, result, img, dev->capture_cb_data);
else
fp_dbg("ignoring capture result as no callback is subscribed");
}
/* Drivers call this when capture has stopped */
void fpi_drvcb_capture_stopped(struct fp_dev *dev)
{
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_CAPTURE_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->capture_stop_cb)
dev->capture_stop_cb(dev, dev->capture_stop_cb_data);
}
/**
* fp_async_capture_stop:
* @dev: the struct #fp_dev device
* @callback: the callback to call when the capture has been stopped
* @user_data: user data to pass to the callback
*
* Stops an ongoing verification started with fp_async_capture_start().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_ERROR
&& dev->state != DEV_STATE_CAPTURING
&& dev->state != DEV_STATE_CAPTURE_DONE);
dev->capture_cb = NULL;
dev->capture_stop_cb = callback;
dev->capture_stop_cb_data = user_data;
dev->state = DEV_STATE_CAPTURE_STOPPING;
if (!drv->capture_start)
return -ENOTSUP;
if (!drv->capture_stop) {
dev->state = DEV_STATE_INITIALIZED;
fpi_drvcb_capture_stopped(dev);
return 0;
}
r = drv->capture_stop(dev);
if (r < 0) {
fp_err("failed to stop capture");
dev->capture_stop_cb = NULL;
}
return r;
}

39
libfprint/fpi-async.h Normal file
View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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
*/
#ifndef __FPI_ASYNC_H__
#define __FPI_ASYNC_H__
#include "fpi-dev.h"
#include "fpi-data.h"
void fpi_drvcb_open_complete(struct fp_dev *dev, int status);
void fpi_drvcb_close_complete(struct fp_dev *dev);
void fpi_drvcb_enroll_started(struct fp_dev *dev, int status);
void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
struct fp_print_data *data, struct fp_img *img);
void fpi_drvcb_enroll_stopped(struct fp_dev *dev);
void fpi_drvcb_verify_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
struct fp_img *img);
void fpi_drvcb_verify_stopped(struct fp_dev *dev);
#endif

835
libfprint/fpi-core.c Normal file
View File

@@ -0,0 +1,835 @@
/*
* Core functions for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <libusb.h>
#include "fp_internal.h"
libusb_context *fpi_usb_ctx = NULL;
GSList *opened_devices = NULL;
/**
* SECTION:discovery
* @title: Device discovery
* @short_description: Device discovery functions
*
* These functions allow you to scan the system for supported fingerprint
* scanning hardware. This is your starting point when integrating libfprint
* into your software.
*
* When you've identified a discovered device that you would like to control,
* you can open it with fp_dev_open(). Note that discovered devices may no
* longer be available at the time when you want to open them, for example
* the user may have unplugged the device.
*/
/**
* SECTION:drv
* @title: Driver operations
* @short_description: Driver operation functions
*
* Internally, libfprint is abstracted into various drivers to communicate
* with the different types of supported fingerprint readers. libfprint works
* hard so that you don't have to care about these internal abstractions,
* however there are some situations where you may be interested in a little
* behind-the-scenes driver info.
*
* You can obtain the driver for a device using fp_dev_get_driver(), which
* you can pass to the functions documented on this page.
*/
/**
* SECTION:dev
* @title: Devices operations
* @short_description: Device operation functions
*
* In order to interact with fingerprint scanners, your software will
* interface primarily with libfprint's representation of devices, detailed
* on this page.
*
* # Enrolling # {#enrolling}
*
* Enrolling is represented within libfprint as a multi-stage process. This
* slightly complicates things for application developers, but is required
* for a smooth process.
*
* Some devices require the user to scan their finger multiple times in
* order to complete the enrollment process. libfprint must return control
* to your application in-between each scan in order for your application to
* instruct the user to swipe their finger again. Each scan is referred to
* as a stage, so a device that requires 3 scans for enrollment corresponds
* to you running 3 enrollment stages using libfprint.
*
* The fp_dev_get_nr_enroll_stages() function can be used to find out how
* many enroll stages are needed.
*
* In order to complete an enroll stage, you call an enroll function such
* as fp_enroll_finger(). The return of this function does not necessarily
* indicate that a stage has completed though, as the user may not have
* produced a good enough scan. Each stage may have to be retried several
* times.
*
* The exact semantics of the enroll functions are described in the
* fp_enroll_finger() documentation. You should pay careful attention to the
* details.
*
* # Imaging # {#imaging}
*
* libfprint provides you with some ways to retrieve images of scanned
* fingers, such as the fp_dev_img_capture() function, or some enroll/verify
* function variants which provide images. You may wish to do something with
* such images in your application.
*
* However, you must be aware that not all hardware supported by libfprint
* operates like this. Most hardware does operate simply by sending
* fingerprint images to the host computer for further processing, but some
* devices do all fingerprint processing in hardware and do not present images
* to the host computer.
*
* You can use fp_dev_supports_imaging() to see if image capture is possible
* on a particular device. Your application must be able to cope with the
* fact that libfprint does support regular operations (e.g. enrolling and
* verification) on some devices which do not provide images.
*/
/**
* SECTION:fpi-core
* @title: Driver structures
* @short_description: Driver structures
*
* Driver structures need to be defined inside each driver in
* order for the core library to know what function to call, and the capabilities
* of the driver and the devices it supports.
*/
/**
* SECTION:fpi-core-img
* @title: Image driver structures
* @short_description: Image driver structures
*
* Image driver structures need to be defined inside each image driver in
* order for the core library to know what function to call, and the capabilities
* of the driver and the devices it supports. Its structure is based off the
* #fp_driver struct.
*/
static GSList *registered_drivers = NULL;
static void register_driver(struct fp_driver *drv)
{
if (drv->id == 0) {
fp_err("not registering driver %s: driver ID is 0", drv->name);
return;
}
registered_drivers = g_slist_prepend(registered_drivers, (gpointer) drv);
fp_dbg("registered driver %s", drv->name);
}
#include "drivers_arrays.h"
static void register_drivers(void)
{
unsigned int i;
for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++)
register_driver(primitive_drivers[i]);
for (i = 0; i < G_N_ELEMENTS(img_drivers); i++) {
struct fp_img_driver *imgdriver = img_drivers[i];
fpi_img_driver_setup(imgdriver);
register_driver(&imgdriver->driver);
}
}
API_EXPORTED struct fp_driver **fprint_get_drivers (void)
{
GPtrArray *array;
unsigned int i;
array = g_ptr_array_new ();
for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++)
g_ptr_array_add (array, primitive_drivers[i]);
for (i = 0; i < G_N_ELEMENTS(img_drivers); i++)
g_ptr_array_add (array, &(img_drivers[i]->driver));
/* Add a null item terminating the array */
g_ptr_array_add (array, NULL);
return (struct fp_driver **) g_ptr_array_free (array, FALSE);
}
static struct fp_driver *find_supporting_driver(libusb_device *udev,
const struct usb_id **usb_id, uint32_t *devtype)
{
int ret;
GSList *elem = registered_drivers;
struct libusb_device_descriptor dsc;
const struct usb_id *best_usb_id;
struct fp_driver *best_drv;
uint32_t best_devtype;
int drv_score = 0;
ret = libusb_get_device_descriptor(udev, &dsc);
if (ret < 0) {
fp_err("Failed to get device descriptor");
return NULL;
}
best_drv = NULL;
best_devtype = 0;
do {
struct fp_driver *drv = elem->data;
uint32_t type = 0;
const struct usb_id *id;
for (id = drv->id_table; id->vendor; id++) {
if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) {
if (drv->discover) {
int r = drv->discover(&dsc, &type);
if (r < 0)
fp_err("%s discover failed, code %d", drv->name, r);
if (r <= 0)
continue;
/* Has a discover function, and matched our device */
drv_score = 100;
} else {
/* Already got a driver as good */
if (drv_score >= 50)
continue;
drv_score = 50;
}
fp_dbg("driver %s supports USB device %04x:%04x",
drv->name, id->vendor, id->product);
best_usb_id = id;
best_drv = drv;
best_devtype = type;
/* We found the best possible driver */
if (drv_score == 100)
break;
}
}
} while ((elem = g_slist_next(elem)));
if (best_drv != NULL) {
fp_dbg("selected driver %s supports USB device %04x:%04x",
best_drv->name, dsc.idVendor, dsc.idProduct);
*devtype = best_devtype;
*usb_id = best_usb_id;
}
return best_drv;
}
static struct fp_dscv_dev *discover_dev(libusb_device *udev)
{
const struct usb_id *usb_id;
struct fp_driver *drv;
struct fp_dscv_dev *ddev;
uint32_t devtype;
drv = find_supporting_driver(udev, &usb_id, &devtype);
if (!drv)
return NULL;
ddev = g_malloc0(sizeof(*ddev));
ddev->drv = drv;
ddev->udev = udev;
ddev->driver_data = usb_id->driver_data;
ddev->devtype = devtype;
return ddev;
}
/**
* fp_discover_devs:
*
* Scans the system and returns a list of discovered devices. This is your
* entry point into finding a fingerprint reader to operate. Note that %NULL
* is only returned on error. When there are no supported readers available,
* an empty list is returned instead.
*
* Returns: a nul-terminated list of discovered devices or %NULL on error.
* Must be freed with fp_dscv_devs_free() after use.
*/
API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void)
{
GPtrArray *tmparray;
libusb_device *udev;
libusb_device **devs;
int r;
int i = 0;
g_return_val_if_fail (registered_drivers != NULL, NULL);
r = libusb_get_device_list(fpi_usb_ctx, &devs);
if (r < 0) {
fp_err("couldn't enumerate USB devices, error %d", r);
return NULL;
}
tmparray = g_ptr_array_new ();
/* Check each device against each driver, temporarily storing successfully
* discovered devices in a GPtrArray. */
while ((udev = devs[i++]) != NULL) {
struct fp_dscv_dev *ddev = discover_dev(udev);
if (!ddev)
continue;
/* discover_dev() doesn't hold a reference to the udev,
* so increase the reference for ddev to hold this ref */
libusb_ref_device(udev);
g_ptr_array_add (tmparray, (gpointer) ddev);
}
libusb_free_device_list(devs, 1);
/* Convert our temporary array into a standard NULL-terminated pointer
* array. */
g_ptr_array_add (tmparray, NULL);
return (struct fp_dscv_dev **) g_ptr_array_free (tmparray, FALSE);
}
/**
* fp_dscv_devs_free:
* @devs: the list of discovered devices. If %NULL, function simply
* returns.
*
* Free a list of discovered devices. This function destroys the list and all
* discovered devices that it included, so make sure you have opened your
* discovered device <emphasis role="strong">before</emphasis> freeing the list.
*/
API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs)
{
int i;
if (!devs)
return;
for (i = 0; devs[i]; i++) {
libusb_unref_device(devs[i]->udev);
g_free(devs[i]);
}
g_free(devs);
}
/**
* fp_dscv_dev_get_driver:
* @dev: the discovered device
*
* Gets the #fp_driver for a discovered device.
*
* Returns: the driver backing the device
*/
API_EXPORTED struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev)
{
g_return_val_if_fail(dev, NULL);
return dev->drv;
}
/**
* fp_dscv_dev_get_driver_id:
* @dev: a discovered fingerprint device
*
* Returns a unique driver identifier for the underlying driver
* for that device.
*
* Returns: the ID for #dev
*/
API_EXPORTED uint16_t fp_dscv_dev_get_driver_id(struct fp_dscv_dev *dev)
{
g_return_val_if_fail(dev, 0);
return fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev));
}
/**
* fp_dscv_dev_get_devtype:
* @dev: the discovered device
*
* Gets the [devtype](advanced-topics.html#device-types) for a discovered device.
*
* Returns: the devtype of the device
*/
API_EXPORTED uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev)
{
g_return_val_if_fail(dev, 0);
return dev->devtype;
}
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv)
{
switch (drv->type) {
case DRIVER_PRIMITIVE:
return PRINT_DATA_RAW;
case DRIVER_IMAGING:
return PRINT_DATA_NBIS_MINUTIAE;
default:
fp_err("unrecognised drv type %d", drv->type);
return PRINT_DATA_RAW;
}
}
/**
* fp_dscv_dev_supports_print_data:
* @dev: the discovered device
* @print: the print for compatibility checking
*
* Determines if a specific #fp_print_data stored print appears to be
* compatible with a discovered device.
*
* Returns: 1 if the print is compatible with the device, 0 otherwise
*/
API_EXPORTED int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev,
struct fp_print_data *print)
{
g_return_val_if_fail(dev, 0);
g_return_val_if_fail(print, 0);
return fpi_print_data_compatible(dev->drv->id, dev->devtype,
fpi_driver_get_data_type(dev->drv), print->driver_id, print->devtype,
print->type);
}
/**
* fp_dscv_dev_supports_dscv_print:
* @dev: the discovered device
* @print: the discovered print for compatibility checking
*
* Determines if a specific #fp_dscv_print discovered print appears to be
* compatible with a discovered device.
*
* Returns: 1 if the print is compatible with the device, 0 otherwise
*
* Deprecated: Do not use.
*/
API_EXPORTED int fp_dscv_dev_supports_dscv_print(struct fp_dscv_dev *dev,
struct fp_dscv_print *print)
{
g_return_val_if_fail(dev, 0);
g_return_val_if_fail(print, 0);
return fpi_print_data_compatible(dev->drv->id, dev->devtype, 0,
print->driver_id, print->devtype, 0);
}
/**
* fp_dscv_dev_for_print_data:
* @devs: a list of discovered devices
* @print: the print under inspection
*
* Searches a list of discovered devices for a device that appears to be
* compatible with a #fp_print_data stored print.
*
* Returns: the first discovered device that appears to support the print, or
* %NULL if no apparently compatible devices could be found
*
* Deprecated: Do not use.
*/
API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_print_data(struct fp_dscv_dev **devs,
struct fp_print_data *print)
{
struct fp_dscv_dev *ddev;
int i;
g_return_val_if_fail(devs, NULL);
g_return_val_if_fail(print, NULL);
for (i = 0; (ddev = devs[i]); i++)
if (fp_dscv_dev_supports_print_data(ddev, print))
return ddev;
return NULL;
}
/**
* fp_dscv_dev_for_dscv_print:
* @devs: a list of discovered devices
* @print: the print under inspection
*
* Searches a list of discovered devices for a device that appears to be
* compatible with a #fp_dscv_print discovered print.
*
* Returns: the first discovered device that appears to support the print, or
* %NULL if no apparently compatible devices could be found
*
* Deprecated: Do not use.
*/
API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_dscv_print(struct fp_dscv_dev **devs,
struct fp_dscv_print *print)
{
struct fp_dscv_dev *ddev;
int i;
g_return_val_if_fail(devs, NULL);
g_return_val_if_fail(print, NULL);
for (i = 0; (ddev = devs[i]); i++) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
if (fp_dscv_dev_supports_dscv_print(ddev, print))
return ddev;
#pragma GCC diagnostic pop
}
return NULL;
}
/**
* fp_dev_get_driver:
* @dev: the struct #fp_dev device
*
* Get the #fp_driver for a fingerprint device.
*
* Returns: the driver controlling the device
*/
API_EXPORTED struct fp_driver *fp_dev_get_driver(struct fp_dev *dev)
{
g_return_val_if_fail(dev, NULL);
return dev->drv;
}
/**
* fp_dev_get_nr_enroll_stages:
* @dev: the struct #fp_dev device
*
* Gets the number of [enroll stages](intro.html#enrollment) required to enroll a
* fingerprint with the device.
*
* Returns: the number of enroll stages
*/
API_EXPORTED int fp_dev_get_nr_enroll_stages(struct fp_dev *dev)
{
g_return_val_if_fail(dev, 0);
return dev->nr_enroll_stages;
}
/**
* fp_dev_get_devtype:
* @dev: the struct #fp_dev device
*
* Gets the [devtype](advanced-topics.html#device-types) for a device.
*
* Returns: the devtype
*/
API_EXPORTED uint32_t fp_dev_get_devtype(struct fp_dev *dev)
{
g_return_val_if_fail(dev, 0);
return dev->devtype;
}
/**
* fp_dev_supports_print_data:
* @dev: the struct #fp_dev device
* @data: the stored print
*
* Determines if a stored print is compatible with a certain device.
*
* Returns: 1 if the print is compatible with the device, 0 if not
*/
API_EXPORTED int fp_dev_supports_print_data(struct fp_dev *dev,
struct fp_print_data *data)
{
g_return_val_if_fail(dev, 0);
g_return_val_if_fail(data, 0);
return fpi_print_data_compatible(dev->drv->id, dev->devtype,
fpi_driver_get_data_type(dev->drv), data->driver_id, data->devtype,
data->type);
}
/**
* fp_dev_supports_dscv_print:
* @dev: the struct #fp_dev device
* @print: the discovered print
*
* Determines if a #fp_dscv_print discovered print appears to be compatible
* with a certain device.
*
* Returns: 1 if the print is compatible with the device, 0 if not
*
* Deprecated: Do not use.
*/
API_EXPORTED int fp_dev_supports_dscv_print(struct fp_dev *dev,
struct fp_dscv_print *print)
{
g_return_val_if_fail(dev, 0);
g_return_val_if_fail(print, 0);
return fpi_print_data_compatible(dev->drv->id, dev->devtype,
0, print->driver_id, print->devtype, 0);
}
/**
* fp_driver_get_name:
* @drv: the driver
*
* Retrieves the name of the driver. For example: "upekts"
*
* Returns: the driver name. Must not be modified or freed.
*/
API_EXPORTED const char *fp_driver_get_name(struct fp_driver *drv)
{
g_return_val_if_fail(drv, NULL);
return drv->name;
}
/**
* fp_driver_get_full_name:
* @drv: the driver
*
* Retrieves a descriptive name of the driver. For example: "UPEK TouchStrip"
*
* Returns: the descriptive name. Must not be modified or freed.
*/
API_EXPORTED const char *fp_driver_get_full_name(struct fp_driver *drv)
{
g_return_val_if_fail(drv, NULL);
return drv->full_name;
}
/**
* fp_driver_get_driver_id:
* @drv: the driver
*
* Retrieves the driver ID code for a driver.
*
* Returns: the driver ID
*/
API_EXPORTED uint16_t fp_driver_get_driver_id(struct fp_driver *drv)
{
g_return_val_if_fail(drv, 0);
return drv->id;
}
/**
* fp_driver_get_scan_type:
* @drv: the driver
*
* Retrieves the scan type for the devices associated with the driver.
*
* Returns: the scan type
*/
API_EXPORTED enum fp_scan_type fp_driver_get_scan_type(struct fp_driver *drv)
{
g_return_val_if_fail(drv, FP_SCAN_TYPE_PRESS);
return drv->scan_type;
}
/**
* fp_driver_supports_imaging:
* @drv: the driver
*
* Determines if a driver has imaging capabilities. If a driver has imaging
* capabilities you are able to perform imaging operations such as retrieving
* scan images using fp_dev_img_capture(). However, not all drivers support
* imaging devices some do all processing in hardware. This function will
* indicate which class a device in question falls into.
*
* Returns: 1 if the device is an imaging device, 0 if the device does not
* provide images to the host computer
*/
API_EXPORTED int fp_driver_supports_imaging(struct fp_driver *drv)
{
g_return_val_if_fail(drv, 0);
return drv->capture_start != NULL;
}
/**
* fp_dev_supports_imaging:
* @dev: the struct #fp_dev device
*
* Determines if a device has imaging capabilities. If a device has imaging
* capabilities you are able to perform imaging operations such as retrieving
* scan images using fp_dev_img_capture(). However, not all devices are
* imaging devices some do all processing in hardware. This function will
* indicate which class a device in question falls into.
*
* Returns: 1 if the device is an imaging device, 0 if the device does not
* provide images to the host computer
*/
API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev)
{
g_return_val_if_fail(dev, 0);
return dev->drv->capture_start != NULL;
}
/**
* fp_dev_supports_identification:
* @dev: the struct #fp_dev device
*
* Determines if a device is capable of [identification](intro.html#identification)
* through fp_identify_finger() and similar. Not all devices support this
* functionality.
*
* Returns: 1 if the device is capable of identification, 0 otherwise.
*/
API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
{
g_return_val_if_fail(dev, 0);
return dev->drv->identify_start != NULL;
}
/**
* fp_dev_get_img_width:
* @dev: the struct #fp_dev device
*
* Gets the expected width of images that will be captured from the device.
* This function will return -1 for devices that are not
* [imaging devices](libfprint-Devices-operations.html#imaging). If the width of images from this device
* can vary, 0 will be returned.
*
* Returns: the expected image width, or 0 for variable, or -1 for non-imaging
* devices.
*/
API_EXPORTED int fp_dev_get_img_width(struct fp_dev *dev)
{
g_return_val_if_fail(dev, -1);
if (!dev->img_dev) {
fp_dbg("get image width for non-imaging device");
return -1;
}
return fpi_imgdev_get_img_width(dev->img_dev);
}
/**
* fp_dev_get_img_height:
* @dev: the struct #fp_dev device
*
* Gets the expected height of images that will be captured from the device.
* This function will return -1 for devices that are not
* [imaging devices](libfprint-Devices-operations.html#imaging). If the height of images from this device
* can vary, 0 will be returned.
*
* Returns: the expected image height, or 0 for variable, or -1 for non-imaging
* devices.
*/
API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev)
{
g_return_val_if_fail(dev, -1);
if (!dev->img_dev) {
fp_dbg("get image height for non-imaging device");
return -1;
}
return fpi_imgdev_get_img_height(dev->img_dev);
}
/**
* fp_set_debug:
* @level: the verbosity level
*
* This call does nothing, see fp_init() for details.
*/
API_EXPORTED void fp_set_debug(int level)
{
/* Nothing */
}
/**
* fp_init:
*
* Initialise libfprint. This function must be called before you attempt to
* use the library in any way.
*
* To enable debug output of libfprint specifically, use GLib's `G_MESSAGES_DEBUG`
* environment variable as explained in [Running and debugging GLib Applications](https://developer.gnome.org/glib/stable/glib-running.html#G_MESSAGES_DEBUG).
*
* The log domains used in libfprint are either `libfprint` or `libfprint-FP_COMPONENT`
* where `FP_COMPONENT` is defined in the source code for each driver, or component
* of the library. Starting with `all` and trimming down is advised.
*
* To enable debugging of libusb, for USB-based fingerprint reader drivers, use
* libusb's `LIBUSB_DEBUG` environment variable as explained in the
* [libusb-1.0 API Reference](http://libusb.sourceforge.net/api-1.0/#msglog).
*
* Example:
*
* ```
* LIBUSB_DEBUG=4 G_MESSAGES_DEBUG=all my-libfprint-application
* ```
*
* Returns: 0 on success, non-zero on error.
*/
API_EXPORTED int fp_init(void)
{
int r;
G_DEBUG_HERE();
r = libusb_init(&fpi_usb_ctx);
if (r < 0)
return r;
register_drivers();
fpi_poll_init();
return 0;
}
/**
* fp_exit:
*
* Deinitialise libfprint. This function should be called during your program
* exit sequence. You must not use any libfprint functions after calling this
* function, unless you call fp_init() again.
*/
API_EXPORTED void fp_exit(void)
{
G_DEBUG_HERE();
if (opened_devices) {
GSList *copy = g_slist_copy(opened_devices);
GSList *elem = copy;
fp_dbg("naughty app left devices open on exit!");
do
fp_dev_close((struct fp_dev *) elem->data);
while ((elem = g_slist_next(elem)));
g_slist_free(copy);
g_slist_free(opened_devices);
opened_devices = NULL;
}
fpi_data_exit();
fpi_poll_exit();
g_slist_free(registered_drivers);
registered_drivers = NULL;
libusb_exit(fpi_usb_ctx);
}

118
libfprint/fpi-core.h Normal file
View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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
*/
#ifndef __FPI_CORE_H__
#define __FPI_CORE_H__
#include <fprint.h>
#include "fpi-dev-img.h"
/**
* usb_id:
* @vendor: the USB vendor ID
* @product: the USB product ID
* @driver_data: data to differentiate devices of different
* vendor and product IDs.
*
* The struct #usb_id is used to declare devices supported by a
* particular driver. The @driver_data information is used to
* differentiate different models of devices which only need
* small changes compared to the default driver behaviour to function.
*
* For example, a device might have a different initialisation from
* the stock device, so the driver could do:
*
* |[<!-- language="C" -->
* if (driver_data == MY_DIFFERENT_DEVICE_QUIRK) {
* ...
* } else {
* ...
* }
* ]|
*
* The default value is zero, so the @driver_data needs to be a
* non-zero to be useful.
*/
struct usb_id {
uint16_t vendor;
uint16_t product;
unsigned long driver_data;
};
/**
* fp_driver_type:
* @DRIVER_PRIMITIVE: primitive, non-imaging, driver
* @DRIVER_IMAGING: imaging driver
*
* The type of device the driver supports.
*/
enum fp_driver_type {
DRIVER_PRIMITIVE = 0,
DRIVER_IMAGING = 1,
};
struct fp_driver {
const uint16_t id;
const char *name;
const char *full_name;
const struct usb_id * const id_table;
enum fp_driver_type type;
enum fp_scan_type scan_type;
/* Device operations */
int (*discover)(struct libusb_device_descriptor *dsc, uint32_t *devtype);
int (*open)(struct fp_dev *dev, unsigned long driver_data);
void (*close)(struct fp_dev *dev);
int (*enroll_start)(struct fp_dev *dev);
int (*enroll_stop)(struct fp_dev *dev);
int (*verify_start)(struct fp_dev *dev);
int (*verify_stop)(struct fp_dev *dev, gboolean iterating);
int (*identify_start)(struct fp_dev *dev);
int (*identify_stop)(struct fp_dev *dev, gboolean iterating);
int (*capture_start)(struct fp_dev *dev);
int (*capture_stop)(struct fp_dev *dev);
};
/**
* FpiImgDriverFlags:
* @FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE: Whether the driver supports
* unconditional image capture. No driver currently does.
*
* Flags used in the #fp_img_driver to advertise the capabilities of drivers.
*/
typedef enum {
FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE = 1 << 0
} FpiImgDriverFlags;
struct fp_img_driver {
struct fp_driver driver;
FpiImgDriverFlags flags;
int img_width;
int img_height;
int bz3_threshold;
/* Device operations */
int (*open)(struct fp_img_dev *dev, unsigned long driver_data);
void (*close)(struct fp_img_dev *dev);
int (*activate)(struct fp_img_dev *dev);
int (*change_state)(struct fp_img_dev *dev, enum fp_imgdev_state state);
void (*deactivate)(struct fp_img_dev *dev);
};
#endif

View File

@@ -32,19 +32,49 @@
#define DIR_PERMS 0700
/** @defgroup print_data Stored prints
* Stored prints are represented by a structure named <tt>fp_print_data</tt>.
struct fpi_print_data_fp2 {
char prefix[3];
uint16_t driver_id;
uint32_t devtype;
unsigned char data_type;
unsigned char data[0];
} __attribute__((__packed__));
struct fpi_print_data_item_fp2 {
uint32_t length;
unsigned char data[0];
} __attribute__((__packed__));
/**
* SECTION: print_data
* @title: Stored prints
* @short_description: Stored prints functions
*
* Stored prints are represented by a structure named #fp_print_data.
* Stored prints are originally obtained from an enrollment function such as
* fp_enroll_finger().
*
* This page documents the various operations you can do with a stored print.
* Note that by default, "stored prints" are not actually stored anywhere
* except in RAM. For the simple scenarios, libfprint provides a simple API
* for you to save and load the stored prints referring to a single user in
* their home directory. For more advanced users, libfprint provides APIs for
* you to convert print data to a byte string, and to reconstruct stored prints
* except in RAM. Storage needs to be handled by the API user by using the
* fp_print_data_get_data() and fp_print_data_from_data(). This API allows
* to convert print data into byte strings, and to reconstruct stored prints
* from such data at a later point. You are welcome to store these byte strings
* in any fashion that suits you.
*
* The provided API to store data on disk is deprecated and should not be
* used anymore. This API stored the prints in the current user's home
* directory.
*/
/*
* SECTION: fpi-data
* @title: Stored prints creation
* @short_description: Stored prints creation functions
*
* Stored print can be loaded and created by certain drivers which do their own
* print matching in hardware. Most drivers will not be using those functions.
* See #fp_print_data for the public API counterpart.
*/
static char *base_store = NULL;
@@ -73,7 +103,6 @@ void fpi_data_exit(void)
((finger) >= LEFT_THUMB && (finger) <= RIGHT_LITTLE)
/* for debug messages only */
#ifdef ENABLE_DEBUG_LOGGING
static const char *finger_num_to_str(enum fp_finger finger)
{
const char *names[] = {
@@ -92,90 +121,204 @@ static const char *finger_num_to_str(enum fp_finger finger)
return "UNKNOWN";
return names[finger];
}
#endif
static struct fp_print_data *print_data_new(uint16_t driver_id,
uint32_t devtype, enum fp_print_data_type type, size_t length)
uint32_t devtype, enum fp_print_data_type type)
{
struct fp_print_data *data = g_malloc(sizeof(*data) + length);
fp_dbg("length=%zd driver=%02x devtype=%04x", length, driver_id, devtype);
memset(data, 0, sizeof(*data));
struct fp_print_data *data = g_malloc0(sizeof(*data));
fp_dbg("driver=%02x devtype=%04x", driver_id, devtype);
data->driver_id = driver_id;
data->devtype = devtype;
data->type = type;
data->length = length;
return data;
}
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length)
static void fpi_print_data_item_free(struct fp_print_data_item *item)
{
return print_data_new(dev->drv->id, dev->devtype,
fpi_driver_get_data_type(dev->drv), length);
g_free(item);
}
/** \ingroup print_data
struct fp_print_data_item *fpi_print_data_item_new(size_t length)
{
struct fp_print_data_item *item = g_malloc0(sizeof(*item) + length);
item->length = length;
return item;
}
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev)
{
return print_data_new(dev->drv->id, dev->devtype,
fpi_driver_get_data_type(dev->drv));
}
struct fp_print_data_item *
fpi_print_data_get_item(struct fp_print_data *data)
{
return data->prints->data;
}
void
fpi_print_data_add_item(struct fp_print_data *data,
struct fp_print_data_item *item)
{
data->prints = g_slist_prepend(data->prints, item);
}
/**
* fp_print_data_get_data:
* @data: the stored print
* @ret: output location for the data buffer. Must be freed with free()
* after use.
* Convert a stored print into a unified representation inside a data buffer.
* You can then store this data buffer in any way that suits you, and load
* it back at some later time using fp_print_data_from_data().
* \param data the stored print
* \param ret output location for the data buffer. Must be freed with free()
* after use.
* \returns the size of the freshly allocated buffer, or 0 on error.
*
* Returns: the size of the freshly allocated buffer, or 0 on error.
*/
API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
unsigned char **ret)
{
struct fpi_print_data_fp1 *buf;
size_t buflen;
struct fpi_print_data_fp2 *out_data;
struct fpi_print_data_item_fp2 *out_item;
struct fp_print_data_item *item;
size_t buflen = 0;
GSList *list_item;
unsigned char *buf;
fp_dbg("");
G_DEBUG_HERE();
buflen = sizeof(*buf) + data->length;
buf = malloc(buflen);
if (!buf)
return 0;
list_item = data->prints;
while (list_item) {
item = list_item->data;
buflen += sizeof(*out_item);
buflen += item->length;
list_item = g_slist_next(list_item);
}
buflen += sizeof(*out_data);
out_data = g_malloc(buflen);
*ret = (unsigned char *) out_data;
buf = out_data->data;
out_data->prefix[0] = 'F';
out_data->prefix[1] = 'P';
out_data->prefix[2] = '2';
out_data->driver_id = GUINT16_TO_LE(data->driver_id);
out_data->devtype = GUINT32_TO_LE(data->devtype);
out_data->data_type = data->type;
list_item = data->prints;
while (list_item) {
item = list_item->data;
out_item = (struct fpi_print_data_item_fp2 *)buf;
out_item->length = GUINT32_TO_LE(item->length);
/* FIXME: fp_print_data_item->data content is not endianness agnostic */
memcpy(out_item->data, item->data, item->length);
buf += sizeof(*out_item);
buf += item->length;
list_item = g_slist_next(list_item);
}
*ret = (unsigned char *) buf;
buf->prefix[0] = 'F';
buf->prefix[1] = 'P';
buf->prefix[2] = '1';
buf->driver_id = GUINT16_TO_LE(data->driver_id);
buf->devtype = GUINT32_TO_LE(data->devtype);
buf->data_type = data->type;
memcpy(buf->data, data->data, data->length);
return buflen;
}
/** \ingroup print_data
static struct fp_print_data *fpi_print_data_from_fp1_data(unsigned char *buf,
size_t buflen)
{
size_t print_data_len;
struct fp_print_data *data;
struct fp_print_data_item *item;
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
print_data_len = buflen - sizeof(*raw);
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
GUINT32_FROM_LE(raw->devtype), raw->data_type);
item = fpi_print_data_item_new(print_data_len);
/* FIXME: fp_print_data->data content is not endianness agnostic */
memcpy(item->data, raw->data, print_data_len);
data->prints = g_slist_prepend(data->prints, item);
return data;
}
static struct fp_print_data *fpi_print_data_from_fp2_data(unsigned char *buf,
size_t buflen)
{
size_t total_data_len, item_len;
struct fp_print_data *data;
struct fp_print_data_item *item;
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
unsigned char *raw_buf;
struct fpi_print_data_item_fp2 *raw_item;
total_data_len = buflen - sizeof(*raw);
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
GUINT32_FROM_LE(raw->devtype), raw->data_type);
raw_buf = raw->data;
while (total_data_len) {
if (total_data_len < sizeof(*raw_item))
break;
total_data_len -= sizeof(*raw_item);
raw_item = (struct fpi_print_data_item_fp2 *)raw_buf;
item_len = GUINT32_FROM_LE(raw_item->length);
fp_dbg("item len %d, total_data_len %d", (int) item_len, (int) total_data_len);
if (total_data_len < item_len) {
fp_err("corrupted fingerprint data");
break;
}
total_data_len -= item_len;
item = fpi_print_data_item_new(item_len);
/* FIXME: fp_print_data->data content is not endianness agnostic */
memcpy(item->data, raw_item->data, item_len);
data->prints = g_slist_prepend(data->prints, item);
raw_buf += sizeof(*raw_item);
raw_buf += item_len;
}
if (g_slist_length(data->prints) == 0) {
fp_print_data_free(data);
data = NULL;
}
return data;
}
/**
* fp_print_data_from_data:
* @buf: the data buffer
* @buflen: the length of the buffer
* Load a stored print from a data buffer. The contents of said buffer must
* be the untouched contents of a buffer previously supplied to you by the
* fp_print_data_get_data() function.
* \param buf the data buffer
* \param buflen the length of the buffer
* \returns the stored print represented by the data, or NULL on error. Must
*
* Returns: the stored print represented by the data, or %NULL on error. Must
* be freed with fp_print_data_free() after use.
*/
API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
size_t buflen)
{
struct fpi_print_data_fp1 *raw = (struct fpi_print_data_fp1 *) buf;
size_t print_data_len;
struct fp_print_data *data;
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
fp_dbg("buffer size %zd", buflen);
if (buflen < sizeof(*raw))
return NULL;
if (strncmp(raw->prefix, "FP1", 3) != 0) {
if (strncmp(raw->prefix, "FP1", 3) == 0) {
return fpi_print_data_from_fp1_data(buf, buflen);
} else if (strncmp(raw->prefix, "FP2", 3) == 0) {
return fpi_print_data_from_fp2_data(buf, buflen);
} else {
fp_dbg("bad header prefix");
return NULL;
}
print_data_len = buflen - sizeof(*raw);
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
GUINT32_FROM_LE(raw->devtype), raw->data_type, print_data_len);
memcpy(data->data, raw->data, print_data_len);
return data;
return NULL;
}
static char *get_path_to_storedir(uint16_t driver_id, uint32_t devtype)
@@ -209,7 +352,11 @@ static char *get_path_to_print(struct fp_dev *dev, enum fp_finger finger)
return __get_path_to_print(dev->drv->id, dev->devtype, finger);
}
/** \ingroup print_data
/**
* fp_print_data_save:
* @data: the stored print to save to disk
* @finger: the finger that this print corresponds to
*
* Saves a stored print to disk, assigned to a specific finger. Even though
* you are limited to storing only the 10 human fingers, this is a
* per-device-type limit. For example, you can store the users right index
@@ -220,9 +367,12 @@ static char *get_path_to_print(struct fp_dev *dev, enum fp_finger finger)
* This function will unconditionally overwrite a fingerprint previously
* saved for the same finger and device type. The print is saved in a hidden
* directory beneath the current user's home directory.
* \param data the stored print to save to disk
* \param finger the finger that this print corresponds to
* \returns 0 on success, non-zero on error.
*
* Returns: 0 on success, non-zero on error.
*
* Deprecated: Data storage should be handled outside of libfprint.
* See <link linkend="libfprint-Stored-prints.description">stored prints description</link>
* for more information.
*/
API_EXPORTED int fp_print_data_save(struct fp_print_data *data,
enum fp_finger finger)
@@ -248,6 +398,7 @@ API_EXPORTED int fp_print_data_save(struct fp_print_data *data,
r = g_mkdir_with_parents(dirpath, DIR_PERMS);
if (r < 0) {
fp_err("couldn't create storage directory");
free(buf);
g_free(path);
g_free(dirpath);
return r;
@@ -319,7 +470,13 @@ static int load_from_file(char *path, struct fp_print_data **data)
return 0;
}
/** \ingroup print_data
/**
* fp_print_data_load:
* @dev: the device you are loading the print for
* @finger: the finger of the file you are loading
* @data: output location to put the corresponding stored print. Must be
* freed with fp_print_data_free() after use.
* Loads a previously stored print from disk. The print must have been saved
* earlier using the fp_print_data_save() function.
*
@@ -327,17 +484,17 @@ static int load_from_file(char *path, struct fp_print_data **data)
* be found. Other error codes (both positive and negative) are possible for
* obscure error conditions (e.g. corruption).
*
* \param dev the device you are loading the print for
* \param finger the finger of the file you are loading
* \param data output location to put the corresponding stored print. Must be
* freed with fp_print_data_free() after use.
* \returns 0 on success, non-zero on error
* Returns: 0 on success, non-zero on error
*
* Deprecated: Data storage should be handled outside of libfprint.
* See <link linkend="libfprint-Stored-prints.description">stored prints description</link>
* for more information.
*/
API_EXPORTED int fp_print_data_load(struct fp_dev *dev,
enum fp_finger finger, struct fp_print_data **data)
{
gchar *path;
struct fp_print_data *fdata;
struct fp_print_data *fdata = NULL;
int r;
if (!base_store)
@@ -359,11 +516,18 @@ API_EXPORTED int fp_print_data_load(struct fp_dev *dev,
return 0;
}
/** \ingroup print_data
/**
* fp_print_data_delete:
* @dev: the device that the print belongs to
* @finger: the finger of the file you are deleting
* Removes a stored print from disk previously saved with fp_print_data_save().
* \param dev the device that the print belongs to
* \param finger the finger of the file you are deleting
* \returns 0 on success, negative on error
*
* Returns: 0 on success, negative on error
*
* Deprecated: Data storage should be handled outside of libfprint.
* See <link linkend="libfprint-Stored-prints.description">stored prints description</link>
* for more information.
*/
API_EXPORTED int fp_print_data_delete(struct fp_dev *dev,
enum fp_finger finger)
@@ -381,18 +545,22 @@ API_EXPORTED int fp_print_data_delete(struct fp_dev *dev,
return r;
}
/** \ingroup print_data
* Attempts to load a stored print based on a \ref dscv_print
* "discovered print" record.
/**
* fp_print_data_from_dscv_print:
* @print: the discovered print
* @data: output location to point to the corresponding stored print. Must
* be freed with fp_print_data_free() after use.
* Attempts to load a stored print based on a #fp_dscv_print
* discovered print record.
*
* A return code of -ENOENT indicates that the file referred to by the
* discovered print could not be found. Other error codes (both positive and
* negative) are possible for obscure error conditions (e.g. corruption).
*
* \param print the discovered print
* \param data output location to point to the corresponding stored print. Must
* be freed with fp_print_data_free() after use.
* \returns 0 on success, non-zero on error.
* Returns: 0 on success, non-zero on error.
*
* Deprecated: Do not use.
*/
API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
struct fp_print_data **data)
@@ -400,40 +568,54 @@ API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
return load_from_file(print->path, data);
}
/** \ingroup print_data
/**
* fp_print_data_free:
* @data: the stored print to destroy. If NULL, function simply returns.
*
* Frees a stored print. Must be called when you are finished using the print.
* \param data the stored print to destroy. If NULL, function simply returns.
*/
API_EXPORTED void fp_print_data_free(struct fp_print_data *data)
{
if (data)
g_slist_free_full(data->prints, (GDestroyNotify)fpi_print_data_item_free);
g_free(data);
}
/** \ingroup print_data
* Gets the \ref driver_id "driver ID" for a stored print. The driver ID
/**
* fp_print_data_get_driver_id:
* @data: the stored print
* Gets the [driver ID](advanced-topics.html#driver_id) for a stored print. The driver ID
* indicates which driver the print originally came from. The print is
* only usable with a device controlled by that driver.
* \param data the stored print
* \returns the driver ID of the driver compatible with the print
*
* Returns: the driver ID of the driver compatible with the print
*/
API_EXPORTED uint16_t fp_print_data_get_driver_id(struct fp_print_data *data)
{
return data->driver_id;
}
/** \ingroup print_data
* Gets the \ref devtype "devtype" for a stored print. The devtype represents
/**
* fp_print_data_get_devtype:
* @data: the stored print
* Gets the [devtype](advanced-topics.html#device-types) for a stored print. The devtype represents
* which type of device under the parent driver is compatible with the print.
* \param data the stored print
* \returns the devtype of the device range compatible with the print
*
* Returns: the devtype of the device range compatible with the print
*/
API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
{
return data->devtype;
}
/** @defgroup dscv_print Print discovery
* The \ref print_data "stored print" documentation detailed a simple API
/**
* SECTION:dscv_print
* @title: Print discovery (deprecated)
* @short_description: Print discovery functions
*
* The [stored print](libfprint-Stored-prints.html) documentation detailed a simple API
* for storing per-device prints for a single user, namely
* fp_print_data_save(). It also detailed a load function,
* fp_print_data_load(), but usage of this function is limited to scenarios
@@ -444,7 +626,7 @@ API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
* previously saved prints, potentially even before device discovery. These
* functions are designed to offer this functionality to you.
*
* Discovered prints are stored in a <tt>dscv_print</tt> structure, and you
* Discovered prints are stored in a #fp_dscv_print structure, and you
* can use functions documented below to access some information about these
* prints. You can determine if a discovered print appears to be compatible
* with a device using functions such as fp_dscv_dev_supports_dscv_print() and
@@ -462,6 +644,10 @@ API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
* circumstances it may turn out that the print is corrupt or not for the
* device that it appeared to be. Also, it is possible that the print may have
* been deleted by the time you come to load it.
*
* Note that this portion of the library is deprecated. All that it offers is
* already implementable using publicly available functions, and its usage is
* unnecessarily restrictive in terms of how it stores data.
*/
static GSList *scan_dev_store_dir(char *devpath, uint16_t driver_id,
@@ -545,11 +731,16 @@ static GSList *scan_driver_store_dir(char *drvpath, uint16_t driver_id,
return list;
}
/** \ingroup dscv_print
/**
* fp_discover_prints:
*
* Scans the users home directory and returns a list of prints that were
* previously saved using fp_print_data_save().
* \returns a NULL-terminated list of discovered prints, must be freed with
*
* Returns: a %NULL-terminated list of discovered prints, must be freed with
* fp_dscv_prints_free() after use.
*
* Deprecated: Do not use.
*/
API_EXPORTED struct fp_dscv_print **fp_discover_prints(void)
{
@@ -558,9 +749,7 @@ API_EXPORTED struct fp_dscv_print **fp_discover_prints(void)
GError *err = NULL;
GSList *tmplist = NULL;
GSList *elem;
unsigned int tmplist_len;
struct fp_dscv_print **list;
unsigned int i;
GPtrArray *array;
if (!base_store)
storage_setup();
@@ -595,23 +784,29 @@ API_EXPORTED struct fp_dscv_print **fp_discover_prints(void)
}
g_dir_close(dir);
tmplist_len = g_slist_length(tmplist);
list = g_malloc(sizeof(*list) * (tmplist_len + 1));
elem = tmplist;
for (i = 0; i < tmplist_len; i++, elem = g_slist_next(elem))
list[i] = elem->data;
list[tmplist_len] = NULL; /* NULL-terminate */
if (tmplist == NULL)
return NULL;
array = g_ptr_array_new();
for (elem = tmplist; elem != NULL; elem = elem->next)
g_ptr_array_add(array, elem->data);
g_ptr_array_add(array, NULL);
g_slist_free(tmplist);
return list;
return (struct fp_dscv_print **) g_ptr_array_free(array, FALSE);
}
/** \ingroup dscv_print
/**
* fp_dscv_prints_free:
* @prints: the list of discovered prints. If NULL, function simply
* returns.
*
* Frees a list of discovered prints. This function also frees the discovered
* prints themselves, so make sure you do not use any discovered prints
* after calling this function.
* \param prints the list of discovered prints. If NULL, function simply
* returns.
*
* Deprecated: Do not use.
*/
API_EXPORTED void fp_dscv_prints_free(struct fp_dscv_print **prints)
{
@@ -629,47 +824,67 @@ API_EXPORTED void fp_dscv_prints_free(struct fp_dscv_print **prints)
g_free(prints);
}
/** \ingroup dscv_print
* Gets the \ref driver_id "driver ID" for a discovered print. The driver ID
/**
* fp_dscv_print_get_driver_id:
* @print: the discovered print
*
* Gets the [driver ID](advanced-topics.html#driver_id) for a discovered print. The driver ID
* indicates which driver the print originally came from. The print is only
* usable with a device controlled by that driver.
* \param print the discovered print
* \returns the driver ID of the driver compatible with the print
*
* Returns: the driver ID of the driver compatible with the print
*
* Deprecated: Do not use.
*/
API_EXPORTED uint16_t fp_dscv_print_get_driver_id(struct fp_dscv_print *print)
{
return print->driver_id;
}
/** \ingroup dscv_print
* Gets the \ref devtype "devtype" for a discovered print. The devtype
/**
* fp_dscv_print_get_devtype:
* @print: the discovered print
*
* Gets the [devtype](advanced-topics.html#device-types) for a discovered print. The devtype
* represents which type of device under the parent driver is compatible
* with the print.
* \param print the discovered print
* \returns the devtype of the device range compatible with the print
*
* Returns: the devtype of the device range compatible with the print
*
* Deprecated: Do not use.
*/
API_EXPORTED uint32_t fp_dscv_print_get_devtype(struct fp_dscv_print *print)
{
return print->devtype;
}
/** \ingroup dscv_print
/**
* fp_dscv_print_get_finger:
* @print: discovered print
*
* Gets the finger code for a discovered print.
* \param print discovered print
* \returns a finger code from #fp_finger
*
* Returns: a finger code from #fp_finger
*
* Deprecated: Do not use.
*/
API_EXPORTED enum fp_finger fp_dscv_print_get_finger(struct fp_dscv_print *print)
{
return print->finger;
}
/** \ingroup dscv_print
/**
* fp_dscv_print_delete:
* @print: the discovered print to remove from disk
*
* Removes a discovered print from disk. After successful return of this
* function, functions such as fp_dscv_print_get_finger() will continue to
* operate as before, however calling fp_print_data_from_dscv_print() will
* fail for obvious reasons.
* \param print the discovered print to remove from disk
* \returns 0 on success, negative on error
*
* Returns: 0 on success, negative on error
*
* Deprecated: Do not use.
*/
API_EXPORTED int fp_dscv_print_delete(struct fp_dscv_print *print)
{

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