Compare commits

..

27 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
72 changed files with 744 additions and 2201 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!')

View File

@@ -7,6 +7,7 @@ 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:
@@ -24,10 +25,16 @@ variables:
- 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:

View File

@@ -3,8 +3,29 @@
## 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.
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

27
NEWS
View File

@@ -1,6 +1,33 @@
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

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,115 +0,0 @@
/*
* Example fingerprint delete finger program, which delete the right index
* finger which has been previously enrolled to disk.
* Copyright (C) 2019 Synaptics Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libfprint/fprint.h>
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
{
struct fp_dscv_dev *ddev = discovered_devs[0];
struct fp_driver *drv;
if (!ddev)
return NULL;
drv = fp_dscv_dev_get_driver(ddev);
printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv));
return ddev;
}
int main(void)
{
int r = 1;
struct fp_dscv_dev *ddev;
struct fp_dscv_dev **discovered_devs;
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);
}
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
fprintf(stderr, "Could not discover devices\n");
goto out;
}
ddev = discover_device(discovered_devs);
if (!ddev) {
fprintf(stderr, "No devices detected.\n");
goto out;
}
dev = fp_dev_open(ddev);
fp_dscv_devs_free(discovered_devs);
if (!dev) {
fprintf(stderr, "Could not open device.\n");
goto out;
}
printf("Opened device. Loading previously enrolled right index finger "
"data...\n");
r = fp_print_data_load(dev, RIGHT_INDEX, &data);
if (r != 0) {
fprintf(stderr, "Failed to load fingerprint, error %d\n", r);
fprintf(stderr, "Did you remember to enroll your right index finger "
"first?\n");
goto out_close;
}
printf("Print loaded. delete data in sensor.\n");
if(!fp_dev_supports_data_in_sensor(dev))
{
printf("This driver doesn't support to store data in sensor.\n");
goto out_close;
}
r = fp_delete_finger(dev, data);
fp_print_data_free(data);
if (r) {
printf("delete finger failed with error %d :(\n", r);
}
else
{
printf("sensor data deleted. now delete host data");
r = fp_print_data_delete(dev, RIGHT_INDEX);
if (r < 0) {
printf("Delete sensor data successfully but delete host data failed. %d :(\n", r);
}
}
out_close:
fp_dev_close(dev);
out:
fp_exit();
return r;
}

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];
@@ -142,10 +144,7 @@ int main(void)
if (!data)
goto out_close;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
r = fp_print_data_save(data, RIGHT_INDEX);
#pragma GCC diagnostic pop
r = print_data_save(data, RIGHT_INDEX);
if (r < 0)
fprintf(stderr, "Data save failed, code %d\n", r);

View File

@@ -1,9 +1,9 @@
examples = [ 'verify_live', 'enroll', 'verify', 'img_capture', 'delete' ]
examples = [ 'verify_live', 'enroll', 'verify', 'img_capture' ]
foreach example: examples
executable(example,
example + '.c',
dependencies: libfprint_dep,
[example + '.c', 'storage.c'],
dependencies: [libfprint_dep, glib_dep],
include_directories: [
root_inc,
],

View File

@@ -1,3 +0,0 @@
These are example images from NIST and are not copyrighted.
The PNG files have been generated by using the greyscale data as a mask.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -1,86 +0,0 @@
#!/usr/bin/env python3
# This script can be used together with the virtual_imgdev to simulate an
# image based fingerprint reader.
#
# To use, set the FP_VIRTUAL_IMGDEV environment variable for both the
# libfprint using program (e.g. fprintd) and this script.
#
# Usually this would work by adding it into the systemd unit file. The
# best way of doing so is to create
# /etc/systemd/system/fprintd.service.d/fprintd-test.conf
#
# [Service]
# RuntimeDirectory=fprint
# Environment=FP_VIRTUAL_IMGDEV=/run/fprint/virtimg_sock
# Environment=G_MESSAGES_DEBUG=all
# ReadWritePaths=$RUNTIME_DIR
#
# After that run:
#
# systemctl daemon-reload
# systemctl restart fprintd.service
#
# You may also need to disable selinux.
#
# Then run this script with e.g.
# FP_VIRTUAL_IMGDEV=/run/fprint/virtimg_sock ./sendvirtimg.py prints/whorl.png
import cairo
import sys
import os
import socket
import struct
if len(sys.argv) == 2:
png = cairo.ImageSurface.create_from_png(sys.argv[1])
# Cairo wants 4 byte aligned rows, so just add a few pixel if necessary
w = png.get_width()
h = png.get_height()
w = (w + 3) // 4 * 4
h = (h + 3) // 4 * 4
img = cairo.ImageSurface(cairo.Format.A8, w, h)
cr = cairo.Context(img)
cr.set_source_rgba(1, 1, 1, 1)
cr.paint()
cr.set_source_rgba(0, 0, 0, 0)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.set_source_surface(png)
cr.paint()
else:
sys.stderr.write('You need to pass a PNG with an alpha channel!\n')
sys.exit(1)
def write_dbg_img():
dbg_img_rgb = cairo.ImageSurface(cairo.Format.RGB24, img.get_width(), img.get_height())
dbg_cr = cairo.Context(dbg_img_rgb)
dbg_cr.set_source_rgb(0, 0, 0)
dbg_cr.paint()
dbg_cr.set_source_rgb(1, 1, 1)
dbg_cr.mask_surface(img, 0, 0)
dbg_img_rgb.write_to_png('/tmp/test.png')
#write_dbg_img()
# Send image through socket
sockaddr = os.environ['FP_VIRTUAL_IMGDEV']
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(sockaddr)
mem = img.get_data()
mem = mem.tobytes()
assert len(mem) == img.get_width() * img.get_height()
encoded_img = struct.pack('ii', img.get_width(), img.get_height())
encoded_img += mem
sock.sendall(encoded_img)

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];
@@ -117,11 +119,8 @@ int main(void)
printf("Opened device. Loading previously enrolled right index finger "
"data...\n");
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
r = fp_print_data_load(dev, RIGHT_INDEX, &data);
#pragma GCC diagnostic pop
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

@@ -1,148 +0,0 @@
#!/usr/bin/env python3
# This script can be used together with the virtual_misdev to simulate an
# match-in-sensor device with internal storage.
#
# To use, set the FP_VIRTUAL_MISDEV environment variable for both the
# libfprint using program (e.g. fprintd) and this script.
#
# Usually this would work by adding it into the systemd unit file. The
# best way of doing so is to create
# /etc/systemd/system/fprintd.service.d/fprintd-test.conf
#
# [Service]
# RuntimeDirectory=fprint
# Environment=FP_VIRTUAL_IMGDEV=/run/fprint/virtimg_sock
# Environment=G_MESSAGES_DEBUG=all
# ReadWritePaths=$RUNTIME_DIR
#
# After that run:
#
# systemctl daemon-reload
# systemctl restart fprintd.service
#
# You may also need to disable selinux.
#
# Then run this script with e.g.
# FP_VIRTUAL_IMGDEV=/run/fprint/virtimg_sock ./virtmissensor.py /tmp/storage
#
# Please note that the storage file should be pre-created with a few lines
# Each line represents a slot, if a print is stored, then it will contain a
# UUID (defined by the driver) and a matching string to identify it again.
# Note that the last slot line should not end with a \n
import sys
import os
import socket
import struct
import argparse
parser = argparse.ArgumentParser(description='Play virtual fingerprint device with internal storage.')
parser.add_argument('storage', metavar='storage', type=argparse.FileType('r+'),
help='The "storage" database (one line per slot)')
parser.add_argument('-e', dest='enroll', type=str,
help='Enroll a print using the string as identifier')
parser.add_argument('-v', dest='verify', type=str,
help='Verify print if the stored identifier matches the given identifier')
parser.add_argument('-d', dest='delete', action='store_const', const=True,
help='Delete print as requested by driver')
args = parser.parse_args()
cnt = 0
if args.enroll:
cnt += 1
if args.verify:
cnt += 1
if args.delete:
cnt += 1
assert cnt == 1, 'You need to give exactly one command argument, -e or -v'
prints = []
for slot in args.storage.read().split('\n'):
split = slot.split(' ', 1)
if len(split) == 2:
prints.append(split)
else:
prints.append(None)
# Send image through socket
sockaddr = os.environ['FP_VIRTUAL_MISDEV']
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(sockaddr)
# Assume we get a full message
msg = sock.recv(1024)
assert(msg[-1] == ord(b'\n'))
if args.enroll:
if not msg.startswith(b'ENROLL '):
sys.stderr.write('Expected to enroll, but driver is not ready for enrolling (%s)\n' % str(msg.split(b' ', 1)[0]))
sys.exit(1)
uuid = msg[7:-1].decode('utf-8')
for slot in prints:
if slot is not None and slot[0] == uuid:
sock.sendall(b'2\n') # ENROLL_FAIL
sys.stderr.write('Failed to enroll; UUID has already been stored!\n')
sys.exit(1)
# Find an empty slot
for i, slot in enumerate(prints):
if slot is not None:
continue
prints[i] = (uuid, args.enroll)
sock.sendall(b'1\n') # ENROLL_COMPLETE
break
else:
# TODO: 2: ENROLL_FAIL, but we should send no empty slot!
sock.sendall(b'2\n') # ENROLL_FAIL
sys.stderr.write('Failed to enroll, no free slots!\n')
sys.exit(1)
elif args.verify:
if not msg.startswith(b'VERIFY '):
sys.stderr.write('Expected to verify, but driver is not ready for verifying (%s)\n' % str(msg.split(b' ', 1)[0]))
sys.exit(1)
uuid = msg[7:-1].decode('utf-8')
for slot in prints:
if slot is not None and slot[0] == uuid:
if slot[1] == args.verify:
sock.sendall(b'1\n') # VERIFY_MATCH
else:
sock.sendall(b'0\n') # VERIFY_NO_MATCH
sys.exit(0)
else:
sys.stderr.write('Slot ID is unknown, returning error\n')
sock.sendall(b'-1') # error, need way to report that print is unkown
elif args.delete:
if not msg.startswith(b'DELETE '):
sys.stderr.write('Expected to delete, but driver is not ready for deleting (%s)\n' % str(msg.split(b' ', 1)[0]))
sys.exit(1)
uuid = msg[7:-1].decode('utf-8')
for i, slot in enumerate(prints):
if slot is not None and slot[0] == uuid:
if slot[0] == uuid:
prints[i] = None
sock.sendall(b'0\n') # DELETE_COMPLETE
break
else:
sys.stderr.write('Slot ID is unknown, just report back complete\n')
sock.sendall(b'0') # DELETE_COMPLETE
prints_str = '\n'.join('' if p is None else '%s %s' % (p[0], p[1]) for p in prints)
prints_human_str = '\n'.join('empty slot' if p is None else '%s %s' % (p[0], p[1]) for p in prints)
print('Prints stored now:')
print(prints_human_str)
args.storage.seek(0)
args.storage.truncate()
args.storage.write(prints_str)

View File

@@ -404,7 +404,7 @@ static unsigned char list_BD_values[10] = {
/*
* 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)
{
@@ -412,7 +412,7 @@ 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
@@ -534,7 +534,7 @@ static void restore_gain(void)
/* 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 {
@@ -728,7 +728,7 @@ static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_dat
struct fp_img_dev *dev = user_data;
/* activation on aes1610 seems much more straightforward compared to aes2501 */
/* verify theres anything missing here */
/* verify there's anything missing here */
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT:
fp_dbg("write init");
@@ -749,7 +749,7 @@ static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_d
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 = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
@@ -820,8 +820,7 @@ struct fp_img_driver aes1610_driver = {
.id = AES1610_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1610",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,

View File

@@ -97,8 +97,7 @@ struct fp_img_driver aes1660_driver = {
.id = AES1660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1660",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,

View File

@@ -415,7 +415,7 @@ static 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 {
@@ -792,7 +792,7 @@ static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_d
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 = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
@@ -862,8 +862,7 @@ struct fp_img_driver aes2501_driver = {
.id = AES2501_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2501",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,

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 {

View File

@@ -447,7 +447,7 @@ static void init_read_data_cb(struct libusb_transfer *transfer)
}
/* TODO: use calibration table, datasheet is rather terse on that
* need more info for implementaion */
* need more info for implementation */
static void calibrate_read_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
@@ -541,7 +541,7 @@ static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_d
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)
{
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
@@ -606,8 +606,7 @@ struct fp_img_driver aes2550_driver = {
.id = AES2550_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2550/AES2810",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,

View File

@@ -100,8 +100,7 @@ struct fp_img_driver aes2660_driver = {
.id = AES2660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2660",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,

View File

@@ -165,8 +165,7 @@ struct fp_img_driver aes3500_driver = {
.id = AES3500_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES3500",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,

View File

@@ -137,7 +137,7 @@ static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
do_capture(dev);
}
int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
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);

View File

@@ -52,7 +52,7 @@ struct aes3k_dev {
};
int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state);
int aes3k_dev_activate(struct fp_img_dev *dev);
void aes3k_dev_deactivate(struct fp_img_dev *dev);
#endif

View File

@@ -162,8 +162,7 @@ struct fp_img_driver aes4000_driver = {
.id = AES4000_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES4000",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,

View File

@@ -53,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);
@@ -82,7 +85,7 @@ static int do_write_regv(struct write_regv_data *wdata, int upper_bound)
data[data_offset++] = regwrite->value;
}
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev (FP_DEV(wdata->imgdev)), 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) {
@@ -109,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)
@@ -132,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;
}
@@ -155,8 +160,6 @@ void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
wdata->callback = callback;
wdata->user_data = user_data;
continue_write_regv(wdata);
g_free(wdata);
}
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,

View File

@@ -589,7 +589,7 @@ static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_d
start_finger_detection(dev);
}
int aesX660_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
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);

View File

@@ -116,7 +116,7 @@ static const unsigned char calibrate_cmd[] = {
0x06,
};
int aesX660_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state);
int aesX660_dev_activate(struct fp_img_dev *dev);
void aesX660_dev_deactivate(struct fp_img_dev *dev);
#endif

View File

@@ -27,7 +27,7 @@ enum {
AES2501_ID = 4,
UPEKTC_ID = 5,
AES1610_ID = 6,
FDU2000_ID = 7,
/* FDU2000_ID = 7, */
VCOM5S_ID = 8,
UPEKSONLY_ID = 9,
VFS101_ID = 10,
@@ -42,8 +42,6 @@ enum {
VFS5011_ID = 19,
VFS0050_ID = 20,
ELAN_ID = 21,
VIRTUAL_IMG_ID = 22,
VIRTUAL_MIS_ID = 23,
};
#endif

View File

@@ -293,23 +293,22 @@ static void elan_process_frame_thirds(unsigned short *raw_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();
for (int i = 0; i < ELAN_SKIP_LAST_FRAMES; i++)
elandev->frames = g_slist_next(elandev->frames);
elandev->num_frames -= ELAN_SKIP_LAST_FRAMES;
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(elandev->frames, (GFunc) elandev->process_frame,
&frames);
fpi_do_movement_estimation(&assembling_ctx, frames,
elandev->num_frames);
img = fpi_assemble_frames(&assembling_ctx, frames, elandev->num_frames);
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);
@@ -425,7 +424,8 @@ elan_run_cmd(fpi_ssm *ssm,
elandev->cmd_timeout = cmd_timeout;
if (cmd->devices != ELAN_ALL_DEV && !(cmd->devices & elandev->dev_type)) {
fp_dbg("skipping for this device");
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;
}
@@ -874,7 +874,7 @@ static void dev_deinit(struct fp_img_dev *dev)
fpi_imgdev_close_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)
{
G_DEBUG_HERE();
elan_activate(dev);
@@ -973,8 +973,7 @@ struct fp_img_driver elan_driver = {
.id = ELAN_ID,
.name = FP_COMPONENT,
.full_name = "ElanTech Fingerprint Sensor",
.bus = BUS_TYPE_USB,
.id_table.usb = elan_id_table,
.id_table = elan_id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,

View File

@@ -1316,7 +1316,7 @@ static void m_init_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
fpi_ssm_next_state(ssm);
break;
case INIT_SENSOR_REQ:
/* In captured traffic, those are splitted. */
/* In captured traffic, those are split. */
msg_set_regs(dev, 18, REG_MODE_CONTROL, REG_MODE_SLEEP,
REG_50, 0x0F, REG_GAIN, 0x04, REG_VRT, 0x08,
REG_VRB, 0x0D, REG_VCO_CONTROL, REG_VCO_RT,
@@ -1390,19 +1390,13 @@ static void m_init_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *idev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *idev)
{
struct etes603_dev *dev = FP_INSTANCE_DATA(FP_DEV(idev));
fpi_ssm *ssm;
g_assert(dev);
if (state != IMGDEV_STATE_AWAIT_FINGER_ON) {
fp_err("The driver is in an unexpected state: %d.", state);
fpi_imgdev_activate_complete(idev, 1);
return -1;
}
/* Reset info and data */
dev->is_active = TRUE;
@@ -1481,8 +1475,7 @@ struct fp_img_driver etes603_driver = {
.id = ETES603_ID,
.name = FP_COMPONENT,
.full_name = "EgisTec ES603",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,

View File

@@ -1,319 +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
*/
#define FP_COMPONENT "fdu2000"
#include "drivers_api.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(fpi_dev_get_usb_dev(FP_DEV(dev)), LED_ON))) {
fp_err("Command: LED_ON");
goto out;
}
if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_READY))) {
fp_err("Command: CAPTURE_READY");
goto out;
}
read:
if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_READ))) {
fp_err("Command: CAPTURE_READ");
goto out;
}
/* Now we are ready to read from dev */
r = libusb_bulk_transfer(fpi_dev_get_usb_dev(FP_DEV(dev)), &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(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_END))) {
fp_err("Command: CAPTURE_END");
goto out;
}
if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), 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(fpi_dev_get_usb_dev(FP_DEV(dev)), 1)) < 0 )
// goto out;
if ( (r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0)) < 0 ) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
//if ( (r = usb_set_altinterface(fpi_dev_get_usb_dev(FP_DEV(dev)), 1)) < 0 )
// goto out;
//if ( (r = usb_clear_halt(fpi_dev_get_usb_dev(FP_DEV(dev)), EP_CMD)) < 0 )
// goto out;
/* Make sure sensor mode is not capture_{ready|read} */
if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_END))) {
fp_err("Command: CAPTURE_END");
goto out;
}
if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), 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(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_END))
fp_err("Command: CAPTURE_END");
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x1162, .product = 0x0300 },
{ 0, 0, 0, },
};
struct fp_img_driver fdu2000_driver = {
.driver = {
.id = FDU2000_ID,
.name = FP_COMPONENT,
.full_name = "Secugen FDU 2000",
.bus = BUS_TYPE_USB,
.id_table.usb = 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

@@ -136,7 +136,7 @@ static unsigned char upeksonly_get_pixel(struct fpi_line_asmbl_ctx *ctx,
unsigned char *buf;
unsigned offset;
/* The scans from this device are rolled right by two colums */
/* The scans from this device are rolled right by two columns */
if (x < ctx->line_width - 2)
offset = x + 2;
else if ((x > ctx->line_width - 2) && (x < ctx->line_width))
@@ -296,12 +296,12 @@ static void row_complete(struct fp_img_dev *dev)
sdev->num_blank = 0;
} else {
sdev->num_blank++;
/* Don't consider the scan complete unless theres at least
/* Don't consider the scan complete unless there's 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.
* from before the first joint resulting in a gap after the initial touch.
*/
if (sdev->num_blank > FINGER_REMOVED_THRESHOLD) {
sdev->finger_state = FINGER_REMOVED;
@@ -1268,7 +1268,7 @@ static void initsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
fpi_ssm_start(sdev->loopsm, loopsm_complete);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *dev)
{
struct sonly_dev *sdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = NULL;
@@ -1348,10 +1348,9 @@ struct fp_img_driver upeksonly_driver = {
.id = UPEKSONLY_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchStrip Sensor-Only",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
.usb_discover = dev_discover,
.discover = dev_discover,
},
.flags = 0,
.img_width = -1,

View File

@@ -377,7 +377,7 @@ static void start_capture(struct fp_img_dev *dev)
fpi_ssm_start(ssm, capture_sm_complete);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
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,
@@ -463,8 +463,7 @@ struct fp_img_driver upektc_driver = {
.id = UPEKTC_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchChip/Eikon Touch 300",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,

View File

@@ -287,7 +287,7 @@ static void capture_read_data_cb(struct libusb_transfer *transfer)
fpi_ssm_mark_completed(ssm);
break;
default:
fp_err("Uknown response!\n");
fp_err("Unknown response!\n");
fpi_ssm_mark_failed(ssm, -EIO);
break;
}
@@ -567,7 +567,7 @@ static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_d
start_capture(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 upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
@@ -631,10 +631,9 @@ struct fp_img_driver upektc_img_driver = {
.id = UPEKTC_IMG_ID,
.name = FP_COMPONENT,
.full_name = "Upek TouchChip Fingerprint Coprocessor",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
.usb_discover = discover,
.discover = discover,
},
.flags = 0,
.img_height = IMAGE_HEIGHT,

View File

@@ -1424,8 +1424,7 @@ struct fp_driver upekts_driver = {
.id = UPEKTS_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchStrip",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
.open = dev_init,
.close = dev_exit,

View File

@@ -668,7 +668,7 @@ static uint32_t do_decode(uint8_t *data, int num_bytes, uint32_t key)
data[i] = data[i+1] ^ xorbyte;
}
/* the final byte is implictly zero */
/* the final byte is implicitly zero */
data[i] = 0;
return update_key(key);
}
@@ -1161,20 +1161,10 @@ static void activate_initsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *us
int r = fpi_ssm_get_error(ssm);
fpi_ssm_free(ssm);
if (r) {
fpi_imgdev_activate_complete(dev, r);
return;
}
r = execute_state_change(dev);
fpi_imgdev_activate_complete(dev, r);
}
/* FIXME: having state parameter here is kinda useless, will we ever
* see a scenario where the parameter is useful so early on in the activation
* process? asynchronity means that it'll only be used in a later function
* call. */
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *dev)
{
struct uru4k_dev *urudev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
@@ -1185,7 +1175,6 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
return r;
urudev->scanpwr_irq_timeouts = 0;
urudev->activate_state = state;
ssm = fpi_ssm_new(FP_DEV(dev), init_run_state, INIT_NUM_STATES, dev);
fpi_ssm_start(ssm, activate_initsm_complete);
return 0;
@@ -1431,8 +1420,7 @@ struct fp_img_driver uru4000_driver = {
.id = URU4000_ID,
.name = FP_COMPONENT,
.full_name = "Digital Persona U.are.U 4000/4000B/4500",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE,

View File

@@ -302,7 +302,7 @@ static void loopsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
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 = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), loop_run_state,
@@ -360,8 +360,7 @@ struct fp_img_driver vcom5s_driver = {
.id = VCOM5S_ID,
.name = FP_COMPONENT,
.full_name = "Veridicom 5thSense",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,

View File

@@ -577,7 +577,7 @@ static void activate_ssm(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
vdev->need_report = 0;
}
/* Asyncronously enquire an interrupt */
/* 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,
@@ -684,7 +684,7 @@ static void dev_activate_callback(fpi_ssm *ssm, struct fp_dev *_dev, void *user_
}
/* Activate device */
static int dev_activate(struct fp_img_dev *idev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
@@ -773,8 +773,7 @@ struct fp_img_driver vfs0050_driver = {
.id = VFS0050_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS0050",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},

View File

@@ -47,7 +47,7 @@
/* Minimum image height */
#define VFS_IMG_MIN_HEIGHT 200
/* Scan level thresold */
/* Scan level threshold */
#define VFS_IMG_SLT_BEGIN 768
#define VFS_IMG_SLT_END 64
#define VFS_IMG_SLT_LINES 4
@@ -641,7 +641,7 @@ static int action_completed(struct fp_img_dev *dev)
#define offset(x, y) ((x) + ((y) * VFS_FRAME_SIZE))
/* Screen image to remove noise and find bottom line and height od image */
/* Screen image to remove noise and find bottom line and height of image */
static void img_screen(struct vfs101_dev *vdev)
{
int y, x, count, top;
@@ -654,7 +654,7 @@ static void img_screen(struct vfs101_dev *vdev)
/* Image returned from sensor can contain many empty lines,
* for remove these lines compare byte 282-283 (scan level information)
* with two differents threshold, one for the begin of finger image and
* with two different thresholds, one for the begin of finger image and
* one for the end. To increase stability of the code use a counter
* of lines that satisfy the threshold.
*/
@@ -700,7 +700,7 @@ static void img_screen(struct vfs101_dev *vdev)
vdev->height = top - vdev->bottom + 1;
/* Checkk max height */
/* Check max height */
if (vdev->height > VFS_IMG_MAX_HEIGHT)
vdev->height = VFS_IMG_MAX_HEIGHT;
@@ -1178,7 +1178,7 @@ static void m_init_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
switch (fpi_ssm_get_cur_state(ssm))
{
case M_INIT_0_RECV_DIRTY:
/* Recv eventualy dirty data */
/* Recv eventually dirty data */
vdev->ignore_error = TRUE;
async_recv(ssm, dev);
break;
@@ -1424,7 +1424,7 @@ static void m_init_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
}
/* Activate device */
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *dev)
{
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
@@ -1463,7 +1463,7 @@ static void dev_deactivate(struct fp_img_dev *dev)
/* Reset active state */
vdev->active = FALSE;
/* Handle eventualy existing events */
/* Handle eventually existing events */
while (vdev->transfer)
fp_handle_events();
@@ -1529,8 +1529,7 @@ struct fp_img_driver vfs101_driver =
.id = VFS101_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS101",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},

View File

@@ -191,7 +191,7 @@ static void m_init_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
}
/* Activate device */
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *dev)
{
fpi_ssm *ssm;
@@ -271,8 +271,7 @@ struct fp_img_driver vfs301_driver =
.id = VFS301_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS301",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},

View File

@@ -109,7 +109,7 @@ typedef struct {
unsigned char scan[VFS301_FP_WIDTH];
/* A offseted, stretched, inverted copy of scan... probably could
/* 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];

View File

@@ -1147,7 +1147,7 @@ static const unsigned char vfs301_24[] = { /* 119 B */
*
* The contents of PACKET() inside this blob seems to be some kind
* of a micro-program, which specifies which columns contain what. LE seems
* to be used also here. Not neccessarily is 1 output column described
* to be used also here. Not necessarily is 1 output column described
* by 1 operation. For example the vfs301_line_t::sum section seems
* to perform 2 operations for each column - probably some kind of diff between
* input lines?

View File

@@ -371,7 +371,7 @@ static int process_chunk(struct vfs5011_data *data, int transferred)
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) {
data->lastline = g_malloc(VFS5011_LINE_SIZE);
data->rows = g_slist_prepend(data->rows, data->lastline);
g_memmove(data->lastline, linebuf, VFS5011_LINE_SIZE);
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",
@@ -405,7 +405,7 @@ submit_image(fpi_ssm *ssm,
g_slist_free_full(data->rows, g_free);
data->rows = NULL;
fp_dbg("Image captured, commiting");
fp_dbg("Image captured, committing");
fpi_imgdev_image_captured(dev, img);
}
@@ -846,7 +846,7 @@ static void start_scan(struct fp_img_dev *dev)
fp_dbg("ssm done, getting out");
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *dev)
{
struct vfs5011_data *data;
@@ -890,8 +890,7 @@ struct fp_img_driver vfs5011_driver = {
.id = VFS5011_ID,
.name = "vfs5011",
.full_name = "Validity VFS5011",
.bus = BUS_TYPE_USB,
.id_table.usb = id_table,
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},

View File

@@ -1,225 +0,0 @@
/*
* Virtual driver for image device debugging
*
* 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
*/
/*
* This is a virtual driver to debug the image based drivers. A small
* python script is provided to connect to it via a socket, allowing
* prints to be sent to this device programatically.
* Using this it is possible to test libfprint and fprintd.
*/
#define FP_COMPONENT "virtual_imgdev"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <stdio.h>
#include "drivers_api.h"
struct virt_dev {
fpi_io_condition *socket_io_cond;
fpi_io_condition *client_io_cond;
gint socket_fd;
gint client_fd;
struct fp_img *recv_img;
gssize recv_img_data_bytes;
gssize recv_img_hdr_bytes;
gint recv_img_hdr[2];
};
static void
client_socket_cb (struct fp_dev *dev, int fd, short int events, void *data)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
gboolean nodata = FALSE;
gssize len;
if (!virt->recv_img) {
/* Reading the header, i.e. width/height. */
len = read(fd,
(guint8*)virt->recv_img_hdr + virt->recv_img_hdr_bytes,
sizeof(virt->recv_img_hdr) - virt->recv_img_hdr_bytes);
fp_dbg("Received %zi bytes from client!", len);
if (len > 0) {
virt->recv_img_hdr_bytes += len;
/* Got the full header, create an image for further processing. */
if (virt->recv_img_hdr_bytes == sizeof(virt->recv_img_hdr)) {
virt->recv_img_data_bytes = 0;
virt->recv_img = fpi_img_new (virt->recv_img_hdr[0] * virt->recv_img_hdr[1]);
virt->recv_img->width = virt->recv_img_hdr[0];
virt->recv_img->height = virt->recv_img_hdr[1];
virt->recv_img->flags = 0;
}
}
} else {
len = read(fd,
(guint8*)virt->recv_img->data + virt->recv_img_data_bytes,
virt->recv_img->length - virt->recv_img_data_bytes);
fp_dbg("Received %zi bytes from client!", len);
if (len > 0) {
virt->recv_img_data_bytes += len;
if (virt->recv_img_data_bytes == virt->recv_img->length) {
/* Submit received image to frontend */
fpi_imgdev_report_finger_status (FP_IMG_DEV (dev), TRUE);
fpi_imgdev_image_captured(FP_IMG_DEV (dev), virt->recv_img);
virt->recv_img = NULL;
fpi_imgdev_report_finger_status (FP_IMG_DEV (dev), FALSE);
}
}
}
if (len <= 0) {
fp_dbg("Client disconnected!");
close (virt->client_fd);
virt->client_fd = -1;
virt->recv_img_hdr_bytes = 0;
if (virt->recv_img)
fp_img_free (virt->recv_img);
virt->recv_img = NULL;
fpi_io_condition_remove (virt->client_io_cond);
virt->client_io_cond = NULL;
}
}
static void
new_connection_cb (struct fp_dev *dev, int fd, short int events, void *data)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
int new_client_fd;
new_client_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
fp_dbg("Got a new connection!");
/* Already have a connection, reject this one */
if (virt->client_fd >= 0) {
fp_warn("Rejecting new connection as we already have one!");
close (new_client_fd);
return;
}
virt->client_fd = new_client_fd;
virt->client_io_cond = fpi_io_condition_add (virt->client_fd, POLL_IN, client_socket_cb, dev, NULL);
}
static int
dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
struct virt_dev *virt;
const char *env;
struct sockaddr_un addr = {
.sun_family = AF_UNIX
};
G_DEBUG_HERE();
virt = g_new0(struct virt_dev, 1);
fp_dev_set_instance_data(FP_DEV(dev), virt);
virt->client_fd = -1;
env = fpi_dev_get_virtual_env (FP_DEV (dev));
virt->socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (virt->socket_fd < 0) {
fp_err("Could not create socket: %m");
return virt->socket_fd;
}
strncpy (addr.sun_path, env, sizeof(addr.sun_path) - 1);
unlink(env);
if (bind(virt->socket_fd, &addr, sizeof(struct sockaddr_un)) < 0) {
fp_err("Could not bind address '%s': %m", addr.sun_path);
close (virt->socket_fd);
virt->socket_fd = -1;
return -1;
}
if (listen (virt->socket_fd, 1) < 0) {
fp_err("Could not open socket for listening: %m");
close (virt->socket_fd);
virt->socket_fd = -1;
return -1;
}
virt->socket_io_cond = fpi_io_condition_add (virt->socket_fd, POLL_IN, new_connection_cb, FP_DEV (dev), NULL);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
if (virt->client_fd >= 0) {
fpi_io_condition_remove (virt->client_io_cond);
close (virt->client_fd);
}
if (virt->socket_fd >= 0) {
fpi_io_condition_remove (virt->socket_io_cond);
close (virt->socket_fd);
}
g_free(virt);
fpi_imgdev_close_complete(dev);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
G_DEBUG_HERE();
fpi_imgdev_activate_complete (dev, 0);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
G_DEBUG_HERE();
fpi_imgdev_deactivate_complete (dev);
}
struct fp_img_driver virtual_imgdev_driver = {
.driver = {
.id = VIRTUAL_IMG_ID,
.name = FP_COMPONENT,
.full_name = "Virtual image device for debugging",
.bus = BUS_TYPE_VIRTUAL,
.id_table.virtual_envvar = "FP_VIRTUAL_IMGDEV",
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

@@ -1,424 +0,0 @@
/*
* Virtual match-in-sensor device with internal storage
*
* 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
*/
/*
* This is a virtual driver to debug features that are relevant for
* match-in-sensor (MIS) devices that store data on the sensor itself.
* In this case data needs to be deleted both locally and from the device
* and we should support garbage collection.
*
* The protocol is line based, when a verify/enroll/etc. command is started
* (or is active when connecting) then we send the command and the UUID
* terminated by a newline.
*
* IDLE\n
* VERIFY UUID\n
* ENROLL UUID\n
* DELETE UUID\n (planned)
* LIST (planned)
*
* The other end simply responds with an integer (terminated by newline)
* that matches the internal fprint return codes.
*/
#define FP_COMPONENT "virtual_misdev"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <stdio.h>
#include "drivers_api.h"
#include "fpi-async.h"
#define VIRT_ENROLL_STAGES 1
enum virtdev_state {
STATE_IDLE = 0,
STATE_VERIFY,
STATE_ENROLL,
STATE_DELETE,
};
struct virt_dev {
enum virtdev_state state;
gchar *curr_uuid;
fpi_io_condition *socket_io_cond;
fpi_io_condition *client_io_cond;
gint socket_fd;
gint client_fd;
gssize recv_len;
guchar *recv_buf;
};
static void send_status(struct fp_dev *dev);
static void
handle_response (struct fp_dev *dev, guchar *buf, gssize len)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
gint result = atoi ((gchar*) buf);
switch (virt->state) {
case STATE_IDLE:
fp_info ("Received unexpected status code %i\n", virt->state);
break;
case STATE_VERIFY:
fp_info ("Reporting verify results back %i\n", result);
fpi_drvcb_report_verify_result (dev, result, NULL);
break;
case STATE_ENROLL: {
struct fp_print_data * fdata = NULL;
fp_info ("Reporting enroll results back %i\n", result);
/* If the enroll is "done", then report back the UUID for the print. */
if (result == FP_ENROLL_COMPLETE) {
struct fp_print_data_item *item = NULL;
fdata = fpi_print_data_new (dev);
item = fpi_print_data_item_new(strlen(virt->curr_uuid));
memcpy(item->data, virt->curr_uuid, strlen(virt->curr_uuid));
fpi_print_data_add_item(fdata, item);
}
fpi_drvcb_enroll_stage_completed (dev, result, fdata, NULL);
break;
}
case STATE_DELETE:
fp_info ("Reporting delete results back %i\n", result);
virt->state = STATE_IDLE;
g_free (virt->curr_uuid);
virt->curr_uuid = NULL;
fpi_drvcb_delete_complete (dev, result);
send_status(dev);
break;
default:
g_assert_not_reached();
}
}
static void
send_status(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
gchar *msg = NULL;
if (virt->client_fd < 0)
return;
switch (virt->state) {
case STATE_IDLE:
msg = g_strdup ("IDLE\n");
break;
case STATE_ENROLL:
msg = g_strdup_printf ("ENROLL %s\n", virt->curr_uuid);
break;
case STATE_VERIFY:
msg = g_strdup_printf ("VERIFY %s\n", virt->curr_uuid);
break;
case STATE_DELETE:
msg = g_strdup_printf ("DELETE %s\n", virt->curr_uuid);
break;
}
send(virt->client_fd, msg, strlen(msg), MSG_NOSIGNAL);
g_free (msg);
}
static void
client_socket_cb (struct fp_dev *dev, int fd, short int events, void *data)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
guchar *pos;
guchar buf[512];
gssize len;
len = read(fd, buf, sizeof(buf));
fp_dbg("Received %zi bytes from client!", len);
if (len > 0) {
virt->recv_buf = g_realloc(virt->recv_buf, virt->recv_len + len);
memcpy(virt->recv_buf + virt->recv_len, buf, len);
virt->recv_len += len;
while ((pos = memmem(virt->recv_buf, virt->recv_len, "\n", 1))) {
/* Found a newline, parse the command */
fp_dbg("got a command response! %p %p", virt->recv_buf, pos);
*pos = '\0';
handle_response(dev, virt->recv_buf, pos - virt->recv_buf);
/* And remove the parsed part from the buffer */
virt->recv_len = virt->recv_len - (pos - virt->recv_buf) - 1;
memmove(pos, virt->recv_buf, virt->recv_len);
virt->recv_buf = realloc(virt->recv_buf, virt->recv_len);
}
} else {
fp_dbg("Client disconnected!");
close (virt->client_fd);
virt->client_fd = -1;
fpi_io_condition_remove (virt->client_io_cond);
virt->client_io_cond = NULL;
g_free(virt->recv_buf);
virt->recv_buf = NULL;
virt->recv_len = 0;
}
}
static void
new_connection_cb (struct fp_dev *dev, int fd, short int events, void *data)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
int new_client_fd;
new_client_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
fp_dbg("Got a new connection!");
/* Already have a connection, reject this one */
if (virt->client_fd >= 0) {
fp_warn("Rejecting new connection as we already have one!");
close (new_client_fd);
return;
}
virt->client_fd = new_client_fd;
virt->client_io_cond = fpi_io_condition_add (virt->client_fd, POLL_IN, client_socket_cb, dev, NULL);
send_status(dev);
}
static int
dev_init(struct fp_dev *dev, unsigned long driver_data)
{
struct virt_dev *virt;
const char *env;
struct sockaddr_un addr = {
.sun_family = AF_UNIX
};
G_DEBUG_HERE();
fpi_dev_set_nr_enroll_stages(dev, VIRT_ENROLL_STAGES);
virt = g_new0(struct virt_dev, 1);
fp_dev_set_instance_data(dev, virt);
virt->client_fd = -1;
env = fpi_dev_get_virtual_env (dev);
virt->socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (virt->socket_fd < 0) {
fp_err("Could not create socket: %m");
return virt->socket_fd;
}
strncpy (addr.sun_path, env, sizeof(addr.sun_path) - 1);
unlink(env);
if (bind(virt->socket_fd, &addr, sizeof(struct sockaddr_un)) < 0) {
fp_err("Could not bind address '%s': %m", addr.sun_path);
close (virt->socket_fd);
virt->socket_fd = -1;
return -1;
}
if (listen (virt->socket_fd, 1) < 0) {
fp_err("Could not open socket for listening: %m");
close (virt->socket_fd);
virt->socket_fd = -1;
return -1;
}
virt->socket_io_cond = fpi_io_condition_add (virt->socket_fd, POLL_IN, new_connection_cb, dev, NULL);
fpi_drvcb_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
G_DEBUG_HERE();
if (virt->client_fd >= 0) {
fpi_io_condition_remove (virt->client_io_cond);
close (virt->client_fd);
}
if (virt->socket_fd >= 0) {
fpi_io_condition_remove (virt->socket_io_cond);
close (virt->socket_fd);
}
g_free (virt->curr_uuid);
virt->curr_uuid = NULL;
g_free(virt);
fpi_drvcb_close_complete(dev);
}
static int enroll_start(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
G_DEBUG_HERE();
if (virt->state != STATE_IDLE)
return -1;
g_assert (virt->curr_uuid == NULL);
virt->state = STATE_ENROLL;
virt->curr_uuid = g_uuid_string_random ();
send_status(dev);
fpi_drvcb_enroll_started(dev, 0);
return 0;
}
static int enroll_stop(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
G_DEBUG_HERE();
if (virt->state != STATE_ENROLL)
return -1;
virt->state = STATE_IDLE;
g_free (virt->curr_uuid);
virt->curr_uuid = NULL;
send_status(dev);
fpi_drvcb_enroll_stopped(dev);
return 0;
}
static int verify_start(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
struct fp_print_data *print;
struct fp_print_data_item *item;
G_DEBUG_HERE();
if (virt->state != STATE_IDLE)
return -1;
g_assert (virt->curr_uuid == NULL);
virt->state = STATE_VERIFY;
print = fpi_dev_get_verify_data(dev);
item = fpi_print_data_get_item(print);
/* We expecte a UUID, that means 36 bytes. */
g_assert(item->length == 36);
virt->curr_uuid = g_malloc(37);
virt->curr_uuid[36] = '\0';
memcpy(virt->curr_uuid, item->data, 36);
g_assert(g_uuid_string_is_valid (virt->curr_uuid));
send_status(dev);
fpi_drvcb_verify_started(dev, 0);
return 0;
}
static int verify_stop(struct fp_dev *dev, gboolean iterating)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
G_DEBUG_HERE();
if (virt->state != STATE_VERIFY)
return -1;
virt->state = STATE_IDLE;
g_free (virt->curr_uuid);
virt->curr_uuid = NULL;
send_status(dev);
fpi_drvcb_verify_stopped(dev);
return 0;
}
static int delete_finger(struct fp_dev *dev)
{
struct virt_dev *virt = FP_INSTANCE_DATA(dev);
struct fp_print_data *print;
struct fp_print_data_item *item;
G_DEBUG_HERE();
if (virt->state != STATE_IDLE)
return -1;
g_assert (virt->curr_uuid == NULL);
virt->state = STATE_DELETE;
print = fpi_dev_get_delete_data(dev);
item = fpi_print_data_get_item(print);
/* We expecte a UUID, that means 36 bytes. */
g_assert(item->length == 36);
virt->curr_uuid = g_malloc(37);
virt->curr_uuid[36] = '\0';
memcpy(virt->curr_uuid, item->data, 36);
g_assert(g_uuid_string_is_valid (virt->curr_uuid));
send_status(dev);
return 0;
}
struct fp_driver virtual_misdev_driver = {
.id = VIRTUAL_MIS_ID,
.name = FP_COMPONENT,
.full_name = "Virtual match-in-sensor device with internal storage",
.bus = BUS_TYPE_VIRTUAL,
.id_table.virtual_envvar = "FP_VIRTUAL_MISDEV",
.scan_type = FP_SCAN_TYPE_PRESS,
.open = dev_init,
.close = dev_deinit,
.enroll_start = enroll_start,
.enroll_stop = enroll_stop,
.verify_start = verify_start,
.verify_stop = verify_stop,
.delete_finger = delete_finger,
};

View File

@@ -77,9 +77,6 @@ enum fp_dev_state {
DEV_STATE_CAPTURING,
DEV_STATE_CAPTURE_DONE,
DEV_STATE_CAPTURE_STOPPING,
DEV_STATE_DELETING,
DEV_STATE_DELETE_DONE,
DEV_STATE_DELETE_STOPPING,
};
struct fp_dev {
@@ -93,16 +90,11 @@ struct fp_dev {
int nr_enroll_stages;
enum fp_bus_type bus;
union {
libusb_device_handle *usb;
const char *virtual_env;
int i2c;
} device;
/* FIXME: This will eventually have a bus type */
libusb_device_handle *udev;
/* read-only to drivers */
struct fp_print_data *verify_data;
struct fp_print_data *delete_data;
/* drivers should not mess with any of the below */
enum fp_dev_state state;
@@ -131,8 +123,6 @@ struct fp_dev {
void *capture_cb_data;
fp_operation_stop_cb capture_stop_cb;
void *capture_stop_cb_data;
fp_delete_cb delete_cb;
void *delete_cb_data;
/* FIXME: better place to put this? */
struct fp_print_data **identify_gallery;
@@ -166,13 +156,7 @@ struct fp_img_dev {
/* fp_dscv_dev structure definition */
struct fp_dscv_dev {
enum fp_bus_type bus;
union {
struct libusb_device *usb;
const char *virtual_env;
char *spi_path;
} desc;
struct libusb_device *udev;
struct fp_driver *drv;
unsigned long driver_data;
uint32_t devtype;

View File

@@ -365,7 +365,7 @@ static void median_filter(int *data, int size, int filtersize)
i1 = 0;
if (i2 >= size)
i2 = size-1;
g_memmove(sortbuf, data+i1, (i2-i1+1)*sizeof(int));
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];
}
@@ -480,7 +480,7 @@ out:
img->height = line_ind;
img->width = ctx->line_width;
img->flags = FP_IMG_V_FLIPPED;
g_memmove(img->data, output, ctx->line_width * line_ind);
memmove(img->data, output, ctx->line_width * line_ind);
g_free(offsets);
g_free(output);
return img;

View File

@@ -66,6 +66,7 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb call
{
struct fp_driver *drv;
struct fp_dev *dev;
libusb_device_handle *udevh;
int r;
g_return_val_if_fail(ddev != NULL, -ENODEV);
@@ -74,32 +75,20 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb call
drv = ddev->drv;
G_DEBUG_HERE();
r = libusb_open(ddev->udev, &udevh);
if (r < 0) {
fp_err("usb_open failed, error %d", r);
return r;
}
dev = g_malloc0(sizeof(*dev));
dev->drv = drv;
dev->bus = ddev->bus;
dev->udev = udevh;
dev->__enroll_stage = -1;
dev->state = DEV_STATE_INITIALIZING;
dev->open_cb = callback;
dev->open_cb_data = user_data;
switch (ddev->bus) {
case BUS_TYPE_USB:
r = libusb_open(ddev->desc.usb, &dev->device.usb);
if (r < 0) {
fp_err("usb_open failed, error %d", r);
g_free (dev);
return r;
}
break;
case BUS_TYPE_SPI:
/* TODO: Implement */
break;
case BUS_TYPE_VIRTUAL:
dev->device.virtual_env = ddev->desc.virtual_env;
break;
}
if (!drv->open) {
fpi_drvcb_open_complete(dev, 0);
return 0;
@@ -109,14 +98,7 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb call
r = drv->open(dev, ddev->driver_data);
if (r) {
fp_err("device initialisation failed, driver=%s", drv->name);
switch (ddev->bus) {
case BUS_TYPE_USB:
libusb_close(dev->device.usb);
case BUS_TYPE_SPI:
case BUS_TYPE_VIRTUAL:
/* Nothing to do (this might change for SPI) */
break;
}
libusb_close(udevh);
g_free(dev);
}
@@ -130,16 +112,7 @@ void fpi_drvcb_close_complete(struct fp_dev *dev)
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
dev->state = DEV_STATE_DEINITIALIZED;
fpi_timeout_cancel_all_for_dev(dev);
switch (dev->bus) {
case BUS_TYPE_USB:
libusb_close(dev->device.usb);
case BUS_TYPE_SPI:
case BUS_TYPE_VIRTUAL:
/* Nothing to do (this might change for SPI) */
break;
}
libusb_close(dev->udev);
if (dev->close_cb)
dev->close_cb(dev, dev->close_cb_data);
g_free(dev);
@@ -414,9 +387,21 @@ API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
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;
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_ERROR
&& dev->state != DEV_STATE_VERIFYING
&& dev->state != DEV_STATE_VERIFY_DONE);
@@ -538,9 +523,21 @@ API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
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;
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
&& dev->state != DEV_STATE_IDENTIFY_DONE);
@@ -707,54 +704,3 @@ API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
}
return r;
}
/**
* fp_async_delete_finger:
* @dev: the struct #fp_dev device
* @data: data to delete. Must have been previously enrolled.
* @callback: the callback to call when data deleted.
* @user_data: user data to pass to the callback
*
* Request to delete data in sensor.
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_delete_finger(struct fp_dev *dev,
struct fp_print_data *data, fp_delete_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->delete_finger)
return -ENOTSUP;
dev->state = DEV_STATE_DELETING;
dev->delete_cb = callback;
dev->delete_cb_data = user_data;
dev->delete_data = data;
r = drv->delete_finger(dev);
if (r < 0) {
dev->delete_cb = NULL;
dev->state = DEV_STATE_ERROR;
fp_err("failed to delete data, error %d", r);
}
return r;
}
/* Drivers call this when delete done */
void fpi_drvcb_delete_complete(struct fp_dev *dev, int status)
{
fp_dbg("status %d", status);
BUG_ON(dev->state != DEV_STATE_DELETING);
dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_DELETE_DONE;
if (dev->delete_cb)
dev->delete_cb(dev, status, dev->delete_cb_data);
}

View File

@@ -36,6 +36,4 @@ 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);
void fpi_drvcb_delete_complete(struct fp_dev *dev, int status);
#endif

View File

@@ -181,7 +181,7 @@ API_EXPORTED struct fp_driver **fprint_get_drivers (void)
return (struct fp_driver **) g_ptr_array_free (array, FALSE);
}
static struct fp_driver *find_supporting_usb_driver(libusb_device *udev,
static struct fp_driver *find_supporting_driver(libusb_device *udev,
const struct usb_id **usb_id, uint32_t *devtype)
{
int ret;
@@ -207,13 +207,10 @@ static struct fp_driver *find_supporting_usb_driver(libusb_device *udev,
uint32_t type = 0;
const struct usb_id *id;
if (drv->bus != BUS_TYPE_USB)
continue;
for (id = drv->id_table.usb; id->vendor; id++) {
for (id = drv->id_table; id->vendor; id++) {
if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) {
if (drv->usb_discover) {
int r = drv->usb_discover(&dsc, &type);
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)
@@ -249,81 +246,26 @@ static struct fp_driver *find_supporting_usb_driver(libusb_device *udev,
return best_drv;
}
static struct fp_dscv_dev *discover_usb_dev(libusb_device *udev)
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_usb_driver(udev, &usb_id, &devtype);
drv = find_supporting_driver(udev, &usb_id, &devtype);
if (!drv)
return NULL;
ddev = g_malloc0(sizeof(*ddev));
ddev->drv = drv;
ddev->bus = BUS_TYPE_USB;
ddev->desc.usb = udev;
ddev->udev = udev;
ddev->driver_data = usb_id->driver_data;
ddev->devtype = devtype;
return ddev;
}
static void discover_usb_devs(GPtrArray *found_devices)
{
libusb_device *udev;
libusb_device **devs;
int r;
int i = 0;
r = libusb_get_device_list(fpi_usb_ctx, &devs);
if (r < 0) {
fp_err("couldn't enumerate USB devices, error %d", r);
return;
}
/* 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_usb_dev(udev);
if (!ddev)
continue;
/* discover_usb_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 (found_devices, (gpointer) ddev);
}
libusb_free_device_list(devs, 1);
}
static void discover_virtual_devs(GPtrArray *found_devices)
{
GSList *elem;
for (elem = registered_drivers; elem; elem = g_slist_next(elem)) {
struct fp_driver *drv = elem->data;
struct fp_dscv_dev *ddev = NULL;
const gchar *var;
if (drv->bus != BUS_TYPE_VIRTUAL)
continue;
var = g_getenv (drv->id_table.virtual_envvar);
if (var == NULL)
continue;
ddev = g_malloc0(sizeof(*ddev));
ddev->drv = drv;
ddev->bus = BUS_TYPE_VIRTUAL;
ddev->desc.virtual_env = var;
ddev->devtype = 0;
g_ptr_array_add (found_devices, ddev);
}
}
/**
* fp_discover_devs:
*
@@ -337,25 +279,39 @@ static void discover_virtual_devs(GPtrArray *found_devices)
*/
API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void)
{
GPtrArray *found_devices;
GPtrArray *tmparray;
libusb_device *udev;
libusb_device **devs;
int r;
int i = 0;
g_return_val_if_fail (registered_drivers != NULL, NULL);
found_devices = g_ptr_array_new ();
discover_usb_devs (found_devices);
discover_virtual_devs (found_devices);
/* Return NULL if no devices were found. */
if (found_devices->len == 0) {
g_ptr_array_free (found_devices, TRUE);
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 (found_devices, NULL);
return (struct fp_dscv_dev **) g_ptr_array_free (found_devices, FALSE);
g_ptr_array_add (tmparray, NULL);
return (struct fp_dscv_dev **) g_ptr_array_free (tmparray, FALSE);
}
/**
@@ -374,17 +330,7 @@ API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs)
return;
for (i = 0; devs[i]; i++) {
switch (devs[i]->bus) {
case BUS_TYPE_USB:
libusb_unref_device(devs[i]->desc.usb);
break;
case BUS_TYPE_SPI:
g_free(devs[i]->desc.spi_path);
break;
case BUS_TYPE_VIRTUAL:
/* Nothing to do */
break;
}
libusb_unref_device(devs[i]->udev);
g_free(devs[i]);
}
g_free(devs);
@@ -757,23 +703,6 @@ API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
return dev->drv->identify_start != NULL;
}
/**
* fp_dev_supports_data_in_sensor:
* @dev: the struct #fp_dev device
*
* Determines if a device is capable of storing print data in sensor.
* Not all devices support this functionality.
*
* Returns: 1 if the device is capable of storing data in sensor, 0 otherwise.
*/
API_EXPORTED int fp_dev_supports_data_in_sensor(struct fp_dev *dev)
{
g_return_val_if_fail(dev, 0);
g_return_val_if_fail(dev->drv, 0);
return dev->drv->delete_finger != NULL;
}
/**
* fp_dev_get_img_width:
* @dev: the struct #fp_dev device

View File

@@ -67,37 +67,16 @@ enum fp_driver_type {
DRIVER_IMAGING = 1,
};
/**
* fp_bus_type:
* @BUS_TYPE_USB: USB device
* @BUS_TYPE_SPI: SPI device
* @BUS_TYPE_VIRTUAL: Virtual test bus
*
* The bus type of the device/driver.
*/
enum fp_bus_type {
BUS_TYPE_USB,
BUS_TYPE_SPI,
BUS_TYPE_VIRTUAL
};
struct fp_driver {
const uint16_t id;
const char *name;
const char *full_name;
enum fp_bus_type bus;
union {
const struct usb_id * const usb;
const char * const *i2c;
const char * virtual_envvar;
} id_table;
const struct usb_id * const id_table;
enum fp_driver_type type;
enum fp_scan_type scan_type;
/* Device operations */
int (*usb_discover)(struct libusb_device_descriptor *dsc, uint32_t *devtype);
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);
@@ -108,7 +87,6 @@ struct fp_driver {
int (*identify_stop)(struct fp_dev *dev, gboolean iterating);
int (*capture_start)(struct fp_dev *dev);
int (*capture_stop)(struct fp_dev *dev);
int (*delete_finger)(struct fp_dev *dev);
};
/**
@@ -132,7 +110,7 @@ struct fp_img_driver {
/* 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 (*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);
};

View File

@@ -214,7 +214,7 @@ API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
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 endianess agnostic */
/* 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;
@@ -236,7 +236,7 @@ static struct fp_print_data *fpi_print_data_from_fp1_data(unsigned char *buf,
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 endianess agnostic */
/* 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);
@@ -272,7 +272,7 @@ static struct fp_print_data *fpi_print_data_from_fp2_data(unsigned char *buf,
total_data_len -= item_len;
item = fpi_print_data_item_new(item_len);
/* FIXME: fp_print_data->data content is not endianess agnostic */
/* 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);

View File

@@ -485,9 +485,15 @@ void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status)
*/
void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
{
enum fp_imgdev_action action;
G_DEBUG_HERE();
switch (imgdev->action) {
action = imgdev->action;
imgdev->action = IMG_ACTION_NONE;
imgdev->action_state = 0;
switch (action) {
case IMG_ACTION_ENROLL:
fpi_drvcb_enroll_stopped(FP_DEV(imgdev));
break;
@@ -504,9 +510,6 @@ void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
fp_err("unhandled action %d", imgdev->action);
break;
}
imgdev->action = IMG_ACTION_NONE;
imgdev->action_state = 0;
}
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev)
@@ -533,14 +536,14 @@ int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev)
return height;
}
static int dev_activate(struct fp_img_dev *imgdev, enum fp_imgdev_state state)
static int dev_activate(struct fp_img_dev *imgdev)
{
struct fp_driver *drv = FP_DEV(imgdev)->drv;
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
if (!imgdrv->activate)
return 0;
return imgdrv->activate(imgdev, state);
return imgdrv->activate(imgdev);
}
static void dev_deactivate(struct fp_img_dev *imgdev)
@@ -562,7 +565,7 @@ static int generic_acquire_start(struct fp_dev *dev, int action)
imgdev->action_state = IMG_ACQUIRE_STATE_ACTIVATING;
imgdev->enroll_stage = 0;
r = dev_activate(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
r = dev_activate(imgdev);
if (r < 0)
fp_err("activation failed with error %d", r);

View File

@@ -114,34 +114,7 @@ FP_INSTANCE_DATA (struct fp_dev *dev)
libusb_device_handle *
fpi_dev_get_usb_dev(struct fp_dev *dev)
{
g_assert (dev->bus == BUS_TYPE_USB);
return dev->device.usb;
}
/**
* fpi_dev_get_virtual_env:
* @dev: a struct #fp_dev
*
* Returns the value of the environment variable that is assicated with
* the virtual device.
*
* Returns: the value of the environment variable
*/
const char *
fpi_dev_get_virtual_env(struct fp_dev *dev)
{
g_assert (dev->bus == BUS_TYPE_VIRTUAL);
return dev->device.virtual_env;
}
int
fpi_dev_get_spi_dev(struct fp_dev *dev)
{
g_assert (dev->bus == BUS_TYPE_SPI);
return dev->device.i2c;
return dev->udev;
}
/**
@@ -175,17 +148,3 @@ fpi_dev_get_verify_data(struct fp_dev *dev)
{
return dev->verify_data;
}
/**
* fpi_dev_get_delete_data:
* @dev: a struct #fp_dev
*
* Returns the delete data associated with @dev.
*
* Returns: a struct #fp_print_data pointer or %NULL
*/
struct fp_print_data *
fpi_dev_get_delete_data(struct fp_dev *dev)
{
return dev->delete_data;
}

View File

@@ -40,11 +40,8 @@ void fp_dev_set_instance_data (struct fp_dev *dev,
void *FP_INSTANCE_DATA (struct fp_dev *dev);
libusb_device_handle *fpi_dev_get_usb_dev(struct fp_dev *dev);
const char *fpi_dev_get_virtual_env(struct fp_dev *dev);
int fpi_dev_get_spi_dev(struct fp_dev *dev);
void fpi_dev_set_nr_enroll_stages(struct fp_dev *dev,
int nr_enroll_stages);
struct fp_print_data *fpi_dev_get_verify_data(struct fp_dev *dev);
struct fp_print_data *fpi_dev_get_delete_data(struct fp_dev *dev);
#endif

View File

@@ -405,7 +405,7 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
}
}
/* FIXME: space is wasted if we dont hit the max minutiae count. would
/* FIXME: space is wasted if we don't hit the max minutiae count. would
* be good to make this dynamic. */
print = fpi_print_data_new(FP_DEV(imgdev));
item = fpi_print_data_item_new(sizeof(struct xyt_struct));

View File

@@ -28,7 +28,6 @@
#include <sys/time.h>
#include <glib.h>
#include <glib-unix.h>
#include <libusb.h>
/**
@@ -76,38 +75,64 @@
* for example.
*/
static GMainContext *fpi_main_ctx = NULL;
/* this is a singly-linked list of pending timers, sorted with the timer that
* is expiring soonest at the head. */
static GSList *active_timers = NULL;
/* notifiers for added or removed poll fds */
static fp_pollfd_added_cb fd_added_cb = NULL;
static fp_pollfd_removed_cb fd_removed_cb = NULL;
struct fpi_timeout {
struct timeval expiry;
fpi_timeout_fn callback;
struct fp_dev *dev;
void *data;
GSource *source;
char *name;
};
static void
fpi_timeout_destroy (gpointer data)
static int timeout_sort_fn(gconstpointer _a, gconstpointer _b)
{
fpi_timeout *timeout = data;
fpi_timeout *a = (fpi_timeout *) _a;
fpi_timeout *b = (fpi_timeout *) _b;
struct timeval *tv_a = &a->expiry;
struct timeval *tv_b = &b->expiry;
active_timers = g_slist_remove (active_timers, timeout);
g_free (timeout);
if (timercmp(tv_a, tv_b, <))
return -1;
else if (timercmp(tv_a, tv_b, >))
return 1;
else
return 0;
}
static gboolean
fpi_timeout_wrapper_cb (gpointer data)
static void
fpi_timeout_free(fpi_timeout *timeout)
{
fpi_timeout *timeout = (fpi_timeout*) data;
if (timeout == NULL)
return;
timeout->callback (timeout->dev, timeout->data);
g_free(timeout->name);
g_free(timeout);
}
return G_SOURCE_REMOVE;
/**
* fpi_timeout_set_name:
* @timeout: a #fpi_timeout
* @name: the name to give the timeout
*
* Sets a name for a timeout, allowing that name to be printed
* along with any timeout related debug.
*/
void
fpi_timeout_set_name(fpi_timeout *timeout,
const char *name)
{
g_return_if_fail (timeout != NULL);
g_return_if_fail (name != NULL);
g_return_if_fail (timeout->name == NULL);
timeout->name = g_strdup(name);
}
/**
@@ -129,39 +154,45 @@ fpi_timeout_wrapper_cb (gpointer data)
*
* Returns: an #fpi_timeout structure
*/
fpi_timeout *
fpi_timeout_add(unsigned int msec,
fpi_timeout *fpi_timeout_add(unsigned int msec,
fpi_timeout_fn callback,
struct fp_dev *dev,
void *data)
{
struct timespec ts;
struct timeval add_msec;
fpi_timeout *timeout;
int r;
timeout = g_new0 (fpi_timeout, 1);
timeout->source = g_timeout_source_new (msec);
active_timers = g_slist_prepend (active_timers, timeout);
g_return_val_if_fail (dev != NULL, NULL);
g_source_set_callback (timeout->source, fpi_timeout_wrapper_cb, timeout, fpi_timeout_destroy);
g_source_attach (timeout->source, fpi_main_ctx);
fp_dbg("in %dms", msec);
r = clock_gettime(CLOCK_MONOTONIC, &ts);
if (r < 0) {
fp_err("failed to read monotonic clock, errno=%d", errno);
BUG();
return NULL;
}
timeout = g_new0(fpi_timeout, 1);
timeout->callback = callback;
timeout->dev = dev;
timeout->data = data;
TIMESPEC_TO_TIMEVAL(&timeout->expiry, &ts);
/* calculate timeout expiry by adding delay to current monotonic clock */
timerclear(&add_msec);
add_msec.tv_sec = msec / 1000;
add_msec.tv_usec = (msec % 1000) * 1000;
timeradd(&timeout->expiry, &add_msec, &timeout->expiry);
active_timers = g_slist_insert_sorted(active_timers, timeout,
timeout_sort_fn);
return timeout;
}
/**
* fpi_timeout_set_name:
* @timeout: a #fpi_timeout
* @name: the name to give the timeout
*
* Sets a name for a timeout, allowing that name to be printed
* along with any timeout related debug.
*/
void
fpi_timeout_set_name(fpi_timeout *timeout,
const char *name)
{
g_source_set_name (timeout->source, name);
}
/**
* fpi_timeout_cancel:
* @timeout: an #fpi_timeout structure
@@ -169,110 +200,81 @@ fpi_timeout_set_name(fpi_timeout *timeout,
* Cancels a timeout scheduled with fpi_timeout_add(), and frees the
* @timeout structure.
*/
void
fpi_timeout_cancel(fpi_timeout *timeout)
void fpi_timeout_cancel(fpi_timeout *timeout)
{
g_source_destroy (timeout->source);
G_DEBUG_HERE();
active_timers = g_slist_remove(active_timers, timeout);
fpi_timeout_free(timeout);
}
struct fpi_io_condition {
fpi_io_condition_fn callback;
int fd;
struct fp_dev *dev;
void *data;
GSource *source;
};
static gboolean
fpi_io_condition_wrapper_cb (int fd, GIOCondition cond, gpointer data)
/* get the expiry time and optionally the timeout structure for the next
* timeout. returns 0 if there are no expired timers, or 1 if the
* timeval/timeout output parameters were populated. if the returned timeval
* is zero then it means the timeout has already expired and should be handled
* ASAP. */
static int get_next_timeout_expiry(struct timeval *out,
struct fpi_timeout **out_timeout)
{
fpi_io_condition *io_cond = data;
short events = 0;
struct timespec ts;
struct timeval tv;
struct fpi_timeout *next_timeout;
int r;
if (cond & G_IO_IN)
events |= POLL_IN;
if (cond & G_IO_OUT)
events |= POLL_OUT;
if (cond & G_IO_PRI)
events |= POLL_PRI;
if (cond & G_IO_ERR)
events |= POLL_ERR;
if (cond & G_IO_HUP)
events |= POLL_HUP;
if (active_timers == NULL)
return 0;
io_cond->callback (io_cond->dev, fd, cond, io_cond->data);
r = clock_gettime(CLOCK_MONOTONIC, &ts);
if (r < 0) {
fp_err("failed to read monotonic clock, errno=%d", errno);
return r;
}
TIMESPEC_TO_TIMEVAL(&tv, &ts);
return G_SOURCE_CONTINUE;
next_timeout = active_timers->data;
if (out_timeout)
*out_timeout = next_timeout;
if (timercmp(&tv, &next_timeout->expiry, >=)) {
if (next_timeout->name)
fp_dbg("first timeout '%s' already expired", next_timeout->name);
else
fp_dbg("first timeout already expired");
timerclear(out);
} else {
timersub(&next_timeout->expiry, &tv, out);
if (next_timeout->name)
fp_dbg("next timeout '%s' in %ld.%06lds", next_timeout->name,
out->tv_sec, out->tv_usec);
else
fp_dbg("next timeout in %ld.%06lds", out->tv_sec, out->tv_usec);
}
return 1;
}
static void
fpi_io_condition_destroy (gpointer data)
/* handle a timeout that has expired */
static void handle_timeout(struct fpi_timeout *timeout)
{
fpi_io_condition *io_cond = data;
if (fd_removed_cb)
fd_removed_cb(io_cond->fd);
g_free (io_cond);
G_DEBUG_HERE();
timeout->callback(timeout->dev, timeout->data);
active_timers = g_slist_remove(active_timers, timeout);
fpi_timeout_free(timeout);
}
fpi_io_condition *
fpi_io_condition_add(int fd,
short int events,
fpi_io_condition_fn callback,
struct fp_dev *dev,
void *data)
static int handle_timeouts(void)
{
fpi_io_condition *io_cond;
GIOCondition cond = 0;
struct timeval next_timeout_expiry;
struct fpi_timeout *next_timeout;
int r;
if (events & POLL_IN)
cond |= G_IO_IN;
if (events & POLL_OUT)
cond |= G_IO_OUT;
if (events & POLL_PRI)
cond |= G_IO_PRI;
if (events & POLL_ERR)
cond |= G_IO_ERR;
if (events & POLL_HUP)
cond |= G_IO_HUP;
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
if (r <= 0)
return r;
io_cond = g_new0 (fpi_io_condition, 1);
io_cond->source = g_unix_fd_source_new (fd, cond);
io_cond->fd = fd;
io_cond->callback = callback;
io_cond->data = data;
io_cond->dev = dev;
if (!timerisset(&next_timeout_expiry))
handle_timeout(next_timeout);
g_source_set_callback (io_cond->source,
G_SOURCE_FUNC (fpi_io_condition_wrapper_cb),
io_cond,
fpi_io_condition_destroy);
g_source_attach (io_cond->source, fpi_main_ctx);
if (fd_added_cb)
fd_added_cb(fd, events);
return io_cond;
}
void
fpi_io_condition_set_name(fpi_io_condition *io_cond,
const char *name)
{
g_source_set_name (io_cond->source, name);
}
void
fpi_io_condition_remove(fpi_io_condition *io_cond)
{
g_source_destroy(io_cond->source);
}
static gboolean
dummy_cb (gpointer user_data)
{
return G_SOURCE_REMOVE;
return 0;
}
/**
@@ -288,22 +290,37 @@ dummy_cb (gpointer user_data)
*/
API_EXPORTED int fp_handle_events_timeout(struct timeval *timeout)
{
GSource *timeout_source;
struct timeval next_timeout_expiry;
struct timeval select_timeout;
struct fpi_timeout *next_timeout;
int r;
if (timeout->tv_sec == 0 && timeout->tv_usec == 0) {
g_main_context_iteration (fpi_main_ctx, FALSE);
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
if (r < 0)
return r;
if (r) {
/* timer already expired? */
if (!timerisset(&next_timeout_expiry)) {
handle_timeout(next_timeout);
return 0;
}
/* Register a timeout on the mainloop and then run in blocking mode */
timeout_source = g_timeout_source_new (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
g_source_set_name (timeout_source, "fpi poll timeout");
g_source_set_callback (timeout_source, dummy_cb, NULL, NULL);
g_source_attach (timeout_source, fpi_main_ctx);
g_main_context_iteration (fpi_main_ctx, TRUE);
g_source_destroy (timeout_source);
/* choose the smallest of next URB timeout or user specified timeout */
if (timercmp(&next_timeout_expiry, timeout, <))
select_timeout = next_timeout_expiry;
else
select_timeout = *timeout;
} else {
select_timeout = *timeout;
}
return 0;
r = libusb_handle_events_timeout(fpi_usb_ctx, &select_timeout);
*timeout = select_timeout;
if (r < 0)
return r;
return handle_timeouts();
}
/**
@@ -333,37 +350,35 @@ API_EXPORTED int fp_handle_events(void)
*/
API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
{
int timeout_;
struct timeval fprint_timeout = { 0, 0 };
struct timeval libusb_timeout = { 0, 0 };
int r_fprint;
int r_libusb;
g_return_val_if_fail (g_main_context_acquire (fpi_main_ctx), 0);
r_fprint = get_next_timeout_expiry(&fprint_timeout, NULL);
r_libusb = libusb_get_next_timeout(fpi_usb_ctx, &libusb_timeout);
g_main_context_query (fpi_main_ctx,
G_MININT,
&timeout_,
NULL,
0);
if (timeout_ < 0)
/* if we have no pending timeouts and the same is true for libusb,
* indicate that we have no pending timouts */
if (r_fprint <= 0 && r_libusb <= 0)
return 0;
tv->tv_sec = timeout_ / 1000;
tv->tv_usec = (timeout_ % 1000) * 1000;
/* if fprint have no pending timeouts return libusb timeout */
else if (r_fprint == 0)
*tv = libusb_timeout;
/* if libusb have no pending timeouts return fprint timeout */
else if (r_libusb == 0)
*tv = fprint_timeout;
/* otherwise return the smaller of the 2 timeouts */
else if (timercmp(&fprint_timeout, &libusb_timeout, <))
*tv = fprint_timeout;
else
*tv = libusb_timeout;
return 1;
}
typedef struct {
GSource source;
GSList *fds;
} fpi_libusb_source;
typedef struct {
int fd;
gpointer tag;
} fpi_libusb_fd;
static fpi_libusb_source *libusb_source = NULL;
/**
* fp_get_pollfds:
* @pollfds: output location for a list of pollfds. If non-%NULL, must be
@@ -379,52 +394,33 @@ static fpi_libusb_source *libusb_source = NULL;
*/
API_EXPORTED ssize_t fp_get_pollfds(struct fp_pollfd **pollfds)
{
gint timeout_;
GPollFD fds_static[16];
GPollFD *fds = fds_static;
gint n_fds;
int i;
const struct libusb_pollfd **usbfds;
const struct libusb_pollfd *usbfd;
struct fp_pollfd *ret;
ssize_t cnt = 0;
size_t i = 0;
g_return_val_if_fail (g_main_context_acquire (fpi_main_ctx), -1);
g_return_val_if_fail (fpi_usb_ctx != NULL, -EIO);
n_fds = g_main_context_query (fpi_main_ctx,
G_MININT,
&timeout_,
fds,
G_N_ELEMENTS (fds_static));
if (n_fds > G_N_ELEMENTS (fds_static)) {
fds = g_new0 (GPollFD, n_fds);
n_fds = g_main_context_query (fpi_main_ctx,
G_MININT,
&timeout_,
fds,
n_fds);
usbfds = libusb_get_pollfds(fpi_usb_ctx);
if (!usbfds) {
*pollfds = NULL;
return -EIO;
}
g_main_context_release (fpi_main_ctx);
while ((usbfd = usbfds[i++]) != NULL)
cnt++;
*pollfds = g_new0 (struct fp_pollfd, n_fds);
for (i = 0; i < n_fds; i++) {
(*pollfds)[i].fd = fds[i].fd;
if (fds[i].events & G_IO_IN)
(*pollfds)[i].events |= POLL_IN;
if (fds[i].events & G_IO_OUT)
(*pollfds)[i].events |= POLL_OUT;
if (fds[i].events & G_IO_PRI)
(*pollfds)[i].events |= POLL_PRI;
if (fds[i].events & G_IO_ERR)
(*pollfds)[i].events |= POLL_ERR;
if (fds[i].events & G_IO_HUP)
(*pollfds)[i].events |= POLL_HUP;
ret = g_malloc(sizeof(struct fp_pollfd) * cnt);
i = 0;
while ((usbfd = usbfds[i]) != NULL) {
ret[i].fd = usbfd->fd;
ret[i].events = usbfd->events;
i++;
}
if (fds != fds_static)
g_free (fds);
return n_fds;
*pollfds = ret;
return cnt;
}
/**
@@ -444,129 +440,30 @@ API_EXPORTED void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
static void add_pollfd(int fd, short events, void *user_data)
{
GIOCondition io_cond = 0;
fpi_libusb_fd *data;
gpointer tag;
if (events & POLL_IN)
io_cond |= G_IO_IN;
if (events & POLL_OUT)
io_cond |= G_IO_OUT;
if (events & POLL_PRI)
io_cond |= G_IO_PRI;
if (events & POLL_ERR)
io_cond |= G_IO_ERR;
if (events & POLL_HUP)
io_cond |= G_IO_HUP;
tag = g_source_add_unix_fd (&libusb_source->source, fd, io_cond);
data = g_new0 (fpi_libusb_fd, 1);
data->fd = fd;
data->tag = tag;
libusb_source->fds = g_slist_prepend (libusb_source->fds, data);
if (fd_added_cb)
fd_added_cb(fd, events);
}
static void remove_pollfd(int fd, void *user_data)
{
GSList *elem = g_slist_find_custom (libusb_source->fds, &fd, g_int_equal);
fpi_libusb_fd *item;
g_return_if_fail (elem != NULL);
item = (fpi_libusb_fd*) elem->data;
g_source_remove_unix_fd (&libusb_source->source, item->tag);
libusb_source->fds = g_slist_remove_link (libusb_source->fds, elem);
g_slist_free (elem);
g_free (item);
if (fd_removed_cb)
fd_removed_cb(fd);
}
static gboolean
fpi_libusb_prepare (GSource *source,
gint *timeout_)
{
struct timeval tv;
*timeout_ = -1;
if (libusb_get_next_timeout(fpi_usb_ctx, &tv) == 1) {
if (tv.tv_sec == 0 && tv.tv_usec == 0)
return TRUE;
*timeout_ = tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
return FALSE;
}
static gboolean
fpi_libusb_check (GSource *source)
{
/* Just call into libusb for every mainloop cycle */
return TRUE;
}
static gboolean
fpi_libusb_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
struct timeval zero_tv = { 0, 0 };
libusb_handle_events_timeout (fpi_usb_ctx, &zero_tv);
return G_SOURCE_CONTINUE;
}
static void
fpi_libusb_finalize (GSource *source)
{
fpi_libusb_source *fpi_source = (fpi_libusb_source*) source;
g_slist_free_full (fpi_source->fds, g_free);
}
GSourceFuncs libusb_source_funcs = {
.prepare = fpi_libusb_prepare,
.check = fpi_libusb_check,
.dispatch = fpi_libusb_dispatch,
.finalize = fpi_libusb_finalize,
};
void fpi_poll_init(void)
{
fpi_main_ctx = g_main_context_new ();
libusb_source = (fpi_libusb_source*) g_source_new (&libusb_source_funcs, sizeof(fpi_libusb_source));
g_source_set_name (&libusb_source->source, "libfprint internal libusb source");
g_source_attach (&libusb_source->source, fpi_main_ctx);
libusb_set_pollfd_notifiers(fpi_usb_ctx, add_pollfd, remove_pollfd, NULL);
}
void fpi_poll_exit(void)
{
g_source_destroy (&libusb_source->source);
libusb_source = NULL;
g_main_context_unref (fpi_main_ctx);
fpi_main_ctx = NULL;
g_slist_free_full(active_timers, (GDestroyNotify) fpi_timeout_free);
active_timers = NULL;
fd_added_cb = NULL;
fd_removed_cb = NULL;
libusb_set_pollfd_notifiers(fpi_usb_ctx, NULL, NULL, NULL);
}
void
fpi_timeout_cancel_all_for_dev(struct fp_dev *dev)
{
@@ -576,10 +473,13 @@ fpi_timeout_cancel_all_for_dev(struct fp_dev *dev)
l = active_timers;
while (l) {
fpi_timeout *cb_data = l->data;
struct fpi_timeout *timeout = l->data;
GSList *current = l;
l = l->next;
if (cb_data->dev == dev)
g_source_destroy (cb_data->source);
if (timeout->dev == dev) {
g_free (timeout);
active_timers = g_slist_delete_link (active_timers, current);
}
}
}

View File

@@ -48,34 +48,4 @@ void fpi_timeout_set_name(fpi_timeout *timeout,
const char *name);
void fpi_timeout_cancel(fpi_timeout *timeout);
/**
* fpi_io_condition_fn:
* @dev: the struct #fp_dev passed to fpi_io_condition_add()
* @fd: the registered file descriptor
* @events: The events that poll returend for the descriptor
* @data: the data passed to fpi_io_condition_add()
*
* The prototype of the callback function for fpi_io_condition_add().
* Note that structure will be free'ed when unregistering the condition.
*/
typedef void (*fpi_io_condition_fn)(struct fp_dev *dev, int fd, short int events, void *data);
/**
* fpi_io_cond:
*
* An opaque structure representing a pollable file descriptor and a
* callback function created with fpi_io_condition_add().
*/
typedef struct fpi_io_condition fpi_io_condition;
fpi_io_condition *fpi_io_condition_add(int fd,
short int events,
fpi_io_condition_fn callback,
struct fp_dev *dev,
void *data);
void fpi_io_condition_set_name(fpi_io_condition *io_cond,
const char *name);
void fpi_io_condition_remove(fpi_io_condition *io_cond);
#endif

View File

@@ -149,7 +149,7 @@ static void enroll_stop_cb(struct fp_dev *dev, void *user_data)
* or I/O problems.
*
* The RETRY codes from #fp_enroll_result may be returned from any enroll
* stage. These codes indicate that the scan was not succesful in that the
* stage. These codes indicate that the scan was not successful in that the
* user did not position their finger correctly or similar. When a RETRY code
* is returned, the enrollment stage is <emphasis role="strong">not</emphasis> advanced, so the next call
* into this function will retry the current stage again. The current stage may
@@ -441,70 +441,6 @@ API_EXPORTED int fp_verify_finger(struct fp_dev *dev,
return fp_verify_finger_img(dev, enrolled_print, NULL);
}
struct sync_delete_data {
gboolean populated;
int result;
};
static void sync_delete_cb(struct fp_dev *dev, int result, void *user_data)
{
struct sync_delete_data *ddata = user_data;
ddata->result = result;
ddata->populated = TRUE;
}
/**
* fp_delete_finger:
* @dev: the struct #fp_dev device to perform the operation.
* @enrolled_data: the id need to delete on sensor. This id is
* returned in previously enrolled with a MIS device.
*
* Perform a delete data operation on sensor. When print data is stored on
* sensor, this function is needed when host deletes enrolled finger.
*
* Returns: negative code on error, otherwise a code from #fp_delete_result
*/
API_EXPORTED int fp_delete_finger(struct fp_dev *dev,
struct fp_print_data *enrolled_data)
{
struct sync_delete_data *ddata;
gboolean stopped = FALSE;
int r;
if (!enrolled_data) {
fp_err("no print given");
return -EINVAL;
}
if (!fp_dev_supports_print_data(dev, enrolled_data)) {
fp_err("print is not compatible with device");
return -EINVAL;
}
fp_dbg("to be handled by %s", dev->drv->name);
ddata = g_malloc0(sizeof(struct sync_delete_data));
r = fp_async_delete_finger(dev, enrolled_data, sync_delete_cb, ddata);
if (r < 0) {
fp_dbg("delete_finger error %d", r);
g_free(ddata);
return r;
}
while (!ddata->populated) {
r = fp_handle_events();
if (r < 0)
goto out;
}
r = ddata->result;
fp_dbg("delete_finger result %d", r);
out:
g_free(ddata);
return r;
}
struct sync_identify_data {
gboolean populated;
int result;

View File

@@ -31,11 +31,10 @@ static GList *insert_driver (GList *list,
{
int i;
for (i = 0; driver->id_table.usb[i].vendor != 0; i++) {
for (i = 0; driver->id_table[i].vendor != 0; i++) {
char *key;
key = g_strdup_printf ("%04x:%04x", driver->id_table.usb[i].vendor,
driver->id_table.usb[i].product);
key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product);
if (g_hash_table_lookup (printed, key) != NULL) {
g_free (key);

View File

@@ -40,8 +40,7 @@ static const struct usb_id blacklist_id_table[] = {
};
struct fp_driver whitelist = {
.bus = BUS_TYPE_USB,
.id_table.usb = whitelist_id_table,
.id_table = whitelist_id_table,
.full_name = "Hardcoded whitelist"
};
@@ -53,13 +52,13 @@ static void print_driver (struct fp_driver *driver)
num_printed = 0;
for (i = 0; driver->id_table.usb[i].vendor != 0; i++) {
for (i = 0; driver->id_table[i].vendor != 0; i++) {
char *key;
blacklist = 0;
for (j = 0; blacklist_id_table[j].vendor != 0; j++) {
if (driver->id_table.usb[i].vendor == blacklist_id_table[j].vendor &&
driver->id_table.usb[i].product == blacklist_id_table[j].product) {
if (driver->id_table[i].vendor == blacklist_id_table[j].vendor &&
driver->id_table[i].product == blacklist_id_table[j].product) {
blacklist = 1;
break;
}
@@ -67,8 +66,7 @@ static void print_driver (struct fp_driver *driver)
if (blacklist)
continue;
key = g_strdup_printf ("%04x:%04x", driver->id_table.usb[i].vendor,
driver->id_table.usb[i].product);
key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product);
if (g_hash_table_lookup (printed, key) != NULL) {
g_free (key);
@@ -80,12 +78,8 @@ static void print_driver (struct fp_driver *driver)
if (num_printed == 0)
printf ("# %s\n", driver->full_name);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n",
driver->id_table.usb[i].vendor,
driver->id_table.usb[i].product);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n",
driver->id_table.usb[i].vendor,
driver->id_table.usb[i].product, driver->full_name);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n", driver->id_table[i].vendor, driver->id_table[i].product);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n", driver->id_table[i].vendor, driver->id_table[i].product, driver->full_name);
num_printed++;
}

View File

@@ -267,17 +267,12 @@ int fp_verify_finger(struct fp_dev *dev,
struct fp_print_data *enrolled_print);
int fp_dev_supports_identification(struct fp_dev *dev);
int fp_dev_supports_data_in_sensor(struct fp_dev *dev);
int fp_identify_finger_img(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset,
struct fp_img **img);
int fp_identify_finger(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset);
int fp_delete_finger(struct fp_dev *dev,
struct fp_print_data *enrolled_print);
/* Data handling */
int fp_print_data_load(struct fp_dev *dev, enum fp_finger finger,
struct fp_print_data **data) LIBFPRINT_DEPRECATED;
@@ -456,21 +451,6 @@ int fp_async_capture_start(struct fp_dev *dev, int unconditional, fp_img_operati
int fp_async_capture_stop(struct fp_dev *dev, fp_operation_stop_cb callback, void *user_data);
/**
* fp_delete_result:
* @FP_DELETE_COMPLETE: Delete completed successfully.
* @FP_DELETE_FAIL: Delete failed
*
*/
enum fp_delete_result {
FP_DELETE_COMPLETE = 0,
FP_DELETE_FAIL = 1,
};
typedef void (*fp_delete_cb)(struct fp_dev *dev, int status, void *user_data);
int fp_async_delete_finger(struct fp_dev *dev, struct fp_print_data *data, fp_delete_cb callback, void *user_data);
#ifdef __cplusplus
}
#endif

View File

@@ -97,9 +97,6 @@ foreach driver: drivers
aeslib = true
aes3k = true
endif
if driver == 'fdu2000'
drivers_sources += [ 'drivers/fdu2000.c' ]
endif
if driver == 'vcom5s'
drivers_sources += [ 'drivers/vcom5s.c' ]
endif
@@ -124,12 +121,6 @@ foreach driver: drivers
if driver == 'elan'
drivers_sources += [ 'drivers/elan.c' ]
endif
if driver == 'virtual_imgdev'
drivers_sources += [ 'drivers/virtual_imgdev.c' ]
endif
if driver == 'virtual_misdev'
drivers_sources += [ 'drivers/virtual_misdev.c' ]
endif
endforeach
if aeslib

View File

@@ -1,5 +1,5 @@
project('libfprint', [ 'c', 'cpp' ],
version: '0.99.0',
version: '1.0',
license: 'LGPLv2.1+',
default_options: [
'buildtype=debugoptimized',
@@ -27,7 +27,9 @@ common_cflags = cc.get_supported_arguments([
'-Wstrict-prototypes',
'-Werror-implicit-function-declaration',
'-Wno-pointer-sign',
'-Wshadow'
'-Wshadow',
'-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_50',
'-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_50',
])
# maintaining compatibility with the previous libtool versioning
@@ -39,14 +41,14 @@ revision = 0
libversion = '@0@.@1@.@2@'.format(soversion, current, revision)
# Dependencies
glib_dep = dependency('glib-2.0', version: '>= 2.28')
glib_dep = dependency('glib-2.0', version: '>= 2.50')
libusb_dep = dependency('libusb-1.0', version: '>= 0.9.1')
mathlib_dep = cc.find_library('m', required: false)
# Drivers
drivers = get_option('drivers').split(',')
all_drivers = [ 'upekts', 'upektc', 'upeksonly', 'vcom5s', 'uru4000', 'aes1610', 'aes1660', 'aes2501', 'aes2550', 'aes2660', 'aes3500', 'aes4000', 'vfs101', 'vfs301', 'vfs5011', 'upektc_img', 'etes603', 'vfs0050', 'elan', 'virtual_imgdev', 'virtual_misdev' ]
primitive_drivers = [ 'upekts', 'virtual_misdev' ]
all_drivers = [ 'upekts', 'upektc', 'upeksonly', 'vcom5s', 'uru4000', 'aes1610', 'aes1660', 'aes2501', 'aes2550', 'aes2660', 'aes3500', 'aes4000', 'vfs101', 'vfs301', 'vfs5011', 'upektc_img', 'etes603', 'vfs0050', 'elan' ]
primitive_drivers = [ 'upekts' ]
if drivers == [ 'all' ]
drivers = all_drivers