Compare commits
27 Commits
wip/benzea
...
libfprint-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
823f2c1067 | ||
|
|
19732341d6 | ||
|
|
0e44eb4c1c | ||
|
|
50461b4d7d | ||
|
|
c11126181e | ||
|
|
658c301e3c | ||
|
|
dce52ed081 | ||
|
|
f309f586c9 | ||
|
|
ae1b10dba8 | ||
|
|
860a256f4b | ||
|
|
cb2f46ed08 | ||
|
|
13deaa66fd | ||
|
|
3597a5b0ed | ||
|
|
0352995cb3 | ||
|
|
e9041da7f4 | ||
|
|
252180e088 | ||
|
|
6361c208bd | ||
|
|
2ef8ace543 | ||
|
|
0400bcc85e | ||
|
|
76db6a5a16 | ||
|
|
5b171f9577 | ||
|
|
4cec28416e | ||
|
|
3b32baccf6 | ||
|
|
16875d7776 | ||
|
|
a9600e23a1 | ||
|
|
a4b6813ebf | ||
|
|
ef90938eb9 |
113
.ci/check-abi
Executable 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!')
|
||||||
@@ -7,6 +7,7 @@ variables:
|
|||||||
DEPENDENCIES: libusb1-devel glib2-devel nss-devel pixman-devel systemd meson gtk-doc
|
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
|
gcc gcc-c++ glibc-devel libX11-devel libXv-devel gtk3-devel flatpak-builder
|
||||||
BUNDLE: "org.freedesktop.libfprint.Demo.flatpak"
|
BUNDLE: "org.freedesktop.libfprint.Demo.flatpak"
|
||||||
|
LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546"
|
||||||
|
|
||||||
.build_one_driver_template: &build_one_driver
|
.build_one_driver_template: &build_one_driver
|
||||||
script:
|
script:
|
||||||
@@ -24,10 +25,16 @@ variables:
|
|||||||
- ninja -C _build
|
- ninja -C _build
|
||||||
- ninja -C _build install
|
- 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:
|
build:
|
||||||
stage: build
|
stage: build
|
||||||
<<: *build_one_driver
|
<<: *build_one_driver
|
||||||
<<: *build
|
<<: *build
|
||||||
|
<<: *check_abi
|
||||||
|
|
||||||
.flatpak_script_template: &flatpak_script
|
.flatpak_script_template: &flatpak_script
|
||||||
script:
|
script:
|
||||||
|
|||||||
25
HACKING.md
@@ -3,8 +3,29 @@
|
|||||||
## GLib
|
## GLib
|
||||||
|
|
||||||
Although the library uses GLib internally, libfprint is designed to provide
|
Although the library uses GLib internally, libfprint is designed to provide
|
||||||
a completely neutral interface to it's application users. So, the public
|
a completely neutral interface to its application users. So, the public
|
||||||
APIs should never return GLib data types or anything like that.
|
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
|
## Two-faced-ness
|
||||||
|
|
||||||
|
|||||||
27
NEWS
@@ -1,6 +1,33 @@
|
|||||||
This file lists notable changes in each release. For the full history of all
|
This file lists notable changes in each release. For the full history of all
|
||||||
changes, see ChangeLog.
|
changes, see ChangeLog.
|
||||||
|
|
||||||
|
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
|
2018-12-14: v0.99.0 release
|
||||||
* Library:
|
* Library:
|
||||||
- All the internal API for device driver writers is now covered by the
|
- All the internal API for device driver writers is now covered by the
|
||||||
|
|||||||
3
code-of-conduct.md
Normal 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/
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include <libfprint/fprint.h>
|
#include <libfprint/fprint.h>
|
||||||
|
|
||||||
|
#include "storage.h"
|
||||||
|
|
||||||
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
||||||
{
|
{
|
||||||
struct fp_dscv_dev *ddev = discovered_devs[0];
|
struct fp_dscv_dev *ddev = discovered_devs[0];
|
||||||
@@ -142,10 +144,7 @@ int main(void)
|
|||||||
if (!data)
|
if (!data)
|
||||||
goto out_close;
|
goto out_close;
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
r = print_data_save(data, RIGHT_INDEX);
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
r = fp_print_data_save(data, RIGHT_INDEX);
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
fprintf(stderr, "Data save failed, code %d\n", r);
|
fprintf(stderr, "Data save failed, code %d\n", r);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
examples = [ 'verify_live', 'enroll', 'verify', 'img_capture', 'delete' ]
|
examples = [ 'verify_live', 'enroll', 'verify', 'img_capture' ]
|
||||||
foreach example: examples
|
foreach example: examples
|
||||||
executable(example,
|
executable(example,
|
||||||
example + '.c',
|
[example + '.c', 'storage.c'],
|
||||||
dependencies: libfprint_dep,
|
dependencies: [libfprint_dep, glib_dep],
|
||||||
include_directories: [
|
include_directories: [
|
||||||
root_inc,
|
root_inc,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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.
|
|
||||||
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 102 KiB |
@@ -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
@@ -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
@@ -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 */
|
||||||
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include <libfprint/fprint.h>
|
#include <libfprint/fprint.h>
|
||||||
|
|
||||||
|
#include "storage.h"
|
||||||
|
|
||||||
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
||||||
{
|
{
|
||||||
struct fp_dscv_dev *ddev = discovered_devs[0];
|
struct fp_dscv_dev *ddev = discovered_devs[0];
|
||||||
@@ -117,11 +119,8 @@ int main(void)
|
|||||||
printf("Opened device. Loading previously enrolled right index finger "
|
printf("Opened device. Loading previously enrolled right index finger "
|
||||||
"data...\n");
|
"data...\n");
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
data = print_data_load(dev, RIGHT_INDEX);
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
if (!data) {
|
||||||
r = fp_print_data_load(dev, RIGHT_INDEX, &data);
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
if (r != 0) {
|
|
||||||
fprintf(stderr, "Failed to load fingerprint, error %d\n", r);
|
fprintf(stderr, "Failed to load fingerprint, error %d\n", r);
|
||||||
fprintf(stderr, "Did you remember to enroll your right index finger "
|
fprintf(stderr, "Did you remember to enroll your right index finger "
|
||||||
"first?\n");
|
"first?\n");
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -404,7 +404,7 @@ static unsigned char list_BD_values[10] = {
|
|||||||
/*
|
/*
|
||||||
* Adjust the gain according to the histogram data
|
* Adjust the gain according to the histogram data
|
||||||
* 0xbd, 0xbe, 0x29 and 0x2A registers are affected
|
* 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. */
|
* TODO: This is a basic support for gain. It needs testing/tweaking. */
|
||||||
static int adjust_gain(unsigned char *buffer, int status)
|
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_BE = 0;
|
||||||
static int pos_list_BD = 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
|
// We adjust strip_scan_reqs for future strips and capture_reqs that is sent just after this step
|
||||||
if (status == GAIN_STATUS_FIRST) {
|
if (status == GAIN_STATUS_FIRST) {
|
||||||
if (buffer[1] > 0x78) { // maximum gain needed
|
if (buffer[1] > 0x78) { // maximum gain needed
|
||||||
@@ -534,7 +534,7 @@ static void restore_gain(void)
|
|||||||
|
|
||||||
/* capture SM movement:
|
/* capture SM movement:
|
||||||
* request and read strip,
|
* 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 */
|
* report lack of finger presence, and move to finger detection */
|
||||||
|
|
||||||
enum capture_states {
|
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;
|
struct fp_img_dev *dev = user_data;
|
||||||
|
|
||||||
/* activation on aes1610 seems much more straightforward compared to aes2501 */
|
/* 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)) {
|
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||||
case WRITE_INIT:
|
case WRITE_INIT:
|
||||||
fp_dbg("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);
|
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));
|
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
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,
|
.id = AES1610_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "AuthenTec AES1610",
|
.full_name = "AuthenTec AES1610",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -97,8 +97,7 @@ struct fp_img_driver aes1660_driver = {
|
|||||||
.id = AES1660_ID,
|
.id = AES1660_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "AuthenTec AES1660",
|
.full_name = "AuthenTec AES1660",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -415,7 +415,7 @@ static struct aes_regwrite strip_scan_reqs[] = {
|
|||||||
/* capture SM movement:
|
/* capture SM movement:
|
||||||
* write reqs and read data 1 + 2,
|
* write reqs and read data 1 + 2,
|
||||||
* request and read strip,
|
* 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 */
|
* report lack of finger presence, and move to finger detection */
|
||||||
|
|
||||||
enum capture_states {
|
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);
|
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));
|
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
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,
|
.id = AES2501_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "AuthenTec AES2501",
|
.full_name = "AuthenTec AES2501",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ enum aes2501_mesure_drive {
|
|||||||
|
|
||||||
/* Select (1=square | 0=sine) wave drive during measure */
|
/* Select (1=square | 0=sine) wave drive during measure */
|
||||||
#define AES2501_MEASDRV_SQUARE 0x20
|
#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
|
#define AES2501_MEASDRV_MEASURE_SQUARE 0x10
|
||||||
|
|
||||||
enum aes2501_measure_freq {
|
enum aes2501_measure_freq {
|
||||||
|
|||||||
@@ -447,7 +447,7 @@ static void init_read_data_cb(struct libusb_transfer *transfer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: use calibration table, datasheet is rather terse on that
|
/* 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)
|
static void calibrate_read_data_cb(struct libusb_transfer *transfer)
|
||||||
{
|
{
|
||||||
fpi_ssm *ssm = transfer->user_data;
|
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);
|
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,
|
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
||||||
ACTIVATE_NUM_STATES, dev);
|
ACTIVATE_NUM_STATES, dev);
|
||||||
@@ -606,8 +606,7 @@ struct fp_img_driver aes2550_driver = {
|
|||||||
.id = AES2550_ID,
|
.id = AES2550_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "AuthenTec AES2550/AES2810",
|
.full_name = "AuthenTec AES2550/AES2810",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -100,8 +100,7 @@ struct fp_img_driver aes2660_driver = {
|
|||||||
.id = AES2660_ID,
|
.id = AES2660_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "AuthenTec AES2660",
|
.full_name = "AuthenTec AES2660",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -165,8 +165,7 @@ struct fp_img_driver aes3500_driver = {
|
|||||||
.id = AES3500_ID,
|
.id = AES3500_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "AuthenTec AES3500",
|
.full_name = "AuthenTec AES3500",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
|
|||||||
do_capture(dev);
|
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));
|
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);
|
aes_write_regv(dev, aesdev->init_reqs, aesdev->init_reqs_len, init_reqs_cb, NULL);
|
||||||
|
|||||||
@@ -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);
|
void aes3k_dev_deactivate(struct fp_img_dev *dev);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -162,8 +162,7 @@ struct fp_img_driver aes4000_driver = {
|
|||||||
.id = AES4000_ID,
|
.id = AES4000_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "AuthenTec AES4000",
|
.full_name = "AuthenTec AES4000",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -53,12 +53,15 @@ static void write_regv_trf_complete(struct libusb_transfer *transfer)
|
|||||||
{
|
{
|
||||||
struct write_regv_data *wdata = transfer->user_data;
|
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);
|
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);
|
wdata->callback(wdata->imgdev, -EPROTO, wdata->user_data);
|
||||||
else
|
g_free(wdata);
|
||||||
|
} else {
|
||||||
continue_write_regv(wdata);
|
continue_write_regv(wdata);
|
||||||
|
}
|
||||||
|
|
||||||
g_free(transfer->buffer);
|
g_free(transfer->buffer);
|
||||||
libusb_free_transfer(transfer);
|
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;
|
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);
|
alloc_size, write_regv_trf_complete, wdata, BULK_TIMEOUT);
|
||||||
r = libusb_submit_transfer(transfer);
|
r = libusb_submit_transfer(transfer);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@@ -109,6 +112,7 @@ static void continue_write_regv(struct write_regv_data *wdata)
|
|||||||
if (offset >= wdata->num_regs) {
|
if (offset >= wdata->num_regs) {
|
||||||
fp_dbg("all registers written");
|
fp_dbg("all registers written");
|
||||||
wdata->callback(wdata->imgdev, 0, wdata->user_data);
|
wdata->callback(wdata->imgdev, 0, wdata->user_data);
|
||||||
|
g_free(wdata);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (wdata->regs[offset].reg)
|
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);
|
r = do_write_regv(wdata, upper_bound);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
wdata->callback(wdata->imgdev, r, wdata->user_data);
|
wdata->callback(wdata->imgdev, r, wdata->user_data);
|
||||||
|
g_free(wdata);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,8 +160,6 @@ void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
|
|||||||
wdata->callback = callback;
|
wdata->callback = callback;
|
||||||
wdata->user_data = user_data;
|
wdata->user_data = user_data;
|
||||||
continue_write_regv(wdata);
|
continue_write_regv(wdata);
|
||||||
|
|
||||||
g_free(wdata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
|
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
|
||||||
|
|||||||
@@ -589,7 +589,7 @@ static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_d
|
|||||||
start_finger_detection(dev);
|
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,
|
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
||||||
ACTIVATE_NUM_STATES, dev);
|
ACTIVATE_NUM_STATES, dev);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ static const unsigned char calibrate_cmd[] = {
|
|||||||
0x06,
|
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);
|
void aesX660_dev_deactivate(struct fp_img_dev *dev);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ enum {
|
|||||||
AES2501_ID = 4,
|
AES2501_ID = 4,
|
||||||
UPEKTC_ID = 5,
|
UPEKTC_ID = 5,
|
||||||
AES1610_ID = 6,
|
AES1610_ID = 6,
|
||||||
FDU2000_ID = 7,
|
/* FDU2000_ID = 7, */
|
||||||
VCOM5S_ID = 8,
|
VCOM5S_ID = 8,
|
||||||
UPEKSONLY_ID = 9,
|
UPEKSONLY_ID = 9,
|
||||||
VFS101_ID = 10,
|
VFS101_ID = 10,
|
||||||
@@ -42,8 +42,6 @@ enum {
|
|||||||
VFS5011_ID = 19,
|
VFS5011_ID = 19,
|
||||||
VFS0050_ID = 20,
|
VFS0050_ID = 20,
|
||||||
ELAN_ID = 21,
|
ELAN_ID = 21,
|
||||||
VIRTUAL_IMG_ID = 22,
|
|
||||||
VIRTUAL_MIS_ID = 23,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -293,23 +293,22 @@ static void elan_process_frame_thirds(unsigned short *raw_frame,
|
|||||||
static void elan_submit_image(struct fp_img_dev *dev)
|
static void elan_submit_image(struct fp_img_dev *dev)
|
||||||
{
|
{
|
||||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||||
|
int num_frames;
|
||||||
|
GSList *raw_frames;
|
||||||
GSList *frames = NULL;
|
GSList *frames = NULL;
|
||||||
struct fp_img *img;
|
struct fp_img *img;
|
||||||
|
|
||||||
G_DEBUG_HERE();
|
G_DEBUG_HERE();
|
||||||
|
|
||||||
for (int i = 0; i < ELAN_SKIP_LAST_FRAMES; i++)
|
num_frames = elandev->num_frames - ELAN_SKIP_LAST_FRAMES;
|
||||||
elandev->frames = g_slist_next(elandev->frames);
|
raw_frames = g_slist_nth(elandev->frames, ELAN_SKIP_LAST_FRAMES);
|
||||||
elandev->num_frames -= ELAN_SKIP_LAST_FRAMES;
|
|
||||||
|
|
||||||
assembling_ctx.frame_width = elandev->frame_width;
|
assembling_ctx.frame_width = elandev->frame_width;
|
||||||
assembling_ctx.frame_height = elandev->frame_height;
|
assembling_ctx.frame_height = elandev->frame_height;
|
||||||
assembling_ctx.image_width = elandev->frame_width * 3 / 2;
|
assembling_ctx.image_width = elandev->frame_width * 3 / 2;
|
||||||
g_slist_foreach(elandev->frames, (GFunc) elandev->process_frame,
|
g_slist_foreach(raw_frames, (GFunc) elandev->process_frame, &frames);
|
||||||
&frames);
|
fpi_do_movement_estimation(&assembling_ctx, frames, num_frames);
|
||||||
fpi_do_movement_estimation(&assembling_ctx, frames,
|
img = fpi_assemble_frames(&assembling_ctx, frames, num_frames);
|
||||||
elandev->num_frames);
|
|
||||||
img = fpi_assemble_frames(&assembling_ctx, frames, elandev->num_frames);
|
|
||||||
|
|
||||||
img->flags |= FP_IMG_PARTIAL;
|
img->flags |= FP_IMG_PARTIAL;
|
||||||
fpi_imgdev_image_captured(dev, img);
|
fpi_imgdev_image_captured(dev, img);
|
||||||
@@ -425,7 +424,8 @@ elan_run_cmd(fpi_ssm *ssm,
|
|||||||
elandev->cmd_timeout = cmd_timeout;
|
elandev->cmd_timeout = cmd_timeout;
|
||||||
|
|
||||||
if (cmd->devices != ELAN_ALL_DEV && !(cmd->devices & elandev->dev_type)) {
|
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);
|
elan_cmd_done(ssm);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -874,7 +874,7 @@ static void dev_deinit(struct fp_img_dev *dev)
|
|||||||
fpi_imgdev_close_complete(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();
|
G_DEBUG_HERE();
|
||||||
elan_activate(dev);
|
elan_activate(dev);
|
||||||
@@ -973,8 +973,7 @@ struct fp_img_driver elan_driver = {
|
|||||||
.id = ELAN_ID,
|
.id = ELAN_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "ElanTech Fingerprint Sensor",
|
.full_name = "ElanTech Fingerprint Sensor",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = elan_id_table,
|
||||||
.id_table.usb = elan_id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -1316,7 +1316,7 @@ static void m_init_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
|||||||
fpi_ssm_next_state(ssm);
|
fpi_ssm_next_state(ssm);
|
||||||
break;
|
break;
|
||||||
case INIT_SENSOR_REQ:
|
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,
|
msg_set_regs(dev, 18, REG_MODE_CONTROL, REG_MODE_SLEEP,
|
||||||
REG_50, 0x0F, REG_GAIN, 0x04, REG_VRT, 0x08,
|
REG_50, 0x0F, REG_GAIN, 0x04, REG_VRT, 0x08,
|
||||||
REG_VRB, 0x0D, REG_VCO_CONTROL, REG_VCO_RT,
|
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);
|
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));
|
struct etes603_dev *dev = FP_INSTANCE_DATA(FP_DEV(idev));
|
||||||
fpi_ssm *ssm;
|
fpi_ssm *ssm;
|
||||||
|
|
||||||
g_assert(dev);
|
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 */
|
/* Reset info and data */
|
||||||
dev->is_active = TRUE;
|
dev->is_active = TRUE;
|
||||||
|
|
||||||
@@ -1481,8 +1475,7 @@ struct fp_img_driver etes603_driver = {
|
|||||||
.id = ETES603_ID,
|
.id = ETES603_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "EgisTec ES603",
|
.full_name = "EgisTec ES603",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -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,
|
|
||||||
};
|
|
||||||
@@ -136,7 +136,7 @@ static unsigned char upeksonly_get_pixel(struct fpi_line_asmbl_ctx *ctx,
|
|||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
unsigned offset;
|
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)
|
if (x < ctx->line_width - 2)
|
||||||
offset = x + 2;
|
offset = x + 2;
|
||||||
else if ((x > ctx->line_width - 2) && (x < ctx->line_width))
|
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;
|
sdev->num_blank = 0;
|
||||||
} else {
|
} else {
|
||||||
sdev->num_blank++;
|
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.
|
* MIN_ROWS recorded or very long blank read occurred.
|
||||||
*
|
*
|
||||||
* Typical problem spot: one brief touch before starting the
|
* Typical problem spot: one brief touch before starting the
|
||||||
* actual scan. Happens most commonly if scan is started
|
* 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) {
|
if (sdev->num_blank > FINGER_REMOVED_THRESHOLD) {
|
||||||
sdev->finger_state = FINGER_REMOVED;
|
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);
|
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));
|
struct sonly_dev *sdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||||
fpi_ssm *ssm = NULL;
|
fpi_ssm *ssm = NULL;
|
||||||
@@ -1348,10 +1348,9 @@ struct fp_img_driver upeksonly_driver = {
|
|||||||
.id = UPEKSONLY_ID,
|
.id = UPEKSONLY_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "UPEK TouchStrip Sensor-Only",
|
.full_name = "UPEK TouchStrip Sensor-Only",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
.usb_discover = dev_discover,
|
.discover = dev_discover,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.img_width = -1,
|
.img_width = -1,
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ static void start_capture(struct fp_img_dev *dev)
|
|||||||
fpi_ssm_start(ssm, capture_sm_complete);
|
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));
|
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
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,
|
.id = UPEKTC_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "UPEK TouchChip/Eikon Touch 300",
|
.full_name = "UPEK TouchChip/Eikon Touch 300",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ static void capture_read_data_cb(struct libusb_transfer *transfer)
|
|||||||
fpi_ssm_mark_completed(ssm);
|
fpi_ssm_mark_completed(ssm);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fp_err("Uknown response!\n");
|
fp_err("Unknown response!\n");
|
||||||
fpi_ssm_mark_failed(ssm, -EIO);
|
fpi_ssm_mark_failed(ssm, -EIO);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -567,7 +567,7 @@ static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_d
|
|||||||
start_capture(dev);
|
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));
|
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
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,
|
.id = UPEKTC_IMG_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "Upek TouchChip Fingerprint Coprocessor",
|
.full_name = "Upek TouchChip Fingerprint Coprocessor",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
.usb_discover = discover,
|
.discover = discover,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.img_height = IMAGE_HEIGHT,
|
.img_height = IMAGE_HEIGHT,
|
||||||
|
|||||||
@@ -1424,8 +1424,7 @@ struct fp_driver upekts_driver = {
|
|||||||
.id = UPEKTS_ID,
|
.id = UPEKTS_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "UPEK TouchStrip",
|
.full_name = "UPEK TouchStrip",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
.open = dev_init,
|
.open = dev_init,
|
||||||
.close = dev_exit,
|
.close = dev_exit,
|
||||||
|
|||||||
@@ -668,7 +668,7 @@ static uint32_t do_decode(uint8_t *data, int num_bytes, uint32_t key)
|
|||||||
data[i] = data[i+1] ^ xorbyte;
|
data[i] = data[i+1] ^ xorbyte;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the final byte is implictly zero */
|
/* the final byte is implicitly zero */
|
||||||
data[i] = 0;
|
data[i] = 0;
|
||||||
return update_key(key);
|
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);
|
int r = fpi_ssm_get_error(ssm);
|
||||||
fpi_ssm_free(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);
|
fpi_imgdev_activate_complete(dev, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: having state parameter here is kinda useless, will we ever
|
static int dev_activate(struct fp_img_dev *dev)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
struct uru4k_dev *urudev = FP_INSTANCE_DATA(FP_DEV(dev));
|
struct uru4k_dev *urudev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||||
fpi_ssm *ssm;
|
fpi_ssm *ssm;
|
||||||
@@ -1185,7 +1175,6 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
urudev->scanpwr_irq_timeouts = 0;
|
urudev->scanpwr_irq_timeouts = 0;
|
||||||
urudev->activate_state = state;
|
|
||||||
ssm = fpi_ssm_new(FP_DEV(dev), init_run_state, INIT_NUM_STATES, dev);
|
ssm = fpi_ssm_new(FP_DEV(dev), init_run_state, INIT_NUM_STATES, dev);
|
||||||
fpi_ssm_start(ssm, activate_initsm_complete);
|
fpi_ssm_start(ssm, activate_initsm_complete);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1431,8 +1420,7 @@ struct fp_img_driver uru4000_driver = {
|
|||||||
.id = URU4000_ID,
|
.id = URU4000_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "Digital Persona U.are.U 4000/4000B/4500",
|
.full_name = "Digital Persona U.are.U 4000/4000B/4500",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||||
},
|
},
|
||||||
.flags = FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE,
|
.flags = FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE,
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ static void loopsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
|||||||
fpi_imgdev_deactivate_complete(dev);
|
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));
|
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), loop_run_state,
|
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,
|
.id = VCOM5S_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "Veridicom 5thSense",
|
.full_name = "Veridicom 5thSense",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||||
},
|
},
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
|||||||
@@ -577,7 +577,7 @@ static void activate_ssm(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
|||||||
vdev->need_report = 0;
|
vdev->need_report = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Asyncronously enquire an interrupt */
|
/* Asynchronously enquire an interrupt */
|
||||||
vdev->transfer = fpi_usb_alloc();
|
vdev->transfer = fpi_usb_alloc();
|
||||||
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||||
libusb_fill_interrupt_transfer(vdev->transfer, usb_dev, 0x83,
|
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 */
|
/* 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));
|
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
|
||||||
|
|
||||||
@@ -773,8 +773,7 @@ struct fp_img_driver vfs0050_driver = {
|
|||||||
.id = VFS0050_ID,
|
.id = VFS0050_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "Validity VFS0050",
|
.full_name = "Validity VFS0050",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
/* Minimum image height */
|
/* Minimum image height */
|
||||||
#define VFS_IMG_MIN_HEIGHT 200
|
#define VFS_IMG_MIN_HEIGHT 200
|
||||||
|
|
||||||
/* Scan level thresold */
|
/* Scan level threshold */
|
||||||
#define VFS_IMG_SLT_BEGIN 768
|
#define VFS_IMG_SLT_BEGIN 768
|
||||||
#define VFS_IMG_SLT_END 64
|
#define VFS_IMG_SLT_END 64
|
||||||
#define VFS_IMG_SLT_LINES 4
|
#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))
|
#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)
|
static void img_screen(struct vfs101_dev *vdev)
|
||||||
{
|
{
|
||||||
int y, x, count, top;
|
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,
|
/* Image returned from sensor can contain many empty lines,
|
||||||
* for remove these lines compare byte 282-283 (scan level information)
|
* 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
|
* one for the end. To increase stability of the code use a counter
|
||||||
* of lines that satisfy the threshold.
|
* of lines that satisfy the threshold.
|
||||||
*/
|
*/
|
||||||
@@ -700,7 +700,7 @@ static void img_screen(struct vfs101_dev *vdev)
|
|||||||
|
|
||||||
vdev->height = top - vdev->bottom + 1;
|
vdev->height = top - vdev->bottom + 1;
|
||||||
|
|
||||||
/* Checkk max height */
|
/* Check max height */
|
||||||
if (vdev->height > VFS_IMG_MAX_HEIGHT)
|
if (vdev->height > VFS_IMG_MAX_HEIGHT)
|
||||||
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))
|
switch (fpi_ssm_get_cur_state(ssm))
|
||||||
{
|
{
|
||||||
case M_INIT_0_RECV_DIRTY:
|
case M_INIT_0_RECV_DIRTY:
|
||||||
/* Recv eventualy dirty data */
|
/* Recv eventually dirty data */
|
||||||
vdev->ignore_error = TRUE;
|
vdev->ignore_error = TRUE;
|
||||||
async_recv(ssm, dev);
|
async_recv(ssm, dev);
|
||||||
break;
|
break;
|
||||||
@@ -1424,7 +1424,7 @@ static void m_init_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Activate device */
|
/* 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));
|
struct vfs101_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||||
fpi_ssm *ssm;
|
fpi_ssm *ssm;
|
||||||
@@ -1463,7 +1463,7 @@ static void dev_deactivate(struct fp_img_dev *dev)
|
|||||||
/* Reset active state */
|
/* Reset active state */
|
||||||
vdev->active = FALSE;
|
vdev->active = FALSE;
|
||||||
|
|
||||||
/* Handle eventualy existing events */
|
/* Handle eventually existing events */
|
||||||
while (vdev->transfer)
|
while (vdev->transfer)
|
||||||
fp_handle_events();
|
fp_handle_events();
|
||||||
|
|
||||||
@@ -1529,8 +1529,7 @@ struct fp_img_driver vfs101_driver =
|
|||||||
.id = VFS101_ID,
|
.id = VFS101_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "Validity VFS101",
|
.full_name = "Validity VFS101",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ static void m_init_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Activate device */
|
/* 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;
|
fpi_ssm *ssm;
|
||||||
|
|
||||||
@@ -271,8 +271,7 @@ struct fp_img_driver vfs301_driver =
|
|||||||
.id = VFS301_ID,
|
.id = VFS301_ID,
|
||||||
.name = FP_COMPONENT,
|
.name = FP_COMPONENT,
|
||||||
.full_name = "Validity VFS301",
|
.full_name = "Validity VFS301",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ typedef struct {
|
|||||||
|
|
||||||
unsigned char scan[VFS301_FP_WIDTH];
|
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?
|
* serve finger motion speed detection?
|
||||||
* Seems to be subdivided to some 10B + 53B + 1B blocks */
|
* Seems to be subdivided to some 10B + 53B + 1B blocks */
|
||||||
unsigned char mirror[64];
|
unsigned char mirror[64];
|
||||||
|
|||||||
@@ -1147,7 +1147,7 @@ static const unsigned char vfs301_24[] = { /* 119 B */
|
|||||||
*
|
*
|
||||||
* The contents of PACKET() inside this blob seems to be some kind
|
* The contents of PACKET() inside this blob seems to be some kind
|
||||||
* of a micro-program, which specifies which columns contain what. LE seems
|
* 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
|
* 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
|
* to perform 2 operations for each column - probably some kind of diff between
|
||||||
* input lines?
|
* input lines?
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ static int process_chunk(struct vfs5011_data *data, int transferred)
|
|||||||
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) {
|
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) {
|
||||||
data->lastline = g_malloc(VFS5011_LINE_SIZE);
|
data->lastline = g_malloc(VFS5011_LINE_SIZE);
|
||||||
data->rows = g_slist_prepend(data->rows, data->lastline);
|
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++;
|
data->lines_recorded++;
|
||||||
if (data->lines_recorded >= data->max_lines_recorded) {
|
if (data->lines_recorded >= data->max_lines_recorded) {
|
||||||
fp_dbg("process_chunk: recorded %d lines, finishing",
|
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);
|
g_slist_free_full(data->rows, g_free);
|
||||||
data->rows = NULL;
|
data->rows = NULL;
|
||||||
|
|
||||||
fp_dbg("Image captured, commiting");
|
fp_dbg("Image captured, committing");
|
||||||
|
|
||||||
fpi_imgdev_image_captured(dev, img);
|
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");
|
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;
|
struct vfs5011_data *data;
|
||||||
|
|
||||||
@@ -890,8 +890,7 @@ struct fp_img_driver vfs5011_driver = {
|
|||||||
.id = VFS5011_ID,
|
.id = VFS5011_ID,
|
||||||
.name = "vfs5011",
|
.name = "vfs5011",
|
||||||
.full_name = "Validity VFS5011",
|
.full_name = "Validity VFS5011",
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = id_table,
|
||||||
.id_table.usb = id_table,
|
|
||||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
|
||||||
};
|
|
||||||
@@ -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,
|
|
||||||
};
|
|
||||||
@@ -77,9 +77,6 @@ enum fp_dev_state {
|
|||||||
DEV_STATE_CAPTURING,
|
DEV_STATE_CAPTURING,
|
||||||
DEV_STATE_CAPTURE_DONE,
|
DEV_STATE_CAPTURE_DONE,
|
||||||
DEV_STATE_CAPTURE_STOPPING,
|
DEV_STATE_CAPTURE_STOPPING,
|
||||||
DEV_STATE_DELETING,
|
|
||||||
DEV_STATE_DELETE_DONE,
|
|
||||||
DEV_STATE_DELETE_STOPPING,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fp_dev {
|
struct fp_dev {
|
||||||
@@ -93,16 +90,11 @@ struct fp_dev {
|
|||||||
|
|
||||||
int nr_enroll_stages;
|
int nr_enroll_stages;
|
||||||
|
|
||||||
enum fp_bus_type bus;
|
/* FIXME: This will eventually have a bus type */
|
||||||
union {
|
libusb_device_handle *udev;
|
||||||
libusb_device_handle *usb;
|
|
||||||
const char *virtual_env;
|
|
||||||
int i2c;
|
|
||||||
} device;
|
|
||||||
|
|
||||||
/* read-only to drivers */
|
/* read-only to drivers */
|
||||||
struct fp_print_data *verify_data;
|
struct fp_print_data *verify_data;
|
||||||
struct fp_print_data *delete_data;
|
|
||||||
|
|
||||||
/* drivers should not mess with any of the below */
|
/* drivers should not mess with any of the below */
|
||||||
enum fp_dev_state state;
|
enum fp_dev_state state;
|
||||||
@@ -131,8 +123,6 @@ struct fp_dev {
|
|||||||
void *capture_cb_data;
|
void *capture_cb_data;
|
||||||
fp_operation_stop_cb capture_stop_cb;
|
fp_operation_stop_cb capture_stop_cb;
|
||||||
void *capture_stop_cb_data;
|
void *capture_stop_cb_data;
|
||||||
fp_delete_cb delete_cb;
|
|
||||||
void *delete_cb_data;
|
|
||||||
|
|
||||||
/* FIXME: better place to put this? */
|
/* FIXME: better place to put this? */
|
||||||
struct fp_print_data **identify_gallery;
|
struct fp_print_data **identify_gallery;
|
||||||
@@ -166,13 +156,7 @@ struct fp_img_dev {
|
|||||||
|
|
||||||
/* fp_dscv_dev structure definition */
|
/* fp_dscv_dev structure definition */
|
||||||
struct fp_dscv_dev {
|
struct fp_dscv_dev {
|
||||||
enum fp_bus_type bus;
|
struct libusb_device *udev;
|
||||||
union {
|
|
||||||
struct libusb_device *usb;
|
|
||||||
const char *virtual_env;
|
|
||||||
char *spi_path;
|
|
||||||
} desc;
|
|
||||||
|
|
||||||
struct fp_driver *drv;
|
struct fp_driver *drv;
|
||||||
unsigned long driver_data;
|
unsigned long driver_data;
|
||||||
uint32_t devtype;
|
uint32_t devtype;
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ static void median_filter(int *data, int size, int filtersize)
|
|||||||
i1 = 0;
|
i1 = 0;
|
||||||
if (i2 >= size)
|
if (i2 >= size)
|
||||||
i2 = size-1;
|
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);
|
g_qsort_with_data(sortbuf, i2-i1+1, sizeof(int), cmpint, NULL);
|
||||||
result[i] = sortbuf[(i2-i1+1)/2];
|
result[i] = sortbuf[(i2-i1+1)/2];
|
||||||
}
|
}
|
||||||
@@ -480,7 +480,7 @@ out:
|
|||||||
img->height = line_ind;
|
img->height = line_ind;
|
||||||
img->width = ctx->line_width;
|
img->width = ctx->line_width;
|
||||||
img->flags = FP_IMG_V_FLIPPED;
|
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(offsets);
|
||||||
g_free(output);
|
g_free(output);
|
||||||
return img;
|
return img;
|
||||||
|
|||||||
@@ -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_driver *drv;
|
||||||
struct fp_dev *dev;
|
struct fp_dev *dev;
|
||||||
|
libusb_device_handle *udevh;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
g_return_val_if_fail(ddev != NULL, -ENODEV);
|
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;
|
drv = ddev->drv;
|
||||||
|
|
||||||
G_DEBUG_HERE();
|
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 = g_malloc0(sizeof(*dev));
|
||||||
dev->drv = drv;
|
dev->drv = drv;
|
||||||
dev->bus = ddev->bus;
|
dev->udev = udevh;
|
||||||
dev->__enroll_stage = -1;
|
dev->__enroll_stage = -1;
|
||||||
dev->state = DEV_STATE_INITIALIZING;
|
dev->state = DEV_STATE_INITIALIZING;
|
||||||
dev->open_cb = callback;
|
dev->open_cb = callback;
|
||||||
dev->open_cb_data = user_data;
|
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) {
|
if (!drv->open) {
|
||||||
fpi_drvcb_open_complete(dev, 0);
|
fpi_drvcb_open_complete(dev, 0);
|
||||||
return 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);
|
r = drv->open(dev, ddev->driver_data);
|
||||||
if (r) {
|
if (r) {
|
||||||
fp_err("device initialisation failed, driver=%s", drv->name);
|
fp_err("device initialisation failed, driver=%s", drv->name);
|
||||||
switch (ddev->bus) {
|
libusb_close(udevh);
|
||||||
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;
|
|
||||||
}
|
|
||||||
g_free(dev);
|
g_free(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,16 +112,7 @@ void fpi_drvcb_close_complete(struct fp_dev *dev)
|
|||||||
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
|
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
|
||||||
dev->state = DEV_STATE_DEINITIALIZED;
|
dev->state = DEV_STATE_DEINITIALIZED;
|
||||||
fpi_timeout_cancel_all_for_dev(dev);
|
fpi_timeout_cancel_all_for_dev(dev);
|
||||||
|
libusb_close(dev->udev);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->close_cb)
|
if (dev->close_cb)
|
||||||
dev->close_cb(dev, dev->close_cb_data);
|
dev->close_cb(dev, dev->close_cb_data);
|
||||||
g_free(dev);
|
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_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;
|
drv = dev->drv;
|
||||||
|
|
||||||
G_DEBUG_HERE();
|
|
||||||
BUG_ON(dev->state != DEV_STATE_ERROR
|
BUG_ON(dev->state != DEV_STATE_ERROR
|
||||||
&& dev->state != DEV_STATE_VERIFYING
|
&& dev->state != DEV_STATE_VERIFYING
|
||||||
&& dev->state != DEV_STATE_VERIFY_DONE);
|
&& 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_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;
|
drv = dev->drv;
|
||||||
|
|
||||||
G_DEBUG_HERE();
|
|
||||||
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
|
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
|
||||||
&& dev->state != DEV_STATE_IDENTIFY_DONE);
|
&& dev->state != DEV_STATE_IDENTIFY_DONE);
|
||||||
|
|
||||||
@@ -707,54 +704,3 @@ API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
|
|||||||
}
|
}
|
||||||
return r;
|
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);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -36,6 +36,4 @@ void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
|
|||||||
struct fp_img *img);
|
struct fp_img *img);
|
||||||
void fpi_drvcb_verify_stopped(struct fp_dev *dev);
|
void fpi_drvcb_verify_stopped(struct fp_dev *dev);
|
||||||
|
|
||||||
void fpi_drvcb_delete_complete(struct fp_dev *dev, int status);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ API_EXPORTED struct fp_driver **fprint_get_drivers (void)
|
|||||||
return (struct fp_driver **) g_ptr_array_free (array, FALSE);
|
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)
|
const struct usb_id **usb_id, uint32_t *devtype)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@@ -207,13 +207,10 @@ static struct fp_driver *find_supporting_usb_driver(libusb_device *udev,
|
|||||||
uint32_t type = 0;
|
uint32_t type = 0;
|
||||||
const struct usb_id *id;
|
const struct usb_id *id;
|
||||||
|
|
||||||
if (drv->bus != BUS_TYPE_USB)
|
for (id = drv->id_table; id->vendor; id++) {
|
||||||
continue;
|
|
||||||
|
|
||||||
for (id = drv->id_table.usb; id->vendor; id++) {
|
|
||||||
if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) {
|
if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) {
|
||||||
if (drv->usb_discover) {
|
if (drv->discover) {
|
||||||
int r = drv->usb_discover(&dsc, &type);
|
int r = drv->discover(&dsc, &type);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
fp_err("%s discover failed, code %d", drv->name, r);
|
fp_err("%s discover failed, code %d", drv->name, r);
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
@@ -249,81 +246,26 @@ static struct fp_driver *find_supporting_usb_driver(libusb_device *udev,
|
|||||||
return best_drv;
|
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;
|
const struct usb_id *usb_id;
|
||||||
struct fp_driver *drv;
|
struct fp_driver *drv;
|
||||||
struct fp_dscv_dev *ddev;
|
struct fp_dscv_dev *ddev;
|
||||||
uint32_t devtype;
|
uint32_t devtype;
|
||||||
|
|
||||||
drv = find_supporting_usb_driver(udev, &usb_id, &devtype);
|
drv = find_supporting_driver(udev, &usb_id, &devtype);
|
||||||
|
|
||||||
if (!drv)
|
if (!drv)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ddev = g_malloc0(sizeof(*ddev));
|
ddev = g_malloc0(sizeof(*ddev));
|
||||||
ddev->drv = drv;
|
ddev->drv = drv;
|
||||||
ddev->bus = BUS_TYPE_USB;
|
ddev->udev = udev;
|
||||||
ddev->desc.usb = udev;
|
|
||||||
ddev->driver_data = usb_id->driver_data;
|
ddev->driver_data = usb_id->driver_data;
|
||||||
ddev->devtype = devtype;
|
ddev->devtype = devtype;
|
||||||
return ddev;
|
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:
|
* 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)
|
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);
|
g_return_val_if_fail (registered_drivers != NULL, NULL);
|
||||||
|
|
||||||
found_devices = g_ptr_array_new ();
|
r = libusb_get_device_list(fpi_usb_ctx, &devs);
|
||||||
|
if (r < 0) {
|
||||||
discover_usb_devs (found_devices);
|
fp_err("couldn't enumerate USB devices, error %d", r);
|
||||||
discover_virtual_devs (found_devices);
|
|
||||||
|
|
||||||
/* Return NULL if no devices were found. */
|
|
||||||
if (found_devices->len == 0) {
|
|
||||||
g_ptr_array_free (found_devices, TRUE);
|
|
||||||
return NULL;
|
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
|
/* Convert our temporary array into a standard NULL-terminated pointer
|
||||||
* array. */
|
* array. */
|
||||||
g_ptr_array_add (found_devices, NULL);
|
g_ptr_array_add (tmparray, NULL);
|
||||||
return (struct fp_dscv_dev **) g_ptr_array_free (found_devices, FALSE);
|
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;
|
return;
|
||||||
|
|
||||||
for (i = 0; devs[i]; i++) {
|
for (i = 0; devs[i]; i++) {
|
||||||
switch (devs[i]->bus) {
|
libusb_unref_device(devs[i]->udev);
|
||||||
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;
|
|
||||||
}
|
|
||||||
g_free(devs[i]);
|
g_free(devs[i]);
|
||||||
}
|
}
|
||||||
g_free(devs);
|
g_free(devs);
|
||||||
@@ -757,23 +703,6 @@ API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
|
|||||||
return dev->drv->identify_start != NULL;
|
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:
|
* fp_dev_get_img_width:
|
||||||
* @dev: the struct #fp_dev device
|
* @dev: the struct #fp_dev device
|
||||||
|
|||||||
@@ -67,37 +67,16 @@ enum fp_driver_type {
|
|||||||
DRIVER_IMAGING = 1,
|
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 {
|
struct fp_driver {
|
||||||
const uint16_t id;
|
const uint16_t id;
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *full_name;
|
const char *full_name;
|
||||||
|
const struct usb_id * const id_table;
|
||||||
enum fp_bus_type bus;
|
|
||||||
union {
|
|
||||||
const struct usb_id * const usb;
|
|
||||||
const char * const *i2c;
|
|
||||||
const char * virtual_envvar;
|
|
||||||
} id_table;
|
|
||||||
|
|
||||||
enum fp_driver_type type;
|
enum fp_driver_type type;
|
||||||
enum fp_scan_type scan_type;
|
enum fp_scan_type scan_type;
|
||||||
|
|
||||||
/* Device operations */
|
/* 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);
|
int (*open)(struct fp_dev *dev, unsigned long driver_data);
|
||||||
void (*close)(struct fp_dev *dev);
|
void (*close)(struct fp_dev *dev);
|
||||||
int (*enroll_start)(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 (*identify_stop)(struct fp_dev *dev, gboolean iterating);
|
||||||
int (*capture_start)(struct fp_dev *dev);
|
int (*capture_start)(struct fp_dev *dev);
|
||||||
int (*capture_stop)(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 */
|
/* Device operations */
|
||||||
int (*open)(struct fp_img_dev *dev, unsigned long driver_data);
|
int (*open)(struct fp_img_dev *dev, unsigned long driver_data);
|
||||||
void (*close)(struct fp_img_dev *dev);
|
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);
|
int (*change_state)(struct fp_img_dev *dev, enum fp_imgdev_state state);
|
||||||
void (*deactivate)(struct fp_img_dev *dev);
|
void (*deactivate)(struct fp_img_dev *dev);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
|
|||||||
item = list_item->data;
|
item = list_item->data;
|
||||||
out_item = (struct fpi_print_data_item_fp2 *)buf;
|
out_item = (struct fpi_print_data_item_fp2 *)buf;
|
||||||
out_item->length = GUINT32_TO_LE(item->length);
|
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);
|
memcpy(out_item->data, item->data, item->length);
|
||||||
buf += sizeof(*out_item);
|
buf += sizeof(*out_item);
|
||||||
buf += item->length;
|
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),
|
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
|
||||||
GUINT32_FROM_LE(raw->devtype), raw->data_type);
|
GUINT32_FROM_LE(raw->devtype), raw->data_type);
|
||||||
item = fpi_print_data_item_new(print_data_len);
|
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);
|
memcpy(item->data, raw->data, print_data_len);
|
||||||
data->prints = g_slist_prepend(data->prints, item);
|
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;
|
total_data_len -= item_len;
|
||||||
|
|
||||||
item = fpi_print_data_item_new(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);
|
memcpy(item->data, raw_item->data, item_len);
|
||||||
data->prints = g_slist_prepend(data->prints, item);
|
data->prints = g_slist_prepend(data->prints, item);
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
|
||||||
{
|
{
|
||||||
|
enum fp_imgdev_action action;
|
||||||
|
|
||||||
G_DEBUG_HERE();
|
G_DEBUG_HERE();
|
||||||
|
|
||||||
switch (imgdev->action) {
|
action = imgdev->action;
|
||||||
|
imgdev->action = IMG_ACTION_NONE;
|
||||||
|
imgdev->action_state = 0;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
case IMG_ACTION_ENROLL:
|
case IMG_ACTION_ENROLL:
|
||||||
fpi_drvcb_enroll_stopped(FP_DEV(imgdev));
|
fpi_drvcb_enroll_stopped(FP_DEV(imgdev));
|
||||||
break;
|
break;
|
||||||
@@ -504,9 +510,6 @@ void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
|
|||||||
fp_err("unhandled action %d", imgdev->action);
|
fp_err("unhandled action %d", imgdev->action);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
imgdev->action = IMG_ACTION_NONE;
|
|
||||||
imgdev->action_state = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev)
|
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;
|
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_driver *drv = FP_DEV(imgdev)->drv;
|
||||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
|
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
|
||||||
|
|
||||||
if (!imgdrv->activate)
|
if (!imgdrv->activate)
|
||||||
return 0;
|
return 0;
|
||||||
return imgdrv->activate(imgdev, state);
|
return imgdrv->activate(imgdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dev_deactivate(struct fp_img_dev *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->action_state = IMG_ACQUIRE_STATE_ACTIVATING;
|
||||||
imgdev->enroll_stage = 0;
|
imgdev->enroll_stage = 0;
|
||||||
|
|
||||||
r = dev_activate(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
|
r = dev_activate(imgdev);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
fp_err("activation failed with error %d", r);
|
fp_err("activation failed with error %d", r);
|
||||||
|
|
||||||
|
|||||||
@@ -114,34 +114,7 @@ FP_INSTANCE_DATA (struct fp_dev *dev)
|
|||||||
libusb_device_handle *
|
libusb_device_handle *
|
||||||
fpi_dev_get_usb_dev(struct fp_dev *dev)
|
fpi_dev_get_usb_dev(struct fp_dev *dev)
|
||||||
{
|
{
|
||||||
g_assert (dev->bus == BUS_TYPE_USB);
|
return dev->udev;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -175,17 +148,3 @@ fpi_dev_get_verify_data(struct fp_dev *dev)
|
|||||||
{
|
{
|
||||||
return dev->verify_data;
|
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -40,11 +40,8 @@ void fp_dev_set_instance_data (struct fp_dev *dev,
|
|||||||
void *FP_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);
|
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,
|
void fpi_dev_set_nr_enroll_stages(struct fp_dev *dev,
|
||||||
int nr_enroll_stages);
|
int nr_enroll_stages);
|
||||||
struct fp_print_data *fpi_dev_get_verify_data(struct fp_dev *dev);
|
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
|
#endif
|
||||||
|
|||||||
@@ -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. */
|
* be good to make this dynamic. */
|
||||||
print = fpi_print_data_new(FP_DEV(imgdev));
|
print = fpi_print_data_new(FP_DEV(imgdev));
|
||||||
item = fpi_print_data_item_new(sizeof(struct xyt_struct));
|
item = fpi_print_data_item_new(sizeof(struct xyt_struct));
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib-unix.h>
|
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,38 +75,64 @@
|
|||||||
* for example.
|
* 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;
|
static GSList *active_timers = NULL;
|
||||||
|
|
||||||
/* notifiers for added or removed poll fds */
|
/* notifiers for added or removed poll fds */
|
||||||
static fp_pollfd_added_cb fd_added_cb = NULL;
|
static fp_pollfd_added_cb fd_added_cb = NULL;
|
||||||
static fp_pollfd_removed_cb fd_removed_cb = NULL;
|
static fp_pollfd_removed_cb fd_removed_cb = NULL;
|
||||||
|
|
||||||
|
|
||||||
struct fpi_timeout {
|
struct fpi_timeout {
|
||||||
|
struct timeval expiry;
|
||||||
fpi_timeout_fn callback;
|
fpi_timeout_fn callback;
|
||||||
struct fp_dev *dev;
|
struct fp_dev *dev;
|
||||||
void *data;
|
void *data;
|
||||||
GSource *source;
|
char *name;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static int timeout_sort_fn(gconstpointer _a, gconstpointer _b)
|
||||||
fpi_timeout_destroy (gpointer data)
|
|
||||||
{
|
{
|
||||||
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);
|
if (timercmp(tv_a, tv_b, <))
|
||||||
|
return -1;
|
||||||
|
else if (timercmp(tv_a, tv_b, >))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fpi_timeout_free(fpi_timeout *timeout)
|
||||||
|
{
|
||||||
|
if (timeout == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_free(timeout->name);
|
||||||
g_free(timeout);
|
g_free(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
/**
|
||||||
fpi_timeout_wrapper_cb (gpointer data)
|
* 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)
|
||||||
{
|
{
|
||||||
fpi_timeout *timeout = (fpi_timeout*) data;
|
g_return_if_fail (timeout != NULL);
|
||||||
|
g_return_if_fail (name != NULL);
|
||||||
|
g_return_if_fail (timeout->name == NULL);
|
||||||
|
|
||||||
timeout->callback (timeout->dev, timeout->data);
|
timeout->name = g_strdup(name);
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,37 +154,43 @@ fpi_timeout_wrapper_cb (gpointer data)
|
|||||||
*
|
*
|
||||||
* Returns: an #fpi_timeout structure
|
* Returns: an #fpi_timeout structure
|
||||||
*/
|
*/
|
||||||
fpi_timeout *
|
fpi_timeout *fpi_timeout_add(unsigned int msec,
|
||||||
fpi_timeout_add(unsigned int msec,
|
|
||||||
fpi_timeout_fn callback,
|
fpi_timeout_fn callback,
|
||||||
struct fp_dev *dev,
|
struct fp_dev *dev,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
struct timeval add_msec;
|
||||||
fpi_timeout *timeout;
|
fpi_timeout *timeout;
|
||||||
|
int r;
|
||||||
|
|
||||||
timeout = g_new0 (fpi_timeout, 1);
|
g_return_val_if_fail (dev != NULL, NULL);
|
||||||
timeout->source = g_timeout_source_new (msec);
|
|
||||||
active_timers = g_slist_prepend (active_timers, timeout);
|
|
||||||
|
|
||||||
g_source_set_callback (timeout->source, fpi_timeout_wrapper_cb, timeout, fpi_timeout_destroy);
|
fp_dbg("in %dms", msec);
|
||||||
g_source_attach (timeout->source, fpi_main_ctx);
|
|
||||||
|
|
||||||
return timeout;
|
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);
|
||||||
* fpi_timeout_set_name:
|
timeout->callback = callback;
|
||||||
* @timeout: a #fpi_timeout
|
timeout->dev = dev;
|
||||||
* @name: the name to give the timeout
|
timeout->data = data;
|
||||||
*
|
TIMESPEC_TO_TIMEVAL(&timeout->expiry, &ts);
|
||||||
* Sets a name for a timeout, allowing that name to be printed
|
|
||||||
* along with any timeout related debug.
|
/* calculate timeout expiry by adding delay to current monotonic clock */
|
||||||
*/
|
timerclear(&add_msec);
|
||||||
void
|
add_msec.tv_sec = msec / 1000;
|
||||||
fpi_timeout_set_name(fpi_timeout *timeout,
|
add_msec.tv_usec = (msec % 1000) * 1000;
|
||||||
const char *name)
|
timeradd(&timeout->expiry, &add_msec, &timeout->expiry);
|
||||||
{
|
|
||||||
g_source_set_name (timeout->source, name);
|
active_timers = g_slist_insert_sorted(active_timers, timeout,
|
||||||
|
timeout_sort_fn);
|
||||||
|
|
||||||
|
return timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,110 +200,81 @@ fpi_timeout_set_name(fpi_timeout *timeout,
|
|||||||
* Cancels a timeout scheduled with fpi_timeout_add(), and frees the
|
* Cancels a timeout scheduled with fpi_timeout_add(), and frees the
|
||||||
* @timeout structure.
|
* @timeout structure.
|
||||||
*/
|
*/
|
||||||
void
|
void fpi_timeout_cancel(fpi_timeout *timeout)
|
||||||
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 {
|
/* get the expiry time and optionally the timeout structure for the next
|
||||||
fpi_io_condition_fn callback;
|
* timeout. returns 0 if there are no expired timers, or 1 if the
|
||||||
int fd;
|
* timeval/timeout output parameters were populated. if the returned timeval
|
||||||
struct fp_dev *dev;
|
* is zero then it means the timeout has already expired and should be handled
|
||||||
void *data;
|
* ASAP. */
|
||||||
GSource *source;
|
static int get_next_timeout_expiry(struct timeval *out,
|
||||||
};
|
struct fpi_timeout **out_timeout)
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fpi_io_condition_wrapper_cb (int fd, GIOCondition cond, gpointer data)
|
|
||||||
{
|
{
|
||||||
fpi_io_condition *io_cond = data;
|
struct timespec ts;
|
||||||
short events = 0;
|
struct timeval tv;
|
||||||
|
struct fpi_timeout *next_timeout;
|
||||||
|
int r;
|
||||||
|
|
||||||
if (cond & G_IO_IN)
|
if (active_timers == NULL)
|
||||||
events |= POLL_IN;
|
return 0;
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
return 1;
|
||||||
fpi_io_condition_destroy (gpointer data)
|
|
||||||
{
|
|
||||||
fpi_io_condition *io_cond = data;
|
|
||||||
|
|
||||||
if (fd_removed_cb)
|
|
||||||
fd_removed_cb(io_cond->fd);
|
|
||||||
|
|
||||||
g_free (io_cond);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fpi_io_condition *
|
/* handle a timeout that has expired */
|
||||||
fpi_io_condition_add(int fd,
|
static void handle_timeout(struct fpi_timeout *timeout)
|
||||||
short int events,
|
|
||||||
fpi_io_condition_fn callback,
|
|
||||||
struct fp_dev *dev,
|
|
||||||
void *data)
|
|
||||||
{
|
{
|
||||||
fpi_io_condition *io_cond;
|
G_DEBUG_HERE();
|
||||||
GIOCondition cond = 0;
|
timeout->callback(timeout->dev, timeout->data);
|
||||||
|
active_timers = g_slist_remove(active_timers, timeout);
|
||||||
if (events & POLL_IN)
|
fpi_timeout_free(timeout);
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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
|
static int handle_timeouts(void)
|
||||||
fpi_io_condition_set_name(fpi_io_condition *io_cond,
|
|
||||||
const char *name)
|
|
||||||
{
|
{
|
||||||
g_source_set_name (io_cond->source, name);
|
struct timeval next_timeout_expiry;
|
||||||
}
|
struct fpi_timeout *next_timeout;
|
||||||
|
int r;
|
||||||
|
|
||||||
void
|
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
|
||||||
fpi_io_condition_remove(fpi_io_condition *io_cond)
|
if (r <= 0)
|
||||||
{
|
return r;
|
||||||
g_source_destroy(io_cond->source);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
if (!timerisset(&next_timeout_expiry))
|
||||||
dummy_cb (gpointer user_data)
|
handle_timeout(next_timeout);
|
||||||
{
|
|
||||||
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)
|
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) {
|
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
|
||||||
g_main_context_iteration (fpi_main_ctx, FALSE);
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
/* timer already expired? */
|
||||||
|
if (!timerisset(&next_timeout_expiry)) {
|
||||||
|
handle_timeout(next_timeout);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register a timeout on the mainloop and then run in blocking mode */
|
/* choose the smallest of next URB timeout or user specified timeout */
|
||||||
timeout_source = g_timeout_source_new (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
|
if (timercmp(&next_timeout_expiry, timeout, <))
|
||||||
g_source_set_name (timeout_source, "fpi poll timeout");
|
select_timeout = next_timeout_expiry;
|
||||||
g_source_set_callback (timeout_source, dummy_cb, NULL, NULL);
|
else
|
||||||
g_source_attach (timeout_source, fpi_main_ctx);
|
select_timeout = *timeout;
|
||||||
g_main_context_iteration (fpi_main_ctx, TRUE);
|
} else {
|
||||||
g_source_destroy (timeout_source);
|
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)
|
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,
|
/* if we have no pending timeouts and the same is true for libusb,
|
||||||
G_MININT,
|
* indicate that we have no pending timouts */
|
||||||
&timeout_,
|
if (r_fprint <= 0 && r_libusb <= 0)
|
||||||
NULL,
|
|
||||||
0);
|
|
||||||
|
|
||||||
if (timeout_ < 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
tv->tv_sec = timeout_ / 1000;
|
/* if fprint have no pending timeouts return libusb timeout */
|
||||||
tv->tv_usec = (timeout_ % 1000) * 1000;
|
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;
|
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:
|
* fp_get_pollfds:
|
||||||
* @pollfds: output location for a list of pollfds. If non-%NULL, must be
|
* @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)
|
API_EXPORTED ssize_t fp_get_pollfds(struct fp_pollfd **pollfds)
|
||||||
{
|
{
|
||||||
gint timeout_;
|
const struct libusb_pollfd **usbfds;
|
||||||
GPollFD fds_static[16];
|
const struct libusb_pollfd *usbfd;
|
||||||
GPollFD *fds = fds_static;
|
struct fp_pollfd *ret;
|
||||||
gint n_fds;
|
ssize_t cnt = 0;
|
||||||
int i;
|
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,
|
usbfds = libusb_get_pollfds(fpi_usb_ctx);
|
||||||
G_MININT,
|
if (!usbfds) {
|
||||||
&timeout_,
|
*pollfds = NULL;
|
||||||
fds,
|
return -EIO;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_main_context_release (fpi_main_ctx);
|
while ((usbfd = usbfds[i++]) != NULL)
|
||||||
|
cnt++;
|
||||||
|
|
||||||
*pollfds = g_new0 (struct fp_pollfd, n_fds);
|
ret = g_malloc(sizeof(struct fp_pollfd) * cnt);
|
||||||
for (i = 0; i < n_fds; i++) {
|
i = 0;
|
||||||
(*pollfds)[i].fd = fds[i].fd;
|
while ((usbfd = usbfds[i]) != NULL) {
|
||||||
|
ret[i].fd = usbfd->fd;
|
||||||
if (fds[i].events & G_IO_IN)
|
ret[i].events = usbfd->events;
|
||||||
(*pollfds)[i].events |= POLL_IN;
|
i++;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds != fds_static)
|
*pollfds = ret;
|
||||||
g_free (fds);
|
return cnt;
|
||||||
|
|
||||||
return n_fds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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)
|
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)
|
if (fd_added_cb)
|
||||||
fd_added_cb(fd, events);
|
fd_added_cb(fd, events);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_pollfd(int fd, void *user_data)
|
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)
|
if (fd_removed_cb)
|
||||||
fd_removed_cb(fd);
|
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)
|
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);
|
libusb_set_pollfd_notifiers(fpi_usb_ctx, add_pollfd, remove_pollfd, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fpi_poll_exit(void)
|
void fpi_poll_exit(void)
|
||||||
{
|
{
|
||||||
g_source_destroy (&libusb_source->source);
|
g_slist_free_full(active_timers, (GDestroyNotify) fpi_timeout_free);
|
||||||
libusb_source = NULL;
|
active_timers = NULL;
|
||||||
g_main_context_unref (fpi_main_ctx);
|
|
||||||
fpi_main_ctx = NULL;
|
|
||||||
|
|
||||||
fd_added_cb = NULL;
|
fd_added_cb = NULL;
|
||||||
fd_removed_cb = NULL;
|
fd_removed_cb = NULL;
|
||||||
|
|
||||||
libusb_set_pollfd_notifiers(fpi_usb_ctx, NULL, NULL, NULL);
|
libusb_set_pollfd_notifiers(fpi_usb_ctx, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
fpi_timeout_cancel_all_for_dev(struct fp_dev *dev)
|
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;
|
l = active_timers;
|
||||||
while (l) {
|
while (l) {
|
||||||
fpi_timeout *cb_data = l->data;
|
struct fpi_timeout *timeout = l->data;
|
||||||
|
GSList *current = l;
|
||||||
|
|
||||||
l = l->next;
|
l = l->next;
|
||||||
if (cb_data->dev == dev)
|
if (timeout->dev == dev) {
|
||||||
g_source_destroy (cb_data->source);
|
g_free (timeout);
|
||||||
|
active_timers = g_slist_delete_link (active_timers, current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,34 +48,4 @@ void fpi_timeout_set_name(fpi_timeout *timeout,
|
|||||||
const char *name);
|
const char *name);
|
||||||
void fpi_timeout_cancel(fpi_timeout *timeout);
|
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
|
#endif
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ static void enroll_stop_cb(struct fp_dev *dev, void *user_data)
|
|||||||
* or I/O problems.
|
* or I/O problems.
|
||||||
*
|
*
|
||||||
* The RETRY codes from #fp_enroll_result may be returned from any enroll
|
* 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
|
* 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
|
* 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
|
* 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);
|
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 {
|
struct sync_identify_data {
|
||||||
gboolean populated;
|
gboolean populated;
|
||||||
int result;
|
int result;
|
||||||
|
|||||||
@@ -31,11 +31,10 @@ static GList *insert_driver (GList *list,
|
|||||||
{
|
{
|
||||||
int i;
|
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;
|
char *key;
|
||||||
|
|
||||||
key = g_strdup_printf ("%04x:%04x", driver->id_table.usb[i].vendor,
|
key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product);
|
||||||
driver->id_table.usb[i].product);
|
|
||||||
|
|
||||||
if (g_hash_table_lookup (printed, key) != NULL) {
|
if (g_hash_table_lookup (printed, key) != NULL) {
|
||||||
g_free (key);
|
g_free (key);
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ static const struct usb_id blacklist_id_table[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct fp_driver whitelist = {
|
struct fp_driver whitelist = {
|
||||||
.bus = BUS_TYPE_USB,
|
.id_table = whitelist_id_table,
|
||||||
.id_table.usb = whitelist_id_table,
|
|
||||||
.full_name = "Hardcoded whitelist"
|
.full_name = "Hardcoded whitelist"
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,13 +52,13 @@ static void print_driver (struct fp_driver *driver)
|
|||||||
|
|
||||||
num_printed = 0;
|
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;
|
char *key;
|
||||||
|
|
||||||
blacklist = 0;
|
blacklist = 0;
|
||||||
for (j = 0; blacklist_id_table[j].vendor != 0; j++) {
|
for (j = 0; blacklist_id_table[j].vendor != 0; j++) {
|
||||||
if (driver->id_table.usb[i].vendor == blacklist_id_table[j].vendor &&
|
if (driver->id_table[i].vendor == blacklist_id_table[j].vendor &&
|
||||||
driver->id_table.usb[i].product == blacklist_id_table[j].product) {
|
driver->id_table[i].product == blacklist_id_table[j].product) {
|
||||||
blacklist = 1;
|
blacklist = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -67,8 +66,7 @@ static void print_driver (struct fp_driver *driver)
|
|||||||
if (blacklist)
|
if (blacklist)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
key = g_strdup_printf ("%04x:%04x", driver->id_table.usb[i].vendor,
|
key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product);
|
||||||
driver->id_table.usb[i].product);
|
|
||||||
|
|
||||||
if (g_hash_table_lookup (printed, key) != NULL) {
|
if (g_hash_table_lookup (printed, key) != NULL) {
|
||||||
g_free (key);
|
g_free (key);
|
||||||
@@ -80,12 +78,8 @@ static void print_driver (struct fp_driver *driver)
|
|||||||
if (num_printed == 0)
|
if (num_printed == 0)
|
||||||
printf ("# %s\n", driver->full_name);
|
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",
|
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);
|
||||||
driver->id_table.usb[i].vendor,
|
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);
|
||||||
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);
|
|
||||||
num_printed++;
|
num_printed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -267,17 +267,12 @@ int fp_verify_finger(struct fp_dev *dev,
|
|||||||
struct fp_print_data *enrolled_print);
|
struct fp_print_data *enrolled_print);
|
||||||
|
|
||||||
int fp_dev_supports_identification(struct fp_dev *dev);
|
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,
|
int fp_identify_finger_img(struct fp_dev *dev,
|
||||||
struct fp_print_data **print_gallery, size_t *match_offset,
|
struct fp_print_data **print_gallery, size_t *match_offset,
|
||||||
struct fp_img **img);
|
struct fp_img **img);
|
||||||
int fp_identify_finger(struct fp_dev *dev,
|
int fp_identify_finger(struct fp_dev *dev,
|
||||||
struct fp_print_data **print_gallery, size_t *match_offset);
|
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 */
|
/* Data handling */
|
||||||
int fp_print_data_load(struct fp_dev *dev, enum fp_finger finger,
|
int fp_print_data_load(struct fp_dev *dev, enum fp_finger finger,
|
||||||
struct fp_print_data **data) LIBFPRINT_DEPRECATED;
|
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);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -97,9 +97,6 @@ foreach driver: drivers
|
|||||||
aeslib = true
|
aeslib = true
|
||||||
aes3k = true
|
aes3k = true
|
||||||
endif
|
endif
|
||||||
if driver == 'fdu2000'
|
|
||||||
drivers_sources += [ 'drivers/fdu2000.c' ]
|
|
||||||
endif
|
|
||||||
if driver == 'vcom5s'
|
if driver == 'vcom5s'
|
||||||
drivers_sources += [ 'drivers/vcom5s.c' ]
|
drivers_sources += [ 'drivers/vcom5s.c' ]
|
||||||
endif
|
endif
|
||||||
@@ -124,12 +121,6 @@ foreach driver: drivers
|
|||||||
if driver == 'elan'
|
if driver == 'elan'
|
||||||
drivers_sources += [ 'drivers/elan.c' ]
|
drivers_sources += [ 'drivers/elan.c' ]
|
||||||
endif
|
endif
|
||||||
if driver == 'virtual_imgdev'
|
|
||||||
drivers_sources += [ 'drivers/virtual_imgdev.c' ]
|
|
||||||
endif
|
|
||||||
if driver == 'virtual_misdev'
|
|
||||||
drivers_sources += [ 'drivers/virtual_misdev.c' ]
|
|
||||||
endif
|
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
if aeslib
|
if aeslib
|
||||||
|
|||||||
12
meson.build
@@ -1,5 +1,5 @@
|
|||||||
project('libfprint', [ 'c', 'cpp' ],
|
project('libfprint', [ 'c', 'cpp' ],
|
||||||
version: '0.99.0',
|
version: '1.0',
|
||||||
license: 'LGPLv2.1+',
|
license: 'LGPLv2.1+',
|
||||||
default_options: [
|
default_options: [
|
||||||
'buildtype=debugoptimized',
|
'buildtype=debugoptimized',
|
||||||
@@ -27,7 +27,9 @@ common_cflags = cc.get_supported_arguments([
|
|||||||
'-Wstrict-prototypes',
|
'-Wstrict-prototypes',
|
||||||
'-Werror-implicit-function-declaration',
|
'-Werror-implicit-function-declaration',
|
||||||
'-Wno-pointer-sign',
|
'-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
|
# maintaining compatibility with the previous libtool versioning
|
||||||
@@ -39,14 +41,14 @@ revision = 0
|
|||||||
libversion = '@0@.@1@.@2@'.format(soversion, current, revision)
|
libversion = '@0@.@1@.@2@'.format(soversion, current, revision)
|
||||||
|
|
||||||
# Dependencies
|
# 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')
|
libusb_dep = dependency('libusb-1.0', version: '>= 0.9.1')
|
||||||
mathlib_dep = cc.find_library('m', required: false)
|
mathlib_dep = cc.find_library('m', required: false)
|
||||||
|
|
||||||
# Drivers
|
# Drivers
|
||||||
drivers = get_option('drivers').split(',')
|
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' ]
|
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', 'virtual_misdev' ]
|
primitive_drivers = [ 'upekts' ]
|
||||||
|
|
||||||
if drivers == [ 'all' ]
|
if drivers == [ 'all' ]
|
||||||
drivers = all_drivers
|
drivers = all_drivers
|
||||||
|
|||||||