Compare commits

...

No commits in common. "openkylin/yangtze" and "pristine-tar" have entirely different histories.

172 changed files with 1 additions and 66978 deletions

View File

@ -1,113 +0,0 @@
#!/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!')

11
AUTHORS
View File

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

504
COPYING
View File

@ -1,504 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

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

View File

@ -1,6 +0,0 @@
libfprint uses the Meson build system. See
http://mesonbuild.com/Quick-guide.html for details on how to
compile libfprint.
The "meson configure" command will give you a list of available
command-line options.

207
NEWS
View File

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

51
README
View File

@ -1,51 +0,0 @@
libfprint
=========
libfprint is part of the fprint project:
https://fprint.freedesktop.org/
libfprint was originally developed as part of an academic project at the
University of Manchester with the aim of hiding differences between different
consumer fingerprint scanners and providing a single uniform API to application
developers. The ultimate goal of the fprint project is to make fingerprint
scanners widely and easily usable under common Linux environments.
The academic university project runs off a codebase maintained separately
from this one, although I try to keep them as similar as possible (I'm not
hiding anything in the academic branch, it's just the open source release
contains some commits excluded from the academic project).
THE UNIVERSITY OF MANCHESTER DOES NOT ENDORSE THIS THIS SOFTWARE RELEASE AND
IS IN NO WAY RESPONSIBLE FOR THE CODE CONTAINED WITHIN, OR ANY DAMAGES CAUSED
BY USING OR DISTRIBUTING THE SOFTWARE. Development does not happen on
university computers and the project is not hosted at the university either.
For more information on libfprint, supported devices, API documentation, etc.,
see the homepage:
https://fprint.freedesktop.org/
libfprint is licensed under the GNU LGPL version 2.1. See the COPYING file
for the license text.
Section 6 of the license states that for compiled works that use this
library, such works must include libfprint copyright notices alongside the
copyright notices for the other parts of the work. We have attempted to
make this process slightly easier for you by grouping these all in one place:
the AUTHORS file.
libfprint includes code from NIST's NBIS software distribution:
http://fingerprint.nist.gov/NBIS/index.html
We include bozorth3 from the US export controlled distribution. We have
determined that it is fine to ship bozorth3 in an open source project,
see https://fprint.freedesktop.org/us-export-control.html
## Historical links
Older versions of libfprint are available at:
https://sourceforge.net/projects/fprint/files/
Historical mailing-list archives:
http://www.reactivated.net/fprint_list_archives/
Historical website:
http://web.archive.org/web/*/https://www.freedesktop.org/wiki/Software/fprint/

11
THANKS
View File

@ -1,11 +0,0 @@
Tony Vroon - hardware donations
Gerrie Mansur from Security Database BV (http://www.securitydatabase.net/) - hardware donations
Joaquin Custodio - hardware donations
TimeTrex (http://www.timetrex.com/) - hardware donations
Craig Watson (NIST)
James Vasile (SFLC)
Toby Howard (University of Manchester)
Seemant Kulleen
Pavel Herrman
Bastien Nocera
Greg Kerr and Martin Konecny from AuthenTec Inc - hardware donations (AES2550 device), datasheets for AES2550 and AES2810

30
TODO
View File

@ -1,30 +0,0 @@
LIBRARY
=======
test suite against NFIQ compliance set
make library optionally asynchronous and maybe thread-safe
nbis cleanups
API function to determine if img device supports uncond. capture
race-free way of saying "save this print but don't overwrite"
NEW DRIVERS
===========
Sunplus 895 driver
AES3400/3500 driver
ID Mouse driver
Support for 2nd generation MS devices
Support for 2nd generation UPEK devices
IMAGING
=======
ignore first frame or two with aes2501
aes2501: increase threshold "sum" for end-of-image detection
aes2501 gain calibration
aes4000 gain calibration
aes4000 resampling
PPMM parameter to get_minutiae seems to have no effect
nbis minutiae should be stored in endian-independent format
PORTABILITY
===========
OpenBSD can't do -Wshadow or visibility
OpenBSD: add compat codes for ENOTSUP ENODATA and EPROTO

View File

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

19
debian/README.source vendored
View File

@ -1,19 +0,0 @@
The libfprint0 postinst script calls udevadmin trigger for all the supported
USB readers. The list of readers used in that file is generated by calling the
following awk script:
/plugdev/ {
printf ("\tudevadm trigger --action=add " )
for (i=1;i<=NF; i++){
if (match($i,/idVendor/)>0) {
match($i, /"[^"]*"/); printf ("--attr-match=idVendor=%s ",substr($i,RSTART+1,RLENGTH-2))
}
if (match($i,/idProduct/)>0) {
match($i, /"[^"]*"/); printf ("--attr-match=idProduct=%s",substr($i,RSTART+1,RLENGTH-2))
}
};
printf("\n")
}
When preparing a new upstream release, this script should be called against the
udev .rules file generated during the build of the package

5
debian/changelog vendored
View File

@ -1,5 +0,0 @@
libfprint (1:1.0-ok1) yangtze; urgency=medium
* build for openKylin.
-- openKylinBot <openKylinBot@openkylin.top> Tue, 07 Jun 2022 14:13:26 +0800

1
debian/compat vendored
View File

@ -1 +0,0 @@
10

83
debian/control vendored
View File

@ -1,83 +0,0 @@
Source: libfprint
Priority: optional
Section: libs
Maintainer: FingerForce Team <fingerforce-devel@lists.alioth.debian.org>
Uploaders: Ulises Vitulli <dererk@debian.org>, Didier Raboud <odyx@debian.org>
Build-Depends: debhelper (>= 10.3),
gtk-doc-tools,
libglib2.0-dev (>= 2.50),
libnss3-dev,
libpixman-1-dev,
libusb-1.0-0-dev,
libxv-dev,
meson
Build-Depends-Indep: libglib2.0-doc
Standards-Version: 4.4.0
Homepage: https://www.freedesktop.org/wiki/Software/fprint/libfprint
Vcs-Browser: https://salsa.debian.org/debian/libfprint
Vcs-Git: https://salsa.debian.org/debian/libfprint.git
Package: libfprint-dev
Section: libdevel
Architecture: any
Depends: libfprint0 (= ${binary:Version}), ${misc:Depends}
Suggests: libfprint-doc
Description: async fingerprint library of fprint project, development headers
The fprint project aims to support for consumer fingerprint reader devices.
.
Previously, Linux support for such devices has been scattered amongst different
projects (many incomplete) and inconsistent in that application developers
would have to implement support for each type of fingerprint reader separately.
The idea is to change that by providing a central system to support all the
fingerprint readers as it's possible to get hands on.
.
libfprint is the centre of efforts, this component does the dirty job of
talking to fingerprint reading devices, and processing fingerprint data.
.
This library depends on the new libusb, which performs asynchronous callbacks,
allowing fprint to perform non-blocking device tasks.
.
This package provides development headers.
Package: libfprint0
Architecture: any
Multi-Arch: same
Depends: ${misc:Depends}, ${shlibs:Depends}
Pre-Depends: ${misc:Pre-Depends}
Description: async fingerprint library of fprint project, shared libraries
The fprint project aims to support for consumer fingerprint reader devices.
.
Previously, Linux support for such devices has been scattered amongst different
projects (many incomplete) and inconsistent in that application developers
would have to implement support for each type of fingerprint reader separately.
The idea is to change that by providing a central system to support all the
fingerprint readers as it's possible to get hands on.
.
libfprint is the centre of efforts, this component does the dirty job of
talking to fingerprint reading devices, and processing fingerprint data.
.
This library depends on the new libusb, which performs asynchronous callbacks,
allowing fprint to perform non-blocking device tasks.
.
This package provides shared libraries.
Package: libfprint-doc
Section: doc
Architecture: all
Depends: ${misc:Depends}
Description: async fingerprint library of fprint project, API documentation
The fprint project aims to support for consumer fingerprint reader devices.
.
Previously, Linux support for such devices has been scattered amongst different
projects (many incomplete) and inconsistent in that application developers
would have to implement support for each type of fingerprint reader separately.
The idea is to change that by providing a central system to support all the
fingerprint readers as it's possible to get hands on.
.
libfprint is the centre of efforts, this component does the dirty job of
talking to fingerprint reading devices, and processing fingerprint data.
.
This library depends on the new libusb, which performs asynchronous callbacks,
allowing fprint to perform non-blocking device tasks.
.
This package provides the API documentation.

45
debian/copyright vendored
View File

@ -1,45 +0,0 @@
This package was debianized by Ulises Vitulli <uvitulli@fi.uba.ar> on
Fri, 19 Sep 2008 05:14:30 -0300.
It was downloaded from <http://reactivated.net/fprint/wiki/Libfprint>
Upstream Authors, and Copyright:
Copyright © 2007 Daniel Drake <dsd@gentoo.org>
Copyright © 2006-2007 Timo Hoenig <thoenig@suse.de>
Copyright © 2006 Pavel Machek <pavel@suse.cz>
Copyright © 1999 Erik Walthinsen <omega@cse.ogi.edu>
Copyright © 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
Copyright © 2007 Cyrille Bagard
Copyright © 2007 Vasily Khoruzhick
Copyright © 2007 Jan-Michael Brummer <buzz2@gmx.de>
Copyright © 2007 Anthony Bretaudeau <wxcover@users.sourceforge.net>
License:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
The files under libfprint/nbis directory has this license:
This software was developed at the National Institute of Standards and
Technology (NIST) by employees of the Federal Government in the course
of their official duties. Pursuant to title 17 Section 105 of the
United States Code, this software is not subject to copyright protection
and is in the public domain. NIST assumes no responsibility whatsoever for
its use by other parties, and makes no guarantees, expressed or implied,
about its quality, reliability, or any other characteristic.
The Debian packaging is © 2008, Ulises Vitulli <uvitulli@fi.uba.ar> and is
licensed under the LGPL v2.1, see `/usr/share/common-licenses/LGPL-2.1'.

3
debian/docs vendored
View File

@ -1,3 +0,0 @@
NEWS
README
TODO

4
debian/gbp.conf vendored
View File

@ -1,4 +0,0 @@
[DEFAULT]
pristine-tar = True
debian-branch = debian
upstream-vcs-tag = V_%(version%.%_)s

View File

@ -1,3 +0,0 @@
usr/include/*
usr/lib/*/lib*.so
usr/lib/*/pkgconfig/*

View File

@ -1 +0,0 @@
usr/share/gtk-doc/

View File

@ -1,2 +0,0 @@
lib/udev/rules.d/
usr/lib/*/lib*.so.*

View File

@ -1,3 +0,0 @@
# For security reasons, we don't want the fingerprint readers to be accessible
# by unprivileged users
udev-rule-missing-uaccess

View File

@ -1,139 +0,0 @@
#!/bin/sh
set -e
#DEBHELPER#
if [ "$1" = "configure" -o "$1" = "upgrade" ] && command -V udevadm >/dev/null 2>&1; then
# apply udev rules at package installation, see
# <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683863#27>
# NOTA BENE: after the DEBHELPER section since dh_installudev
# adds stuff there
udevadm trigger --action=add --attr-match=idVendor=0483 --attr-match=idProduct=2016
udevadm trigger --action=add --attr-match=idVendor=0483 --attr-match=idProduct=2015
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=3001
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=2016
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=1000
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=1001
udevadm trigger --action=add --attr-match=idVendor=061a --attr-match=idProduct=0110
udevadm trigger --action=add --attr-match=idVendor=045e --attr-match=idProduct=00bb
udevadm trigger --action=add --attr-match=idVendor=045e --attr-match=idProduct=00bc
udevadm trigger --action=add --attr-match=idVendor=045e --attr-match=idProduct=00bd
udevadm trigger --action=add --attr-match=idVendor=045e --attr-match=idProduct=00ca
udevadm trigger --action=add --attr-match=idVendor=05ba --attr-match=idProduct=0007
udevadm trigger --action=add --attr-match=idVendor=05ba --attr-match=idProduct=0008
udevadm trigger --action=add --attr-match=idVendor=05ba --attr-match=idProduct=000a
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1600
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1660
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1680
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1681
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1682
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1683
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1684
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1685
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1686
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1687
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1688
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1689
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168a
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168b
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168c
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168d
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168e
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168f
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2500
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2580
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2550
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2810
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2660
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2680
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2681
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2682
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2683
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2684
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2685
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2686
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2687
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2688
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2689
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268a
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268b
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268c
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268d
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268e
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268f
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2691
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=5731
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=5501
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0001
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0005
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0008
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0010
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0011
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0015
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0017
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0018
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=2020
udevadm trigger --action=add --attr-match=idVendor=1c7a --attr-match=idProduct=0603
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0050
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0903
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0907
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c01
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c02
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c03
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c04
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c05
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c06
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c07
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c08
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c09
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0a
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0b
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0c
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0d
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0e
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0f
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c10
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c11
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c12
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c13
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c14
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c15
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c16
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c17
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c18
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c19
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1a
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1b
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1c
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1d
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1e
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1f
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c20
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c21
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c22
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c23
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c24
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c25
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c26
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c27
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c28
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c29
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2a
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2b
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2c
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2d
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2e
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2f
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c30
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c31
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c32
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c33
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c42
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0090
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0091
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0094
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0097
fi
exit 0

View File

@ -1,77 +0,0 @@
libfprint.so.0 libfprint0 #MINVER#
* Build-Depends-Package: libfprint-dev
fp_async_capture_start@Base 1:0.6.0
fp_async_capture_stop@Base 1:0.6.0
fp_async_dev_close@Base 1:0.4.0
fp_async_dev_open@Base 1:0.4.0
fp_async_enroll_start@Base 1:0.4.0
fp_async_enroll_stop@Base 1:0.4.0
fp_async_identify_start@Base 1:0.4.0
fp_async_identify_stop@Base 1:0.4.0
fp_async_verify_start@Base 1:0.4.0
fp_async_verify_stop@Base 1:0.4.0
fp_dev_close@Base 1:0.4.0
fp_dev_get_devtype@Base 1:0.4.0
fp_dev_get_driver@Base 1:0.4.0
fp_dev_get_img_height@Base 1:0.4.0
fp_dev_get_img_width@Base 1:0.4.0
fp_dev_get_nr_enroll_stages@Base 1:0.4.0
fp_dev_img_capture@Base 1:0.4.0
fp_dev_open@Base 1:0.4.0
fp_dev_supports_dscv_print@Base 1:0.4.0
fp_dev_supports_identification@Base 1:0.4.0
fp_dev_supports_imaging@Base 1:0.4.0
fp_dev_supports_print_data@Base 1:0.4.0
fp_discover_devs@Base 1:0.4.0
fp_discover_prints@Base 1:0.4.0
fp_driver_get_driver_id@Base 1:0.4.0
fp_driver_get_full_name@Base 1:0.4.0
fp_driver_get_name@Base 1:0.4.0
fp_driver_get_scan_type@Base 1:0.4.0
fp_driver_supports_imaging@Base 1:0.99.0
fp_dscv_dev_for_dscv_print@Base 1:0.4.0
fp_dscv_dev_for_print_data@Base 1:0.4.0
fp_dscv_dev_get_devtype@Base 1:0.4.0
fp_dscv_dev_get_driver@Base 1:0.4.0
fp_dscv_dev_get_driver_id@Base 1:0.8.1
fp_dscv_dev_supports_dscv_print@Base 1:0.4.0
fp_dscv_dev_supports_print_data@Base 1:0.4.0
fp_dscv_devs_free@Base 1:0.4.0
fp_dscv_print_delete@Base 1:0.4.0
fp_dscv_print_get_devtype@Base 1:0.4.0
fp_dscv_print_get_driver_id@Base 1:0.4.0
fp_dscv_print_get_finger@Base 1:0.4.0
fp_dscv_prints_free@Base 1:0.4.0
fp_enroll_finger@Base 1:0.8.1
fp_enroll_finger_img@Base 1:0.4.0
fp_exit@Base 1:0.4.0
fp_get_next_timeout@Base 1:0.4.0
fp_get_pollfds@Base 1:0.4.0
fp_handle_events@Base 1:0.4.0
fp_handle_events_timeout@Base 1:0.4.0
fp_identify_finger@Base 1:0.8.1
fp_identify_finger_img@Base 1:0.4.0
fp_img_binarize@Base 1:0.4.0
fp_img_free@Base 1:0.4.0
fp_img_get_data@Base 1:0.4.0
fp_img_get_height@Base 1:0.4.0
fp_img_get_minutiae@Base 1:0.4.0
fp_img_get_width@Base 1:0.4.0
fp_img_save_to_file@Base 1:0.4.0
fp_img_standardize@Base 1:0.4.0
fp_init@Base 1:0.4.0
fp_minutia_get_coords@Base 1:0.8.2-3~
fp_print_data_delete@Base 1:0.4.0
fp_print_data_free@Base 1:0.4.0
fp_print_data_from_data@Base 1:0.4.0
fp_print_data_from_dscv_print@Base 1:0.4.0
fp_print_data_get_data@Base 1:0.4.0
fp_print_data_get_devtype@Base 1:0.4.0
fp_print_data_get_driver_id@Base 1:0.4.0
fp_print_data_load@Base 1:0.4.0
fp_print_data_save@Base 1:0.4.0
fp_set_debug@Base 1:0.4.0
fp_set_pollfd_notifiers@Base 1:0.4.0
fp_verify_finger@Base 1:0.8.1
fp_verify_finger_img@Base 1:0.4.0
fprint_get_drivers@Base 1:0.4.0

17
debian/rules vendored
View File

@ -1,17 +0,0 @@
#!/usr/bin/make -f
# Configuration arguments
CONFIG_ARGS = -Dudev_rules_dir=/lib/udev/rules.d -Dgtk-examples=false
%:
dh $@
override_dh_auto_configure:
dh_auto_configure -- $(CONFIG_ARGS)
override_dh_install:
mv debian/tmp/lib/udev/rules.d/60-fprint-autosuspend.rules \
debian/tmp/lib/udev/rules.d/60-libfprint0.rules
sed -i '/^Requires.private\|^Libs.private/ d' \
debian/tmp/usr/lib/*/pkgconfig/libfprint.pc
dh_install

View File

@ -1 +0,0 @@
3.0 (native)

4
debian/watch vendored
View File

@ -1,4 +0,0 @@
version=4
https://gitlab.freedesktop.org/libfprint/libfprint/tags?sort=updated_desc \
/libfprint/libfprint/uploads/(?:[^/]*)/libfprint-(.*).tar.xz

View File

@ -1,523 +0,0 @@
/*
* Example libfprint GTK+ image capture program
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <gtk/gtk.h>
#include <libfprint/fprint.h>
#include "loop.h"
typedef GtkApplication LibfprintDemo;
typedef GtkApplicationClass LibfprintDemoClass;
G_DEFINE_TYPE (LibfprintDemo, libfprint_demo, GTK_TYPE_APPLICATION)
typedef enum {
IMAGE_DISPLAY_NONE = 0,
IMAGE_DISPLAY_MINUTIAE = 1 << 0,
IMAGE_DISPLAY_BINARY = 1 << 1
} ImageDisplayFlags;
typedef struct {
GtkApplicationWindow parent_instance;
GtkWidget *header_bar;
GtkWidget *mode_stack;
GtkWidget *capture_button;
GtkWidget *capture_image;
GtkWidget *spinner;
GtkWidget *instructions;
struct fp_dscv_dev *ddev;
struct fp_dev *dev;
struct fp_img *img;
ImageDisplayFlags img_flags;
} LibfprintDemoWindow;
typedef GtkApplicationWindowClass LibfprintDemoWindowClass;
G_DEFINE_TYPE (LibfprintDemoWindow, libfprint_demo_window, GTK_TYPE_APPLICATION_WINDOW)
typedef enum {
EMPTY_MODE,
NOIMAGING_MODE,
CAPTURE_MODE,
SPINNER_MODE,
ERROR_MODE
} LibfprintDemoMode;
static void libfprint_demo_set_mode (LibfprintDemoWindow *win,
LibfprintDemoMode mode);
static void
pixbuf_destroy (guchar *pixels, gpointer data)
{
if (pixels == NULL)
return;
g_free (pixels);
}
static unsigned char *
img_to_rgbdata (struct fp_img *img,
int width,
int height)
{
int size = width * height;
unsigned char *imgdata = fp_img_get_data (img);
unsigned char *rgbdata = g_malloc (size * 3);
size_t i;
size_t rgb_offset = 0;
for (i = 0; i < size; i++) {
unsigned char pixel = imgdata[i];
rgbdata[rgb_offset++] = pixel;
rgbdata[rgb_offset++] = pixel;
rgbdata[rgb_offset++] = pixel;
}
return rgbdata;
}
static void
plot_minutiae (unsigned char *rgbdata,
int width,
int height,
struct fp_minutia **minlist,
int nr_minutiae)
{
int i;
#define write_pixel(num) do { \
rgbdata[((num) * 3)] = 0xff; \
rgbdata[((num) * 3) + 1] = 0; \
rgbdata[((num) * 3) + 2] = 0; \
} while(0)
for (i = 0; i < nr_minutiae; i++) {
struct fp_minutia *min = minlist[i];
int x, y;
size_t pixel_offset;
fp_minutia_get_coords(min, &x, &y);
pixel_offset = (y * width) + x;
write_pixel(pixel_offset - 2);
write_pixel(pixel_offset - 1);
write_pixel(pixel_offset);
write_pixel(pixel_offset + 1);
write_pixel(pixel_offset + 2);
write_pixel(pixel_offset - (width * 2));
write_pixel(pixel_offset - (width * 1) - 1);
write_pixel(pixel_offset - (width * 1));
write_pixel(pixel_offset - (width * 1) + 1);
write_pixel(pixel_offset + (width * 1) - 1);
write_pixel(pixel_offset + (width * 1));
write_pixel(pixel_offset + (width * 1) + 1);
write_pixel(pixel_offset + (width * 2));
}
}
static GdkPixbuf *
img_to_pixbuf (struct fp_img *img,
ImageDisplayFlags flags)
{
int width;
int height;
unsigned char *rgbdata;
width = fp_img_get_width (img);
height = fp_img_get_height (img);
if (flags & IMAGE_DISPLAY_BINARY) {
struct fp_img *binary;
binary = fp_img_binarize (img);
rgbdata = img_to_rgbdata (binary, width, height);
fp_img_free (binary);
} else {
rgbdata = img_to_rgbdata (img, width, height);
}
if (flags & IMAGE_DISPLAY_MINUTIAE) {
struct fp_minutia **minlist;
int nr_minutiae;
minlist = fp_img_get_minutiae (img, &nr_minutiae);
plot_minutiae (rgbdata, width, height, minlist, nr_minutiae);
}
return gdk_pixbuf_new_from_data (rgbdata, GDK_COLORSPACE_RGB,
FALSE, 8, width, height,
width * 3, pixbuf_destroy,
NULL);
}
static void
update_image (LibfprintDemoWindow *win)
{
GdkPixbuf *pixbuf;
if (win->img == NULL) {
gtk_image_clear (GTK_IMAGE (win->capture_image));
return;
}
g_debug ("Updating image, minutiae %s, binary mode %s",
win->img_flags & IMAGE_DISPLAY_MINUTIAE ? "shown" : "hidden",
win->img_flags & IMAGE_DISPLAY_BINARY ? "on" : "off");
pixbuf = img_to_pixbuf (win->img, win->img_flags);
gtk_image_set_from_pixbuf (GTK_IMAGE (win->capture_image), pixbuf);
g_object_unref (pixbuf);
}
static void
libfprint_demo_set_spinner_label (LibfprintDemoWindow *win,
const char *message)
{
char *label;
label = g_strdup_printf ("<b><span size=\"large\">%s</span></b>", message);
gtk_label_set_markup (GTK_LABEL (win->instructions), label);
g_free (label);
}
static void
libfprint_demo_set_capture_label (LibfprintDemoWindow *win)
{
struct fp_driver *drv;
enum fp_scan_type scan_type;
const char *message;
drv = fp_dscv_dev_get_driver (win->ddev);
scan_type = fp_driver_get_scan_type(drv);
switch (scan_type) {
case FP_SCAN_TYPE_PRESS:
message = "Place your finger on the fingerprint reader";
break;
case FP_SCAN_TYPE_SWIPE:
message = "Swipe your finger across the fingerprint reader";
break;
default:
g_assert_not_reached ();
}
libfprint_demo_set_spinner_label (win, message);
}
static void
dev_capture_start_cb (struct fp_dev *dev,
int result,
struct fp_img *img,
void *user_data)
{
LibfprintDemoWindow *win = user_data;
if (result < 0) {
libfprint_demo_set_mode (win, ERROR_MODE);
return;
}
fp_async_capture_stop (dev, NULL, NULL);
win->img = img;
update_image (win);
libfprint_demo_set_mode (win, CAPTURE_MODE);
}
static void
dev_open_cb (struct fp_dev *dev, int status, void *user_data)
{
LibfprintDemoWindow *win = user_data;
int r;
if (status < 0) {
libfprint_demo_set_mode (win, ERROR_MODE);
return;
}
libfprint_demo_set_capture_label (win);
win->dev = dev;
r = fp_async_capture_start (win->dev, FALSE, dev_capture_start_cb, user_data);
if (r < 0) {
g_warning ("fp_async_capture_start failed: %d", r);
libfprint_demo_set_mode (win, ERROR_MODE);
return;
}
}
static void
activate_capture (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
LibfprintDemoWindow *win = user_data;
int r;
libfprint_demo_set_mode (win, SPINNER_MODE);
g_clear_pointer (&win->img, fp_img_free);
if (win->dev != NULL) {
dev_open_cb (win->dev, 0, user_data);
return;
}
libfprint_demo_set_spinner_label (win, "Opening fingerprint reader");
r = fp_async_dev_open (win->ddev, dev_open_cb, user_data);
if (r < 0) {
g_warning ("fp_async_dev_open failed: %d", r);
libfprint_demo_set_mode (win, ERROR_MODE);
return;
}
}
static void
activate_quit (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkApplication *app = user_data;
GtkWidget *win;
GList *list, *next;
list = gtk_application_get_windows (app);
while (list)
{
win = list->data;
next = list->next;
gtk_widget_destroy (GTK_WIDGET (win));
list = next;
}
}
static void
activate_show_minutiae (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
LibfprintDemoWindow *win = user_data;
GVariant *state;
gboolean new_state;
state = g_action_get_state (G_ACTION (action));
new_state = !g_variant_get_boolean (state);
g_action_change_state (G_ACTION (action), g_variant_new_boolean (new_state));
g_variant_unref (state);
if (new_state)
win->img_flags |= IMAGE_DISPLAY_MINUTIAE;
else
win->img_flags &= ~IMAGE_DISPLAY_MINUTIAE;
update_image (win);
}
static void
activate_show_binary (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
LibfprintDemoWindow *win = user_data;
GVariant *state;
gboolean new_state;
state = g_action_get_state (G_ACTION (action));
new_state = !g_variant_get_boolean (state);
g_action_change_state (G_ACTION (action), g_variant_new_boolean (new_state));
g_variant_unref (state);
if (new_state)
win->img_flags |= IMAGE_DISPLAY_BINARY;
else
win->img_flags &= ~IMAGE_DISPLAY_BINARY;
update_image (win);
}
static void
change_show_minutiae_state (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
g_simple_action_set_state (action, state);
}
static void
change_show_binary_state (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
g_simple_action_set_state (action, state);
}
static GActionEntry app_entries[] = {
{ "quit", activate_quit, NULL, NULL, NULL },
};
static GActionEntry win_entries[] = {
{ "show-minutiae", activate_show_minutiae, NULL, "false", change_show_minutiae_state },
{ "show-binary", activate_show_binary, NULL, "false", change_show_binary_state },
{ "capture", activate_capture, NULL, NULL, NULL }
};
static void
activate (GApplication *app)
{
LibfprintDemoWindow *window;
window = g_object_new (libfprint_demo_window_get_type (),
"application", app,
NULL);
gtk_widget_show (GTK_WIDGET (window));
}
static void
libfprint_demo_set_mode (LibfprintDemoWindow *win,
LibfprintDemoMode mode)
{
struct fp_driver *drv;
char *title;
switch (mode) {
case EMPTY_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "empty-mode");
gtk_widget_set_sensitive (win->capture_button, FALSE);
gtk_spinner_stop (GTK_SPINNER (win->spinner));
break;
case NOIMAGING_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "noimaging-mode");
gtk_widget_set_sensitive (win->capture_button, FALSE);
gtk_spinner_stop (GTK_SPINNER (win->spinner));
break;
case CAPTURE_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "capture-mode");
gtk_widget_set_sensitive (win->capture_button, TRUE);
drv = fp_dscv_dev_get_driver (win->ddev);
title = g_strdup_printf ("%s Test", fp_driver_get_full_name (drv));
gtk_header_bar_set_title (GTK_HEADER_BAR (win->header_bar), title);
g_free (title);
gtk_spinner_stop (GTK_SPINNER (win->spinner));
break;
case SPINNER_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "spinner-mode");
gtk_widget_set_sensitive (win->capture_button, FALSE);
gtk_spinner_start (GTK_SPINNER (win->spinner));
break;
case ERROR_MODE:
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "error-mode");
gtk_widget_set_sensitive (win->capture_button, FALSE);
gtk_spinner_stop (GTK_SPINNER (win->spinner));
break;
default:
g_assert_not_reached ();
}
}
static void
libfprint_demo_init (LibfprintDemo *app)
{
g_action_map_add_action_entries (G_ACTION_MAP (app),
app_entries, G_N_ELEMENTS (app_entries),
app);
}
static void
libfprint_demo_class_init (LibfprintDemoClass *class)
{
GApplicationClass *app_class = G_APPLICATION_CLASS (class);
app_class->activate = activate;
}
static void
libfprint_demo_window_init (LibfprintDemoWindow *window)
{
struct fp_dscv_dev **discovered_devs;
gtk_widget_init_template (GTK_WIDGET (window));
gtk_window_set_default_size (GTK_WINDOW (window), 700, 500);
g_action_map_add_action_entries (G_ACTION_MAP (window),
win_entries, G_N_ELEMENTS (win_entries),
window);
if (fp_init () < 0) {
libfprint_demo_set_mode (window, ERROR_MODE);
return;
}
setup_pollfds ();
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
libfprint_demo_set_mode (window, ERROR_MODE);
return;
}
/* Empty list? */
if (discovered_devs[0] == NULL) {
fp_dscv_devs_free (discovered_devs);
libfprint_demo_set_mode (window, EMPTY_MODE);
return;
}
if (!fp_driver_supports_imaging(fp_dscv_dev_get_driver(discovered_devs[0]))) {
libfprint_demo_set_mode (window, NOIMAGING_MODE);
return;
}
window->ddev = discovered_devs[0];
libfprint_demo_set_mode (window, CAPTURE_MODE);
}
static void
libfprint_demo_window_class_init (LibfprintDemoWindowClass *class)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
gtk_widget_class_set_template_from_resource (widget_class, "/libfprint_demo/gtk-libfprint-test.ui");
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, header_bar);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, mode_stack);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, capture_button);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, capture_image);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, spinner);
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, instructions);
//FIXME setup dispose
}
int main (int argc, char **argv)
{
GtkApplication *app;
app = GTK_APPLICATION (g_object_new (libfprint_demo_get_type (),
"application-id", "org.freedesktop.libfprint.Demo",
"flags", G_APPLICATION_FLAGS_NONE,
NULL));
return g_application_run (G_APPLICATION (app), 0, NULL);
}

View File

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

View File

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

View File

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

View File

@ -1,27 +0,0 @@
/*
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef POLL_H
#define POLL_H
int setup_pollfds(void);
#endif

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,159 +0,0 @@
/*
* Example fingerprint enrollment program
* Enrolls your right index finger and saves the print to disk
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libfprint/fprint.h>
#include "storage.h"
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
{
struct fp_dscv_dev *ddev = discovered_devs[0];
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;
}
struct fp_print_data *enroll(struct fp_dev *dev) {
struct fp_print_data *enrolled_print = NULL;
int r;
printf("You will need to successfully scan your finger %d times to "
"complete the process.\n", fp_dev_get_nr_enroll_stages(dev));
do {
struct fp_img *img = NULL;
printf("\nScan your finger now.\n");
r = fp_enroll_finger_img(dev, &enrolled_print, &img);
if (img) {
fp_img_save_to_file(img, "enrolled.pgm");
printf("Wrote scanned image to enrolled.pgm\n");
fp_img_free(img);
}
if (r < 0) {
printf("Enroll failed with error %d\n", r);
return NULL;
}
switch (r) {
case FP_ENROLL_COMPLETE:
printf("Enroll complete!\n");
break;
case FP_ENROLL_FAIL:
printf("Enroll failed, something wen't wrong :(\n");
return NULL;
case FP_ENROLL_PASS:
printf("Enroll stage passed. Yay!\n");
break;
case FP_ENROLL_RETRY:
printf("Didn't quite catch that. Please try again.\n");
break;
case FP_ENROLL_RETRY_TOO_SHORT:
printf("Your swipe was too short, please try again.\n");
break;
case FP_ENROLL_RETRY_CENTER_FINGER:
printf("Didn't catch that, please center your finger on the "
"sensor and try again.\n");
break;
case FP_ENROLL_RETRY_REMOVE_FINGER:
printf("Scan failed, please remove your finger and then try "
"again.\n");
break;
}
} while (r != FP_ENROLL_COMPLETE);
if (!enrolled_print) {
fprintf(stderr, "Enroll complete but no print?\n");
return NULL;
}
printf("Enrollment completed!\n\n");
return enrolled_print;
}
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;
printf("This program will enroll your right index finger, "
"unconditionally overwriting any right-index print that was enrolled "
"previously. If you want to continue, press enter, otherwise hit "
"Ctrl+C\n");
getchar();
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
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. It's now time to enroll your finger.\n\n");
data = enroll(dev);
if (!data)
goto out_close;
r = print_data_save(data, RIGHT_INDEX);
if (r < 0)
fprintf(stderr, "Data save failed, code %d\n", r);
fp_print_data_free(data);
out_close:
fp_dev_close(dev);
out:
fp_exit();
return r;
}

View File

@ -1,108 +0,0 @@
/*
* Example libfprint image capture program
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.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_img *img = NULL;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
fprintf(stderr, "Could not discover devices\n");
goto out;
}
ddev = discover_device(discovered_devs);
if (!ddev) {
fp_dscv_devs_free(discovered_devs);
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;
}
if (!fp_dev_supports_imaging(dev)) {
fprintf(stderr, "this device does not have imaging capabilities.\n");
goto out_close;
}
printf("Opened device. It's now time to scan your finger.\n\n");
r = fp_dev_img_capture(dev, 0, &img);
if (r) {
fprintf(stderr, "image capture failed, code %d\n", r);
goto out_close;
}
r = fp_img_save_to_file(img, "finger.pgm");
if (r) {
fprintf(stderr, "img save failed, code %d\n", r);
goto out_close;
}
fp_img_standardize(img);
r = fp_img_save_to_file(img, "finger_standardized.pgm");
fp_img_free(img);
if (r) {
fprintf(stderr, "standardized img save failed, code %d\n", r);
goto out_close;
}
r = 0;
out_close:
fp_dev_close(dev);
out:
fp_exit();
return r;
}

View File

@ -1,261 +0,0 @@
/*
* Example libfprint continuous image capture program
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libfprint/fprint.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/extensions/Xvlib.h>
#define FORMAT 0x32595559
static int adaptor = -1;
static char *framebuffer = NULL;
static Display *display = NULL;
static Window window=(Window)NULL;
static XvImage *xv_image = NULL;
static XvAdaptorInfo *info;
static GC gc;
static int connection = -1;
/* based on macro by Bart Nabbe */
#define GREY2YUV(grey, y, u, v)\
y = (9798*grey + 19235*grey + 3736*grey) / 32768;\
u = (-4784*grey - 9437*grey + 14221*grey) / 32768 + 128;\
v = (20218*grey - 16941*grey - 3277*grey) / 32768 + 128;\
y = y < 0 ? 0 : y;\
u = u < 0 ? 0 : u;\
v = v < 0 ? 0 : v;\
y = y > 255 ? 255 : y;\
u = u > 255 ? 255 : u;\
v = v > 255 ? 255 : v
static void grey2yuy2 (unsigned char *grey, char *YUV, int num) {
int i, j;
int y0, y1, u0, u1, v0, v1;
uint64_t gval;
for (i = 0, j = 0; i < num; i += 2, j += 4)
{
gval = grey[i];
GREY2YUV (gval, y0, u0 , v0);
gval = grey[i + 1];
GREY2YUV (gval, y1, u1 , v1);
YUV[j + 0] = y0;
YUV[j + 1] = (u0+u1)/2;
YUV[j + 2] = y1;
YUV[j + 3] = (v0+v1)/2;
}
}
static void display_frame(struct fp_img *img)
{
int width = fp_img_get_width(img);
int height = fp_img_get_height(img);
unsigned char *data = fp_img_get_data(img);
if (adaptor < 0)
return;
grey2yuy2(data, framebuffer, width * height);
xv_image = XvCreateImage(display, info[adaptor].base_id, FORMAT,
framebuffer, width, height);
XvPutImage(display, info[adaptor].base_id, window, gc, xv_image,
0, 0, width, height, 0, 0, width, height);
}
static void QueryXv(void)
{
unsigned int num_adaptors;
int num_formats;
XvImageFormatValues *formats = NULL;
int i,j;
char xv_name[5];
XvQueryAdaptors(display, DefaultRootWindow(display), &num_adaptors,
&info);
for(i = 0; i < num_adaptors; i++) {
formats = XvListImageFormats(display, info[i].base_id,
&num_formats);
for(j = 0; j < num_formats; j++) {
xv_name[4] = 0;
memcpy(xv_name, &formats[j].id, 4);
if(formats[j].id == FORMAT) {
printf("using Xv format 0x%x %s %s\n",
formats[j].id, xv_name,
(formats[j].format==XvPacked)
? "packed" : "planar");
if (adaptor < 0)
adaptor = i;
}
}
}
XFree(formats);
if (adaptor < 0)
printf("No suitable Xv adaptor found\n");
}
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;
XEvent xev;
XGCValues xgcv;
long background=0x010203;
struct fp_dscv_dev *ddev;
struct fp_dscv_dev **discovered_devs;
struct fp_dev *dev;
int img_width;
int img_height;
int standardize = 0;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
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;
}
if (!fp_dev_supports_imaging(dev)) {
fprintf(stderr, "this device does not have imaging capabilities.\n");
goto out_close;
}
img_width = fp_dev_get_img_width(dev);
img_height = fp_dev_get_img_height(dev);
if (img_width <= 0 || img_height <= 0) {
fprintf(stderr, "this device returns images with variable dimensions,"
" this example does not support that.\n");
goto out_close;
}
framebuffer = malloc(img_width * img_height * 2);
if (!framebuffer)
goto out_close;
/* make the window */
display = XOpenDisplay(getenv("DISPLAY"));
if(display == NULL) {
fprintf(stderr,"Could not open display \"%s\"\n",
getenv("DISPLAY"));
goto out_close;
}
QueryXv();
if (adaptor < 0)
goto out_close;
window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0,
img_width, img_height, 0,
WhitePixel(display, DefaultScreen(display)), background);
XSelectInput(display, window, StructureNotifyMask | KeyPressMask);
XMapWindow(display, window);
connection = ConnectionNumber(display);
gc = XCreateGC(display, window, 0, &xgcv);
printf("Press S to toggle standardized mode, Q to quit\n");
while (1) { /* event loop */
struct fp_img *img;
r = fp_dev_img_capture(dev, 1, &img);
if (r) {
fprintf(stderr, "image capture failed, code %d\n", r);
goto out_close;
}
if (standardize)
fp_img_standardize(img);
display_frame(img);
fp_img_free(img);
XFlush(display);
while (XPending(display) > 0) {
XNextEvent(display, &xev);
if (xev.type != KeyPress)
continue;
switch (XKeycodeToKeysym(display, xev.xkey.keycode, 0)) {
case XK_q:
case XK_Q:
r = 0;
goto out_close;
break;
case XK_s:
case XK_S:
standardize = !standardize;
break;
}
} /* XPending */
}
r = 0;
out_close:
if (framebuffer)
free(framebuffer);
fp_dev_close(dev);
if ((void *) window != NULL)
XUnmapWindow(display, window);
if (display != NULL)
XFlush(display);
out:
fp_exit();
return r;
}

View File

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

View File

@ -1,136 +0,0 @@
/*
* 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;
}

View File

@ -1,27 +0,0 @@
/*
* Trivial storage driver for example programs
*
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __STORAGE_H
#define __STORAGE_H
int print_data_save(struct fp_print_data *fp_data, enum fp_finger finger);
struct fp_print_data* print_data_load(struct fp_dev *dev, enum fp_finger finger);
#endif /* __STORAGE_H */

View File

@ -1,149 +0,0 @@
/*
* Example fingerprint verification program, which verifies the right index
* finger which has been previously enrolled to disk.
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libfprint/fprint.h>
#include "storage.h"
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
{
struct fp_dscv_dev *ddev = discovered_devs[0];
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 verify(struct fp_dev *dev, struct fp_print_data *data)
{
int r;
do {
struct fp_img *img = NULL;
sleep(1);
printf("\nScan your finger now.\n");
r = fp_verify_finger_img(dev, data, &img);
if (img) {
fp_img_save_to_file(img, "verify.pgm");
printf("Wrote scanned image to verify.pgm\n");
fp_img_free(img);
}
if (r < 0) {
printf("verification failed with error %d :(\n", r);
return r;
}
switch (r) {
case FP_VERIFY_NO_MATCH:
printf("NO MATCH!\n");
return 0;
case FP_VERIFY_MATCH:
printf("MATCH!\n");
return 0;
case FP_VERIFY_RETRY:
printf("Scan didn't quite work. Please try again.\n");
break;
case FP_VERIFY_RETRY_TOO_SHORT:
printf("Swipe was too short, please try again.\n");
break;
case FP_VERIFY_RETRY_CENTER_FINGER:
printf("Please center your finger on the sensor and try again.\n");
break;
case FP_VERIFY_RETRY_REMOVE_FINGER:
printf("Please remove finger from the sensor and try again.\n");
break;
}
} while (1);
}
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");
data = print_data_load(dev, RIGHT_INDEX);
if (!data) {
fprintf(stderr, "Failed to load fingerprint, error %d\n", r);
fprintf(stderr, "Did you remember to enroll your right index finger "
"first?\n");
goto out_close;
}
printf("Print loaded. Time to verify!\n");
do {
char buffer[20];
verify(dev, data);
printf("Verify again? [Y/n]? ");
fgets(buffer, sizeof(buffer), stdin);
if (buffer[0] != '\n' && buffer[0] != 'y' && buffer[0] != 'Y')
break;
} while (1);
fp_print_data_free(data);
out_close:
fp_dev_close(dev);
out:
fp_exit();
return r;
}

View File

@ -1,188 +0,0 @@
/*
* Example fingerprint verification program
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <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;
}
struct fp_print_data *enroll(struct fp_dev *dev) {
struct fp_print_data *enrolled_print = NULL;
int r;
printf("You will need to successfully scan your finger %d times to "
"complete the process.\n", fp_dev_get_nr_enroll_stages(dev));
do {
printf("\nScan your finger now.\n");
r = fp_enroll_finger(dev, &enrolled_print);
if (r < 0) {
printf("Enroll failed with error %d\n", r);
return NULL;
}
switch (r) {
case FP_ENROLL_COMPLETE:
printf("Enroll complete!\n");
break;
case FP_ENROLL_FAIL:
printf("Enroll failed, something wen't wrong :(\n");
return NULL;
case FP_ENROLL_PASS:
printf("Enroll stage passed. Yay!\n");
break;
case FP_ENROLL_RETRY:
printf("Didn't quite catch that. Please try again.\n");
break;
case FP_ENROLL_RETRY_TOO_SHORT:
printf("Your swipe was too short, please try again.\n");
break;
case FP_ENROLL_RETRY_CENTER_FINGER:
printf("Didn't catch that, please center your finger on the "
"sensor and try again.\n");
break;
case FP_ENROLL_RETRY_REMOVE_FINGER:
printf("Scan failed, please remove your finger and then try "
"again.\n");
break;
}
} while (r != FP_ENROLL_COMPLETE);
if (!enrolled_print) {
fprintf(stderr, "Enroll complete but no print?\n");
return NULL;
}
printf("Enrollment completed!\n\n");
return enrolled_print;
}
int verify(struct fp_dev *dev, struct fp_print_data *data)
{
int r;
do {
sleep(1);
printf("\nScan your finger now.\n");
r = fp_verify_finger(dev, data);
if (r < 0) {
printf("verification failed with error %d :(\n", r);
return r;
}
switch (r) {
case FP_VERIFY_NO_MATCH:
printf("NO MATCH!\n");
return 0;
case FP_VERIFY_MATCH:
printf("MATCH!\n");
return 0;
case FP_VERIFY_RETRY:
printf("Scan didn't quite work. Please try again.\n");
break;
case FP_VERIFY_RETRY_TOO_SHORT:
printf("Swipe was too short, please try again.\n");
break;
case FP_VERIFY_RETRY_CENTER_FINGER:
printf("Please center your finger on the sensor and try again.\n");
break;
case FP_VERIFY_RETRY_REMOVE_FINGER:
printf("Please remove finger from the sensor and try again.\n");
break;
}
} while (1);
}
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. It's now time to enroll your finger.\n\n");
data = enroll(dev);
if (!data)
goto out_close;
printf("Normally we'd save that print to disk, and recall it at some "
"point later when we want to authenticate the user who just "
"enrolled. In the interests of demonstration, we'll authenticate "
"that user immediately.\n");
do {
char buffer[20];
verify(dev, data);
printf("Verify again? [Y/n]? ");
fgets(buffer, sizeof(buffer), stdin);
if (buffer[0] != '\n' && buffer[0] != 'y' && buffer[0] != 'Y')
break;
} while (1);
fp_print_data_free(data);
out_close:
fp_dev_close(dev);
out:
fp_exit();
return r;
}

View File

@ -1,837 +0,0 @@
/*
* AuthenTec AES1610 driver for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007 Vasily Khoruzhick
* Copyright (C) 2009 Guido Grazioli <guido.grazioli@gmail.com>
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* Based on code from libfprint aes2501 driver.
*
* 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 "aes1610"
#include "drivers_api.h"
#include "aeslib.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
static int adjust_gain(unsigned char *buffer, int status);
#define FIRST_AES1610_REG 0x1B
#define LAST_AES1610_REG 0xFF
#define GAIN_STATUS_FIRST 1
#define GAIN_STATUS_NORMAL 2
/* FIXME these need checking */
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
#define FINGER_DETECTION_LEN 19
#define STRIP_CAPTURE_LEN 665
/*
* The AES1610 is an imaging device using a swipe-type sensor. It samples
* the finger at preprogrammed intervals, sending a 128x8 frame to the
* computer.
* Unless the user is scanning their finger unreasonably fast, the frames
* *will* overlap. The implementation below detects this overlap and produces
* a contiguous image as the end result.
* The fact that the user determines the length of the swipe (and hence the
* number of useful frames) and also the fact that overlap varies means that
* images returned from this driver vary in height.
*/
#define FRAME_WIDTH 128
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
/* maximum number of frames to read during a scan */
/* FIXME reduce substantially */
#define MAX_FRAMES 350
/****** GENERAL FUNCTIONS ******/
struct aes1610_dev {
uint8_t read_regs_retry_count;
GSList *strips;
size_t strips_len;
gboolean deactivating;
uint8_t blanks_count;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
typedef void (*aes1610_read_regs_cb)(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data);
struct aes1610_read_regs {
struct fp_img_dev *dev;
aes1610_read_regs_cb callback;
struct aes_regwrite *regwrite;
void *user_data;
};
/* FIXME: what to do here? */
static void stub_capture_stop_cb(struct fp_img_dev *dev, int result, void *user_data)
{
}
/* check that read succeeded but ignore all data */
static void generic_ignore_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_failed(ssm, -EIO);
else if (transfer->length != transfer->actual_length)
fpi_ssm_mark_failed(ssm, -EPROTO);
else
fpi_ssm_next_state(ssm);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
fpi_ssm *ssm = user_data;
if (result == 0)
fpi_ssm_next_state(ssm);
else
fpi_ssm_mark_failed(ssm, result);
}
/* read the specified number of bytes from the IN endpoint but throw them
* away, then increment the SSM */
static void generic_read_ignore_data(fpi_ssm *ssm, struct fp_dev *dev, size_t bytes)
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
int r;
data = g_malloc(bytes);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(dev), EP_IN, data, bytes,
generic_ignore_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
/****** FINGER PRESENCE DETECTION ******/
static const struct aes_regwrite finger_det_reqs[] = {
{ 0x80, 0x01 },
{ 0x80, 0x12 },
{ 0x85, 0x00 },
{ 0x8A, 0x00 },
{ 0x8B, 0x0E },
{ 0x8C, 0x90 },
{ 0x8D, 0x83 },
{ 0x8E, 0x07 },
{ 0x8F, 0x07 },
{ 0x96, 0x00 },
{ 0x97, 0x48 },
{ 0xA1, 0x00 },
{ 0xA2, 0x50 },
{ 0xA6, 0xE4 },
{ 0xAD, 0x08 },
{ 0xAE, 0x5B },
{ 0xAF, 0x54 },
{ 0xB1, 0x28 },
{ 0xB5, 0xAB },
{ 0xB6, 0x0E },
{ 0x1B, 0x2D },
{ 0x81, 0x04 }
};
static void start_finger_detection(struct fp_img_dev *dev);
static void finger_det_data_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
unsigned char *data = transfer->buffer;
int i;
int sum = 0;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_imgdev_session_error(dev, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fpi_imgdev_session_error(dev, -EPROTO);
goto out;
}
/* examine histogram to determine finger presence */
for (i = 3; i < 17; i++)
sum += (data[i] & 0xf) + (data[i] >> 4);
if (sum > 20) {
/* reset default gain */
adjust_gain(data,GAIN_STATUS_FIRST);
/* finger present, start capturing */
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
} else {
/* no finger, poll for a new histogram */
start_finger_detection(dev);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
{
struct libusb_transfer *transfer;
unsigned char *data;
int r;
if (result) {
fpi_imgdev_session_error(dev, result);
return;
}
transfer = fpi_usb_alloc();
data = g_malloc(FINGER_DETECTION_LEN);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, FINGER_DETECTION_LEN,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
}
static void start_finger_detection(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
aes_write_regv(dev, finger_det_reqs, G_N_ELEMENTS(finger_det_reqs), finger_det_reqs_cb, NULL);
}
/****** CAPTURE ******/
static struct aes_regwrite capture_reqs[] = {
{ 0x80, 0x01 },
{ 0x80, 0x12 },
{ 0x84, 0x01 },
{ 0x85, 0x00 },
{ 0x89, 0x64 },
{ 0x8A, 0x00 },
{ 0x8B, 0x0E },
{ 0x8C, 0x90 },
{ 0xBE, 0x23 },
{ 0x29, 0x04 },
{ 0x2A, 0xFF },
{ 0x96, 0x00 },
{ 0x98, 0x03 },
{ 0x99, 0x00 },
{ 0x9C, 0xA5 },
{ 0x9D, 0x40 },
{ 0x9E, 0xC6 },
{ 0x9F, 0x8E },
{ 0xA2, 0x50 },
{ 0xA3, 0xF0 },
{ 0xAD, 0x08 },
{ 0xBD, 0x4F },
{ 0xAF, 0x54 },
{ 0xB1, 0x08 },
{ 0xB5, 0xAB },
{ 0x1B, 0x2D },
{ 0xB6, 0x4E },
{ 0xB8, 0x70 },
{ 0x2B, 0xB3 },
{ 0x2C, 0x5D },
{ 0x2D, 0x98 },
{ 0x2E, 0xB0 },
{ 0x2F, 0x20 },
{ 0xA2, 0xD0 },
{ 0x1D, 0x21 },
{ 0x1E, 0xBE },
{ 0x1C, 0x00 },
{ 0x1D, 0x30 },
{ 0x1E, 0x29 },
{ 0x1C, 0x01 },
{ 0x1D, 0x00 },
{ 0x1E, 0x9E },
{ 0x1C, 0x02 },
{ 0x1D, 0x30 },
{ 0x1E, 0xBB },
{ 0x1C, 0x03 },
{ 0x1D, 0x00 },
{ 0x1E, 0x9D },
{ 0x1C, 0x04 },
{ 0x1D, 0x22 },
{ 0x1E, 0xFF },
{ 0x1C, 0x05 },
{ 0x1D, 0x1B },
{ 0x1E, 0x4E },
{ 0x1C, 0x06 },
{ 0x1D, 0x16 },
{ 0x1E, 0x28 },
{ 0x1C, 0x07 },
{ 0x1D, 0x22 },
{ 0x1E, 0xFF },
{ 0x1C, 0x08 },
{ 0x1D, 0x15 },
{ 0x1E, 0xF1 },
{ 0x1C, 0x09 },
{ 0x1D, 0x30 },
{ 0x1E, 0xD5 },
{ 0x1C, 0x0A },
{ 0x1D, 0x00 },
{ 0x1E, 0x9E },
{ 0x1C, 0x0B },
{ 0x1D, 0x17 },
{ 0x1E, 0x9D },
{ 0x1C, 0x0C },
{ 0x1D, 0x28 },
{ 0x1E, 0xD7 },
{ 0x1C, 0x0D },
{ 0x1D, 0x17 },
{ 0x1E, 0xD7 },
{ 0x1C, 0x0E },
{ 0x1D, 0x0A },
{ 0x1E, 0xCB },
{ 0x1C, 0x0F },
{ 0x1D, 0x24 },
{ 0x1E, 0x14 },
{ 0x1C, 0x10 },
{ 0x1D, 0x17 },
{ 0x1E, 0x85 },
{ 0x1C, 0x11 },
{ 0x1D, 0x15 },
{ 0x1E, 0x71 },
{ 0x1C, 0x12 },
{ 0x1D, 0x2B },
{ 0x1E, 0x36 },
{ 0x1C, 0x13 },
{ 0x1D, 0x12 },
{ 0x1E, 0x06 },
{ 0x1C, 0x14 },
{ 0x1D, 0x30 },
{ 0x1E, 0x97 },
{ 0x1C, 0x15 },
{ 0x1D, 0x21 },
{ 0x1E, 0x32 },
{ 0x1C, 0x16 },
{ 0x1D, 0x06 },
{ 0x1E, 0xE6 },
{ 0x1C, 0x17 },
{ 0x1D, 0x16 },
{ 0x1E, 0x06 },
{ 0x1C, 0x18 },
{ 0x1D, 0x30 },
{ 0x1E, 0x01 },
{ 0x1C, 0x19 },
{ 0x1D, 0x21 },
{ 0x1E, 0x37 },
{ 0x1C, 0x1A },
{ 0x1D, 0x00 },
{ 0x1E, 0x08 },
{ 0x1C, 0x1B },
{ 0x1D, 0x80 },
{ 0x1E, 0xD5 },
{ 0xA2, 0x50 },
{ 0xA2, 0x50 },
{ 0x81, 0x01 }
};
static struct aes_regwrite strip_scan_reqs[] = {
{ 0xBE, 0x23 },
{ 0x29, 0x04 },
{ 0x2A, 0xFF },
{ 0xBD, 0x4F },
{ 0xFF, 0x00 }
};
static const struct aes_regwrite capture_stop[] = {
{ 0x81,0x00 }
};
/*
* The different possible values for 0xBE register */
static unsigned char list_BE_values[10] = {
0x23, 0x43, 0x63, 0x64, 0x65, 0x67, 0x6A, 0x6B
};
/*
* The different possible values for 0xBD register */
static unsigned char list_BD_values[10] = {
0x28, 0x2b, 0x30, 0x3b, 0x45, 0x49, 0x4B
};
/*
* Adjust the gain according to the histogram data
* 0xbd, 0xbe, 0x29 and 0x2A registers are affected
* Returns 0 if no problem occurred
* TODO: This is a basic support for gain. It needs testing/tweaking. */
static int adjust_gain(unsigned char *buffer, int status)
{
// The position in the array of possible values for 0xBE and 0xBD registers
static int pos_list_BE = 0;
static int pos_list_BD = 0;
// This is the first adjustment (we begin acquisition)
// We adjust strip_scan_reqs for future strips and capture_reqs that is sent just after this step
if (status == GAIN_STATUS_FIRST) {
if (buffer[1] > 0x78) { // maximum gain needed
strip_scan_reqs[0].value = 0x6B;
strip_scan_reqs[1].value = 0x06;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x4B;
}
else if (buffer[1] > 0x55) {
strip_scan_reqs[0].value = 0x63;
strip_scan_reqs[1].value = 0x15;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x3b;
}
else if (buffer[1] > 0x40 || buffer[16] > 0x19) {
strip_scan_reqs[0].value = 0x43;
strip_scan_reqs[1].value = 0x13;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x30;
}
else { // minimum gain needed
strip_scan_reqs[0].value = 0x23;
strip_scan_reqs[1].value = 0x07;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x28;
}
// Now copy this values in capture_reqs
capture_reqs[8].value = strip_scan_reqs[0].value;
capture_reqs[9].value = strip_scan_reqs[1].value;
capture_reqs[10].value = strip_scan_reqs[2].value;
capture_reqs[21].value = strip_scan_reqs[3].value;
fp_dbg("first gain: %x %x %x %x %x %x %x %x", strip_scan_reqs[0].reg, strip_scan_reqs[0].value, strip_scan_reqs[1].reg, strip_scan_reqs[1].value, strip_scan_reqs[2].reg, strip_scan_reqs[2].value, strip_scan_reqs[3].reg, strip_scan_reqs[3].value);
}
// Every 2/3 strips
// We try to soften big changes of the gain (at least for 0xBE and 0xBD
// FIXME: This softenning will need testing and tweaking too
else if (status == GAIN_STATUS_NORMAL) {
if (buffer[514] > 0x78) { // maximum gain needed
if (pos_list_BE < 7)
pos_list_BE++;
if (pos_list_BD < 6)
pos_list_BD++;
strip_scan_reqs[1].value = 0x04;
strip_scan_reqs[2].value = 0x35;
}
else if (buffer[514] > 0x55) {
if (pos_list_BE < 2)
pos_list_BE++;
else if (pos_list_BE > 2)
pos_list_BE--;
if (pos_list_BD < 2)
pos_list_BD++;
else if (pos_list_BD > 2)
pos_list_BD--;
strip_scan_reqs[1].value = 0x15;
strip_scan_reqs[2].value = 0x35;
}
else if (buffer[514] > 0x40 || buffer[529] > 0x19) {
if (pos_list_BE < 1)
pos_list_BE++;
else if (pos_list_BE > 1)
pos_list_BE--;
if (pos_list_BD < 1)
pos_list_BD++;
else if (pos_list_BD > 1)
pos_list_BD--;
strip_scan_reqs[1].value = 0x13;
strip_scan_reqs[2].value = 0x35;
}
else { // minimum gain needed
if (pos_list_BE > 0)
pos_list_BE--;
if (pos_list_BD > 0)
pos_list_BD--;
strip_scan_reqs[1].value = 0x07;
strip_scan_reqs[2].value = 0x35;
}
strip_scan_reqs[0].value = list_BE_values[pos_list_BE];
strip_scan_reqs[3].value = list_BD_values[pos_list_BD];
fp_dbg("gain: %x %x %x %x %x %x %x %x", strip_scan_reqs[0].reg, strip_scan_reqs[0].value, strip_scan_reqs[1].reg, strip_scan_reqs[1].value, strip_scan_reqs[2].reg, strip_scan_reqs[2].value, strip_scan_reqs[3].reg, strip_scan_reqs[3].value);
}
// Unknown status
else {
fp_err("Unexpected gain status.");
return 1;
}
return 0;
}
/*
* Restore the default gain values */
static void restore_gain(void)
{
strip_scan_reqs[0].value = list_BE_values[0];
strip_scan_reqs[1].value = 0x04;
strip_scan_reqs[2].value = 0xFF;
strip_scan_reqs[3].value = list_BD_values[0];
capture_reqs[8].value = list_BE_values[0];
capture_reqs[9].value = 0x04;
capture_reqs[10].value = 0xFF;
capture_reqs[21].value = list_BD_values[0];
}
/* capture SM movement:
* request and read strip,
* jump back to request UNLESS there's no finger, in which case exit SM,
* report lack of finger presence, and move to finger detection */
enum capture_states {
CAPTURE_WRITE_REQS,
CAPTURE_READ_DATA,
CAPTURE_REQUEST_STRIP,
CAPTURE_READ_STRIP,
CAPTURE_NUM_STATES,
};
static void capture_read_strip_cb(struct libusb_transfer *transfer)
{
unsigned char *stripdata;
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = transfer->buffer;
int sum, i;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fpi_ssm_mark_failed(ssm, -EPROTO);
goto out;
}
/* FIXME: would preallocating strip buffers be a decent optimization? */
sum = 0;
for (i = 516; i < 530; i++)
{
/* histogram[i] = number of pixels of value i
Only the pixel values from 10 to 15 are used to detect finger. */
sum += data[i];
}
if (sum > 0) {
/* FIXME: would preallocating strip buffers be a decent optimization? */
struct fpi_frame *stripe = g_malloc(FRAME_WIDTH * (FRAME_HEIGHT / 2) + sizeof(struct fpi_frame));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
memcpy(stripdata, data + 1, FRAME_WIDTH * (FRAME_HEIGHT / 2));
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
aesdev->blanks_count = 0;
}
if (sum < 0) {
fpi_ssm_mark_failed(ssm, sum);
goto out;
}
fp_dbg("sum=%d", sum);
/* FIXME: 0 might be too low as a threshold */
/* FIXME: sometimes we get 0 in the middle of a scan, should we wait for
* a few consecutive zeroes? */
/* If sum is 0 for a reasonable # of frames, finger has been removed */
if (sum == 0) {
aesdev->blanks_count++;
fp_dbg("got blank frame");
}
/* use histogram data above for gain calibration (0xbd, 0xbe, 0x29 and 0x2A ) */
adjust_gain(data, GAIN_STATUS_NORMAL);
/* stop capturing if MAX_FRAMES is reached */
if (aesdev->blanks_count > 10 || g_slist_length(aesdev->strips) >= MAX_FRAMES) {
struct fp_img *img;
fp_dbg("sending stop capture.... blanks=%d frames=%d", aesdev->blanks_count, g_slist_length(aesdev->strips));
/* send stop capture bits */
aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop), stub_capture_stop_cb, NULL);
aesdev->strips = g_slist_reverse(aesdev->strips);
fpi_do_movement_estimation(&assembling_ctx, aesdev->strips, aesdev->strips_len);
img = fpi_assemble_frames(&assembling_ctx, aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
aesdev->blanks_count = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
/* Acquisition finished: restore default gain values */
restore_gain();
} else {
/* obtain next strip */
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(_dev);
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_REQS:
fp_dbg("write reqs");
aes_write_regv(dev, capture_reqs, G_N_ELEMENTS(capture_reqs),
generic_write_regv_cb, ssm);
break;
case CAPTURE_READ_DATA:
fp_dbg("read data");
generic_read_ignore_data(ssm, _dev, STRIP_CAPTURE_LEN);
break;
case CAPTURE_REQUEST_STRIP:
fp_dbg("request strip");
if (aesdev->deactivating)
fpi_ssm_mark_completed(ssm);
else
aes_write_regv(dev, strip_scan_reqs, G_N_ELEMENTS(strip_scan_reqs),
generic_write_regv_cb, ssm);
break;
case CAPTURE_READ_STRIP: ;
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
data = g_malloc(STRIP_CAPTURE_LEN);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, STRIP_CAPTURE_LEN,
capture_read_strip_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
break;
};
}
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(_dev);
G_DEBUG_HERE();
if (aesdev->deactivating)
complete_deactivation(dev);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static void start_capture(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
G_DEBUG_HERE();
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
static const struct aes_regwrite init[] = {
{ 0x82, 0x00 }
};
/*
static const struct aes_regwrite stop_reader[] = {
{ 0xFF, 0x00 }
};
*/
enum activate_states {
WRITE_INIT,
ACTIVATE_NUM_STATES,
};
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
/* activation on aes1610 seems much more straightforward compared to aes2501 */
/* verify there's anything missing here */
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT:
fp_dbg("write init");
aes_write_regv(dev, init, G_N_ELEMENTS(init), generic_write_regv_cb, ssm);
break;
}
}
/* jump to finger detection */
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
aesdev->read_regs_retry_count = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
/* FIXME: audit cancellation points, probably need more, specifically
* in error handling paths? */
aesdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
/* FIXME: if we're in the middle of a scan, we should cancel the scan.
* maybe we can do this with a master reset, unconditionally? */
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
aesdev->blanks_count = 0;
fpi_imgdev_deactivate_complete(dev);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* FIXME check endpoints */
int r;
struct aes1610_dev *aesdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = g_malloc0(sizeof(struct aes1610_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev;
aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x1600 }, /* AES1600 */
{ 0, 0, 0, },
};
struct fp_img_driver aes1610_driver = {
.driver = {
.id = AES1610_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1610",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 20,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

@ -1,112 +0,0 @@
/*
* AuthenTec AES1660 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "aes1660"
#include "drivers_api.h"
#include "aeslib.h"
#include "aesx660.h"
#include "aes1660.h"
#define FRAME_WIDTH 128
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = AESX660_FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = g_malloc0(sizeof(struct aesX660_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
aesdev->buffer = g_malloc0(AES1660_FRAME_SIZE + AESX660_HEADER_SIZE);
aesdev->init_seqs[0] = aes1660_init_1;
aesdev->init_seqs_len[0] = G_N_ELEMENTS(aes1660_init_1);
aesdev->init_seqs[1] = aes1660_init_2;
aesdev->init_seqs_len[1] = G_N_ELEMENTS(aes1660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes1660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes1660_start_imaging_cmd);
aesdev->assembling_ctx = &assembling_ctx;
aesdev->extra_img_flags = FP_IMG_PARTIAL;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x1660 },
{ .vendor = 0x08ff, .product = 0x1680 },
{ .vendor = 0x08ff, .product = 0x1681 },
{ .vendor = 0x08ff, .product = 0x1682 },
{ .vendor = 0x08ff, .product = 0x1683 },
{ .vendor = 0x08ff, .product = 0x1684 },
{ .vendor = 0x08ff, .product = 0x1685 },
{ .vendor = 0x08ff, .product = 0x1686 },
{ .vendor = 0x08ff, .product = 0x1687 },
{ .vendor = 0x08ff, .product = 0x1688 },
{ .vendor = 0x08ff, .product = 0x1689 },
{ .vendor = 0x08ff, .product = 0x168a },
{ .vendor = 0x08ff, .product = 0x168b },
{ .vendor = 0x08ff, .product = 0x168c },
{ .vendor = 0x08ff, .product = 0x168d },
{ .vendor = 0x08ff, .product = 0x168e },
{ .vendor = 0x08ff, .product = 0x168f },
{ 0, 0, 0, },
};
struct fp_img_driver aes1660_driver = {
.driver = {
.id = AES1660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1660",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.bz3_threshold = 20,
.open = dev_init,
.close = dev_deinit,
.activate = aesX660_dev_activate,
.deactivate = aesX660_dev_deactivate,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,877 +0,0 @@
/*
* AuthenTec AES2501 driver for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007-2008, 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* Based on code from http://home.gna.org/aes2501, relicensed with permission
*
* 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 "aes2501"
#include "drivers_api.h"
#include "aeslib.h"
#include "aes2501.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
/* FIXME these need checking */
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
#define FINGER_DETECTION_LEN 20
#define READ_REGS_LEN 126
#define READ_REGS_RESP_LEN 159
#define STRIP_CAPTURE_LEN 1705
/*
* The AES2501 is an imaging device using a swipe-type sensor. It samples
* the finger at preprogrammed intervals, sending a 192x16 frame to the
* computer.
* Unless the user is scanning their finger unreasonably fast, the frames
* *will* overlap. The implementation below detects this overlap and produces
* a contiguous image as the end result.
* The fact that the user determines the length of the swipe (and hence the
* number of useful frames) and also the fact that overlap varies means that
* images returned from this driver vary in height.
*/
#define FRAME_WIDTH 192
#define FRAME_HEIGHT 16
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
/* maximum number of frames to read during a scan */
/* FIXME reduce substantially */
#define MAX_FRAMES 150
/****** GENERAL FUNCTIONS ******/
struct aes2501_dev {
uint8_t read_regs_retry_count;
GSList *strips;
size_t strips_len;
gboolean deactivating;
int no_finger_cnt;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
typedef void (*aes2501_read_regs_cb)(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data);
struct aes2501_read_regs {
struct fp_img_dev *dev;
aes2501_read_regs_cb callback;
struct aes_regwrite *regwrite;
void *user_data;
};
static void read_regs_data_cb(struct libusb_transfer *transfer,
struct fp_dev *dev,
fpi_ssm *ssm,
void *user_data)
{
struct aes2501_read_regs *rdata = user_data;
unsigned char *retdata = NULL;
int r;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
r = -EIO;
} else if (transfer->length != transfer->actual_length) {
r = -EPROTO;
} else {
r = 0;
retdata = transfer->buffer;
}
rdata->callback(rdata->dev, r, retdata, rdata->user_data);
g_free(rdata);
}
static void read_regs_rq_cb(struct fp_img_dev *dev, int result, void *user_data)
{
struct aes2501_read_regs *rdata = user_data;
fpi_usb_transfer *transfer;
unsigned char *data;
int r;
g_free(rdata->regwrite);
if (result != 0)
goto err;
data = g_malloc(READ_REGS_LEN);
transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
NULL,
EP_IN,
data,
READ_REGS_LEN,
read_regs_data_cb,
rdata,
BULK_TIMEOUT);
r = fpi_usb_submit_transfer(transfer);
if (r < 0) {
result = -EIO;
goto err;
}
return;
err:
rdata->callback(dev, result, NULL, rdata->user_data);
g_free(rdata);
}
static void read_regs(struct fp_img_dev *dev, aes2501_read_regs_cb callback,
void *user_data)
{
/* FIXME: regwrite is dynamic because of asynchronity. is this really
* required? */
struct aes_regwrite *regwrite = g_malloc(sizeof(*regwrite));
struct aes2501_read_regs *rdata = g_malloc(sizeof(*rdata));
G_DEBUG_HERE();
regwrite->reg = AES2501_REG_CTRL2;
regwrite->value = AES2501_CTRL2_READ_REGS;
rdata->dev = dev;
rdata->callback = callback;
rdata->user_data = user_data;
rdata->regwrite = regwrite;
aes_write_regv(dev, (const struct aes_regwrite *) regwrite, 1,
read_regs_rq_cb, rdata);
}
/* Read the value of a specific register from a register dump */
static int regval_from_dump(unsigned char *data, uint8_t target)
{
if (*data != FIRST_AES2501_REG) {
fp_err("not a register dump");
return -EILSEQ;
}
if (!(FIRST_AES2501_REG <= target && target <= LAST_AES2501_REG)) {
fp_err("out of range");
return -EINVAL;
}
target -= FIRST_AES2501_REG;
target *= 2;
return data[target + 1];
}
static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
fpi_ssm *ssm = user_data;
if (result == 0)
fpi_ssm_next_state(ssm);
else
fpi_ssm_mark_failed(ssm, result);
}
/* check that read succeeded but ignore all data */
static void generic_ignore_data_cb(struct libusb_transfer *transfer,
struct fp_dev *dev,
fpi_ssm *ssm,
void *user_data)
{
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_failed(ssm, -EIO);
else if (transfer->length != transfer->actual_length)
fpi_ssm_mark_failed(ssm, -EPROTO);
else
fpi_ssm_next_state(ssm);
}
/* read the specified number of bytes from the IN endpoint but throw them
* away, then increment the SSM */
static void generic_read_ignore_data(fpi_ssm *ssm, struct fp_dev *dev, size_t bytes)
{
fpi_usb_transfer *transfer;
unsigned char *data;
int r;
data = g_malloc(bytes);
transfer = fpi_usb_fill_bulk_transfer(dev,
ssm,
EP_IN,
data,
bytes,
generic_ignore_data_cb,
NULL,
BULK_TIMEOUT);
r = fpi_usb_submit_transfer(transfer);
if (r < 0)
fpi_ssm_mark_failed(ssm, r);
}
/****** IMAGE PROCESSING ******/
static int sum_histogram_values(unsigned char *data, uint8_t threshold)
{
int r = 0;
int i;
uint16_t *histogram = (uint16_t *)(data + 1);
if (*data != 0xde)
return -EILSEQ;
if (threshold > 0x0f)
return -EINVAL;
/* FIXME endianness */
for (i = threshold; i < 16; i++)
r += histogram[i];
return r;
}
/****** FINGER PRESENCE DETECTION ******/
static const struct aes_regwrite finger_det_reqs[] = {
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ AES2501_REG_DETCTRL,
AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS },
{ AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US },
{ AES2501_REG_MEASDRV, AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE },
{ AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M },
{ AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE },
{ AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE },
{ AES2501_REG_CHANGAIN,
AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X },
{ AES2501_REG_ADREFHI, 0x44 },
{ AES2501_REG_ADREFLO, 0x34 },
{ AES2501_REG_STRTCOL, 0x16 },
{ AES2501_REG_ENDCOL, 0x16 },
{ AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08 },
{ AES2501_REG_TREG1, 0x70 },
{ 0xa2, 0x02 },
{ 0xa7, 0x00 },
{ AES2501_REG_TREGC, AES2501_TREGC_ENABLE },
{ AES2501_REG_TREGD, 0x1a },
{ 0, 0 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
{ AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE },
};
static void start_finger_detection(struct fp_img_dev *dev);
static void finger_det_data_cb(struct libusb_transfer *transfer,
struct fp_dev *_dev,
fpi_ssm *ssm,
void *user_data)
{
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
unsigned char *data = transfer->buffer;
int i;
int sum = 0;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_imgdev_session_error(dev, -EIO);
return;
} else if (transfer->length != transfer->actual_length) {
fpi_imgdev_session_error(dev, -EPROTO);
return;
}
/* examine histogram to determine finger presence */
for (i = 1; i < 9; i++)
sum += (data[i] & 0xf) + (data[i] >> 4);
if (sum > 20) {
/* finger present, start capturing */
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
} else {
/* no finger, poll for a new histogram */
start_finger_detection(dev);
}
}
static void finger_det_reqs_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
fpi_usb_transfer *transfer;
unsigned char *data;
int r;
if (result) {
fpi_imgdev_session_error(dev, result);
return;
}
data = g_malloc(FINGER_DETECTION_LEN);
transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
NULL,
EP_IN,
data,
FINGER_DETECTION_LEN,
finger_det_data_cb,
NULL,
BULK_TIMEOUT);
r = fpi_usb_submit_transfer(transfer);
if (r < 0)
fpi_imgdev_session_error(dev, r);
}
static void start_finger_detection(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
aes_write_regv(dev, finger_det_reqs, G_N_ELEMENTS(finger_det_reqs),
finger_det_reqs_cb, NULL);
}
/****** CAPTURE ******/
static const struct aes_regwrite capture_reqs_1[] = {
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ 0, 0 },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ AES2501_REG_DETCTRL,
AES2501_DETCTRL_SDELAY_31_MS | AES2501_DETCTRL_DRATE_CONTINUOUS },
{ AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US },
{ AES2501_REG_DEMODPHASE2, 0x7c },
{ AES2501_REG_MEASDRV,
AES2501_MEASDRV_MEASURE_SQUARE | AES2501_MEASDRV_MDRIVE_0_325 },
{ AES2501_REG_DEMODPHASE1, 0x24 },
{ AES2501_REG_CHWORD1, 0x00 },
{ AES2501_REG_CHWORD2, 0x6c },
{ AES2501_REG_CHWORD3, 0x09 },
{ AES2501_REG_CHWORD4, 0x54 },
{ AES2501_REG_CHWORD5, 0x78 },
{ 0xa2, 0x02 },
{ 0xa7, 0x00 },
{ 0xb6, 0x26 },
{ 0xb7, 0x1a },
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
{ AES2501_REG_IMAGCTRL,
AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE |
AES2501_IMAGCTRL_IMG_DATA_DISABLE },
{ AES2501_REG_STRTCOL, 0x10 },
{ AES2501_REG_ENDCOL, 0x1f },
{ AES2501_REG_CHANGAIN,
AES2501_CHANGAIN_STAGE1_2X | AES2501_CHANGAIN_STAGE2_2X },
{ AES2501_REG_ADREFHI, 0x70 },
{ AES2501_REG_ADREFLO, 0x20 },
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
{ AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE },
};
static const struct aes_regwrite capture_reqs_2[] = {
{ AES2501_REG_IMAGCTRL,
AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE |
AES2501_IMAGCTRL_IMG_DATA_DISABLE },
{ AES2501_REG_STRTCOL, 0x10 },
{ AES2501_REG_ENDCOL, 0x1f },
{ AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X },
{ AES2501_REG_ADREFHI, 0x70 },
{ AES2501_REG_ADREFLO, 0x20 },
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
};
static struct aes_regwrite strip_scan_reqs[] = {
{ AES2501_REG_IMAGCTRL,
AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE },
{ AES2501_REG_STRTCOL, 0x00 },
{ AES2501_REG_ENDCOL, 0x2f },
{ AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X },
{ AES2501_REG_ADREFHI, AES2501_ADREFHI_MAX_VALUE },
{ AES2501_REG_ADREFLO, 0x20 },
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
};
/* capture SM movement:
* write reqs and read data 1 + 2,
* request and read strip,
* jump back to request UNLESS there's no finger, in which case exit SM,
* report lack of finger presence, and move to finger detection */
enum capture_states {
CAPTURE_WRITE_REQS_1,
CAPTURE_READ_DATA_1,
CAPTURE_WRITE_REQS_2,
CAPTURE_READ_DATA_2,
CAPTURE_REQUEST_STRIP,
CAPTURE_READ_STRIP,
CAPTURE_NUM_STATES,
};
static void capture_read_strip_cb(struct libusb_transfer *transfer,
struct fp_dev *_dev,
fpi_ssm *ssm,
void *user_data)
{
unsigned char *stripdata;
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(_dev);
unsigned char *data = transfer->buffer;
int sum;
int threshold;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_failed(ssm, -EIO);
return;
} else if (transfer->length != transfer->actual_length) {
fpi_ssm_mark_failed(ssm, -EPROTO);
return;
}
threshold = regval_from_dump(data + 1 + 192*8 + 1 + 16*2 + 1 + 8,
AES2501_REG_DATFMT);
if (threshold < 0) {
fpi_ssm_mark_failed(ssm, threshold);
return;
}
sum = sum_histogram_values(data + 1 + 192*8, threshold & 0x0f);
if (sum < 0) {
fpi_ssm_mark_failed(ssm, sum);
return;
}
fp_dbg("sum=%d", sum);
if (sum < AES2501_SUM_LOW_THRESH) {
strip_scan_reqs[4].value -= 0x8;
if (strip_scan_reqs[4].value < AES2501_ADREFHI_MIN_VALUE)
strip_scan_reqs[4].value = AES2501_ADREFHI_MIN_VALUE;
} else if (sum > AES2501_SUM_HIGH_THRESH) {
strip_scan_reqs[4].value += 0x8;
if (strip_scan_reqs[4].value > AES2501_ADREFHI_MAX_VALUE)
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
}
fp_dbg("ADREFHI is %.2x", strip_scan_reqs[4].value);
/* Sum is 0, maybe finger was removed? Wait for 3 empty frames
* to ensure
*/
if (sum == 0) {
aesdev->no_finger_cnt++;
if (aesdev->no_finger_cnt == 3) {
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
fpi_do_movement_estimation(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img = fpi_assemble_frames(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
}
} else {
/* obtain next strip */
/* FIXME: would preallocating strip buffers be a decent optimization? */
struct fpi_frame *stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
memcpy(stripdata, data + 1, 192*8);
aesdev->no_finger_cnt = 0;
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
}
}
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(_dev);
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_REQS_1:
aes_write_regv(dev, capture_reqs_1, G_N_ELEMENTS(capture_reqs_1),
generic_write_regv_cb, ssm);
break;
case CAPTURE_READ_DATA_1:
generic_read_ignore_data(ssm, _dev, READ_REGS_RESP_LEN);
break;
case CAPTURE_WRITE_REQS_2:
aes_write_regv(dev, capture_reqs_2, G_N_ELEMENTS(capture_reqs_2),
generic_write_regv_cb, ssm);
break;
case CAPTURE_READ_DATA_2:
generic_read_ignore_data(ssm, _dev, READ_REGS_RESP_LEN);
break;
case CAPTURE_REQUEST_STRIP:
if (aesdev->deactivating)
fpi_ssm_mark_completed(ssm);
else
aes_write_regv(dev, strip_scan_reqs, G_N_ELEMENTS(strip_scan_reqs),
generic_write_regv_cb, ssm);
break;
case CAPTURE_READ_STRIP: ;
fpi_usb_transfer *transfer;
unsigned char *data;
data = g_malloc(STRIP_CAPTURE_LEN);
transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
ssm,
EP_IN,
data,
STRIP_CAPTURE_LEN,
capture_read_strip_cb,
NULL,
BULK_TIMEOUT);
r = fpi_usb_submit_transfer(transfer);
if (r < 0)
fpi_ssm_mark_failed(ssm, r);
break;
};
}
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(_dev);
G_DEBUG_HERE();
if (aesdev->deactivating)
complete_deactivation(dev);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static void start_capture(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
aesdev->no_finger_cnt = 0;
/* Reset gain */
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
G_DEBUG_HERE();
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
static const struct aes_regwrite init_1[] = {
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ 0, 0 },
{ 0xb0, 0x27 }, /* Reserved? */
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ 0xff, 0x00 }, /* Reserved? */
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ AES2501_REG_DETCTRL,
AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS },
{ AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US },
{ AES2501_REG_MEASDRV,
AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE },
{ AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M },
{ AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE },
{ AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE },
{ AES2501_REG_CHANGAIN,
AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X },
{ AES2501_REG_ADREFHI, 0x44 },
{ AES2501_REG_ADREFLO, 0x34 },
{ AES2501_REG_STRTCOL, 0x16 },
{ AES2501_REG_ENDCOL, 0x16 },
{ AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08 },
{ AES2501_REG_TREG1, 0x70 },
{ 0xa2, 0x02 },
{ 0xa7, 0x00 },
{ AES2501_REG_TREGC, AES2501_TREGC_ENABLE },
{ AES2501_REG_TREGD, 0x1a },
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
{ AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE },
};
static const struct aes_regwrite init_2[] = {
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_AUTOCALOFFSET, 0x41 },
{ AES2501_REG_EXCITCTRL, 0x42 },
{ AES2501_REG_DETCTRL, 0x53 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
};
static const struct aes_regwrite init_3[] = {
{ 0xff, 0x00 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_AUTOCALOFFSET, 0x41 },
{ AES2501_REG_EXCITCTRL, 0x42 },
{ AES2501_REG_DETCTRL, 0x53 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
};
static const struct aes_regwrite init_4[] = {
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ 0xb0, 0x27 },
{ AES2501_REG_ENDROW, 0x0a },
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
{ AES2501_REG_DETCTRL, 0x45 },
{ AES2501_REG_AUTOCALOFFSET, 0x41 },
};
static const struct aes_regwrite init_5[] = {
{ 0xb0, 0x27 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ 0xff, 0x00 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
{ AES2501_REG_EXCITCTRL, 0x40 },
{ AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET },
{ AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET },
};
enum activate_states {
WRITE_INIT_1,
READ_DATA_1,
WRITE_INIT_2,
READ_REGS,
WRITE_INIT_3,
WRITE_INIT_4,
WRITE_INIT_5,
ACTIVATE_NUM_STATES,
};
void activate_read_regs_cb(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data)
{
fpi_ssm *ssm = user_data;
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (status != 0) {
fpi_ssm_mark_failed(ssm, status);
} else {
fp_dbg("reg 0xaf = %x", regs[0x5f]);
if (regs[0x5f] != 0x6b || ++aesdev->read_regs_retry_count == 13)
fpi_ssm_jump_to_state(ssm, WRITE_INIT_4);
else
fpi_ssm_next_state(ssm);
}
}
static void activate_init3_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
fpi_ssm *ssm = user_data;
if (result == 0)
fpi_ssm_jump_to_state(ssm, READ_REGS);
else
fpi_ssm_mark_failed(ssm, result);
}
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
/* This state machine isn't as linear as it may appear. After doing init1
* and init2 register configuration writes, we have to poll a register
* waiting for a specific value. READ_REGS checks the register value, and
* if we're ready to move on, we jump to init4. Otherwise, we write init3
* and then jump back to READ_REGS. In a synchronous model:
[...]
aes_write_regv(init_2);
read_regs(into buffer);
i = 0;
while (buffer[0x5f] == 0x6b) {
aes_write_regv(init_3);
read_regs(into buffer);
if (++i == 13)
break;
}
aes_write_regv(init_4);
*/
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT_1:
aes_write_regv(dev, init_1, G_N_ELEMENTS(init_1),
generic_write_regv_cb, ssm);
break;
case READ_DATA_1:
fp_dbg("read data 1");
generic_read_ignore_data(ssm, _dev, FINGER_DETECTION_LEN);
break;
case WRITE_INIT_2:
aes_write_regv(dev, init_2, G_N_ELEMENTS(init_2),
generic_write_regv_cb, ssm);
break;
case READ_REGS:
read_regs(dev, activate_read_regs_cb, ssm);
break;
case WRITE_INIT_3:
aes_write_regv(dev, init_3, G_N_ELEMENTS(init_3),
activate_init3_cb, ssm);
break;
case WRITE_INIT_4:
aes_write_regv(dev, init_4, G_N_ELEMENTS(init_4),
generic_write_regv_cb, ssm);
break;
case WRITE_INIT_5:
aes_write_regv(dev, init_5, G_N_ELEMENTS(init_5),
generic_write_regv_cb, ssm);
break;
}
}
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
aesdev->read_regs_retry_count = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
/* FIXME: audit cancellation points, probably need more, specifically
* in error handling paths? */
aesdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
/* FIXME: if we're in the middle of a scan, we should cancel the scan.
* maybe we can do this with a master reset, unconditionally? */
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_deactivate_complete(dev);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* FIXME check endpoints */
int r;
struct aes2501_dev *aesdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = g_malloc0(sizeof(struct aes2501_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x2500 }, /* AES2500 */
{ .vendor = 0x08ff, .product = 0x2580 }, /* AES2501 */
{ 0, 0, 0, },
};
struct fp_img_driver aes2501_driver = {
.driver = {
.id = AES2501_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2501",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = IMAGE_WIDTH,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

@ -1,176 +0,0 @@
/*
* AuthenTec AES2501 driver for libfprint
* Copyright (C) 2007 Cyrille Bagard
*
* Based on code from http://home.gna.org/aes2501, relicensed with permission
*
* 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 __AES2501_H
#define __AES2501_H
enum aes2501_regs {
AES2501_REG_CTRL1 = 0x80,
AES2501_REG_CTRL2 = 0x81,
AES2501_REG_EXCITCTRL = 0x82, /* excitation control */
AES2501_REG_DETCTRL = 0x83, /* detect control */
AES2501_REG_COLSCAN = 0x88, /* column scan rate register */
AES2501_REG_MEASDRV = 0x89, /* measure drive */
AES2501_REG_MEASFREQ = 0x8a, /* measure frequency */
AES2501_REG_DEMODPHASE1 = 0x8d,
AES2501_REG_DEMODPHASE2 = 0x8c,
AES2501_REG_CHANGAIN = 0x8e, /* channel gain */
AES2501_REG_ADREFHI = 0x91, /* A/D reference high */
AES2501_REG_ADREFLO = 0x92, /* A/D reference low */
AES2501_REG_STRTROW = 0x93, /* start row */
AES2501_REG_ENDROW = 0x94, /* end row */
AES2501_REG_STRTCOL = 0x95, /* start column */
AES2501_REG_ENDCOL = 0x96, /* end column */
AES2501_REG_DATFMT = 0x97, /* data format */
AES2501_REG_IMAGCTRL = 0x98, /* image data */
AES2501_REG_STAT = 0x9a,
AES2501_REG_CHWORD1 = 0x9b, /* challenge word 1 */
AES2501_REG_CHWORD2 = 0x9c,
AES2501_REG_CHWORD3 = 0x9d,
AES2501_REG_CHWORD4 = 0x9e,
AES2501_REG_CHWORD5 = 0x9f,
AES2501_REG_TREG1 = 0xa1, /* test register 1 */
AES2501_REG_AUTOCALOFFSET = 0xa8,
AES2501_REG_TREGC = 0xac,
AES2501_REG_TREGD = 0xad,
AES2501_REG_LPONT = 0xb4, /* low power oscillator on time */
};
#define FIRST_AES2501_REG AES2501_REG_CTRL1
#define LAST_AES2501_REG AES2501_REG_CHWORD5
#define AES2501_CTRL1_MASTER_RESET (1<<0)
#define AES2501_CTRL1_SCAN_RESET (1<<1) /* stop + restart scan sequencer */
/* 1 = continuously updated, 0 = updated prior to starting a scan */
#define AES2501_CTRL1_REG_UPDATE (1<<2)
/* 1 = continuous scans, 0 = single scans */
#define AES2501_CTRL2_CONTINUOUS 0x01
#define AES2501_CTRL2_READ_REGS 0x02 /* dump registers */
#define AES2501_CTRL2_SET_ONE_SHOT 0x04
#define AES2501_CTRL2_CLR_ONE_SHOT 0x08
#define AES2501_CTRL2_READ_ID 0x10
enum aes2501_detection_rate {
/* rate of detection cycles: */
AES2501_DETCTRL_DRATE_CONTINUOUS = 0x00, /* continuously */
AES2501_DETCTRL_DRATE_16_MS = 0x01, /* every 16.62ms */
AES2501_DETCTRL_DRATE_31_MS = 0x02, /* every 31.24ms */
AES2501_DETCTRL_DRATE_62_MS = 0x03, /* every 62.50ms */
AES2501_DETCTRL_DRATE_125_MS = 0x04, /* every 125.0ms */
AES2501_DETCTRL_DRATE_250_MS = 0x05, /* every 250.0ms */
AES2501_DETCTRL_DRATE_500_MS = 0x06, /* every 500.0ms */
AES2501_DETCTRL_DRATE_1_S = 0x07, /* every 1s */
};
enum aes2501_settling_delay {
AES2501_DETCTRL_SDELAY_31_MS = 0x00, /* 31.25ms */
AES2501_DETCTRL_SSDELAY_62_MS = 0x10, /* 62.5ms */
AES2501_DETCTRL_SSDELAY_125_MS = 0x20, /* 125ms */
AES2501_DETCTRL_SSDELAY_250_MS = 0x30 /* 250ms */
};
enum aes2501_col_scan_rate {
AES2501_COLSCAN_SRATE_32_US = 0x00, /* 32us */
AES2501_COLSCAN_SRATE_64_US = 0x01, /* 64us */
AES2501_COLSCAN_SRATE_128_US = 0x02, /* 128us */
AES2501_COLSCAN_SRATE_256_US = 0x03, /* 256us */
AES2501_COLSCAN_SRATE_512_US = 0x04, /* 512us */
AES2501_COLSCAN_SRATE_1024_US = 0x05, /* 1024us */
AES2501_COLSCAN_SRATE_2048_US = 0x06, /* 2048us */
};
enum aes2501_mesure_drive {
AES2501_MEASDRV_MDRIVE_0_325 = 0x00, /* 0.325 Vpp */
AES2501_MEASDRV_MDRIVE_0_65 = 0x01, /* 0.65 Vpp */
AES2501_MEASDRV_MDRIVE_1_3 = 0x02, /* 1.3 Vpp */
AES2501_MEASDRV_MDRIVE_2_6 = 0x03 /* 2.6 Vpp */
};
/* Select (1=square | 0=sine) wave drive during measure */
#define AES2501_MEASDRV_SQUARE 0x20
/* 0 = use measure drive setting, 1 = when sine wave is selected */
#define AES2501_MEASDRV_MEASURE_SQUARE 0x10
enum aes2501_measure_freq {
AES2501_MEASFREQ_125K = 0x01, /* 125 kHz */
AES2501_MEASFREQ_250K = 0x02, /* 250 kHz */
AES2501_MEASFREQ_500K = 0x03, /* 500 kHz */
AES2501_MEASFREQ_1M = 0x04, /* 1 MHz */
AES2501_MEASFREQ_2M = 0x05 /* 2 MHz */
};
#define DEMODPHASE_NONE 0x00
#define DEMODPHASE_180_00 0x40 /* 180 degrees */
#define DEMODPHASE_2_81 0x01 /* 2.8125 degrees */
#define AES2501_REG_DEMODPHASE1 0x8d
#define DEMODPHASE_1_40 0x40 /* 1.40625 degrees */
#define DEMODPHASE_0_02 0x01 /* 0.02197256 degrees */
enum aes2501_sensor_gain1 {
AES2501_CHANGAIN_STAGE1_2X = 0x00, /* 2x */
AES2501_CHANGAIN_STAGE1_4X = 0x01, /* 4x */
AES2501_CHANGAIN_STAGE1_8X = 0x02, /* 8x */
AES2501_CHANGAIN_STAGE1_16X = 0x03 /* 16x */
};
enum aes2501_sensor_gain2 {
AES2501_CHANGAIN_STAGE2_2X = 0x00, /* 2x */
AES2501_CHANGAIN_STAGE2_4X = 0x10, /* 4x */
AES2501_CHANGAIN_STAGE2_8X = 0x20, /* 8x */
AES2501_CHANGAIN_STAGE2_16X = 0x30 /* 16x */
};
#define AES2501_DATFMT_EIGHT 0x40 /* 1 = 8-bit data, 0 = 4-bit data */
#define AES2501_DATFMT_LOW_RES 0x20
#define AES2501_DATFMT_BIN_IMG 0x10
/* don't send image or authentication messages when imaging */
#define AES2501_IMAGCTRL_IMG_DATA_DISABLE 0x01
/* send histogram when imaging */
#define AES2501_IMAGCTRL_HISTO_DATA_ENABLE 0x02
/* send histogram at end of each row rather than each scan */
#define AES2501_IMAGCTRL_HISTO_EACH_ROW 0x04
/* send full image array rather than 64x64 center */
#define AES2501_IMAGCTRL_HISTO_FULL_ARRAY 0x08
/* return registers before data (rather than after) */
#define AES2501_IMAGCTRL_REG_FIRST 0x10
/* return test registers with register dump */
#define AES2501_IMAGCTRL_TST_REG_ENABLE 0x20
#define AES2501_CHWORD1_IS_FINGER 0x01 /* If set, finger is present */
/* Enable the reading of the register in TREGD */
#define AES2501_TREGC_ENABLE 0x01
#define AES2501_LPONT_MIN_VALUE 0x00 /* 0 ms */
#define AES2501_LPONT_MAX_VALUE 0x1f /* About 16 ms */
#define AES2501_ADREFHI_MIN_VALUE 0x28
#define AES2501_ADREFHI_MAX_VALUE 0x58
#define AES2501_SUM_HIGH_THRESH 1000
#define AES2501_SUM_LOW_THRESH 700
#endif /* __AES2501_H */

View File

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

View File

@ -1,114 +0,0 @@
/*
* AuthenTec AES2550/AES2810 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AES2550_H
#define __AES2550_H
/* Registers bits */
#define AES2550_REG80_MASTER_RESET (1 << 0)
#define AES2550_REG80_FORCE_FINGER_PRESENT (1 << 1)
#define AES2550_REG80_LPO_START (1 << 2)
#define AES2550_REG80_HGC_ENABLE (1 << 3)
#define AES2550_REG80_SENSOR_MODE_OFS (4)
#define AES2550_REG80_AUTO_RESTART_FD (1 << 6)
#define AES2550_REG80_EXT_REG_ENABLE (1 << 7)
#define AES2550_REG81_CONT_SCAN (1 << 0)
#define AES2550_REG81_READ_REG (1 << 1)
#define AES2550_REG81_NSHOT (1 << 2)
#define AES2550_REG81_RUN_FD (1 << 3)
#define AES2550_REG81_READ_ID (1 << 4)
#define AES2550_REG81_RUN_CAL (1 << 5)
#define AES2550_REG81_RUN_TIMER (1 << 6)
#define AES2550_REG81_RUN_BIST (1 << 7)
#define AES2550_REG83_FINGER_PRESENT (1 << 7)
#define AES2550_REG85_FLUSH_PER_FRAME (1 << 7)
#define AES2550_REG8F_EDATA_DISABLE (1 << 1)
#define AES2550_REG8F_AUTH_DISABLE (1 << 2)
#define AES2550_REG8F_EHISTO_DISABLE (1 << 3)
#define AES2550_REG8F_HISTO64 (1 << 4)
#define AES2550_REG8F_SINGLE_REG_ENABLE (1 << 6)
#define AES2550_REG95_COL_SCANNED_OFS (0)
#define AES2550_REG95_EPIX_AVG_OFS (4)
#define AES2550_REGA8_DIG_BIT_DATA_OFS (0)
#define AES2550_REGA8_DIG_BIT_EN (1 << 4)
#define AES2550_REGA8_FIXED_BIT_DATA (1 << 5)
#define AES2550_REGA8_INVERT_BIT_DATA (1 << 6)
#define AES2550_REGAD_LPFD_AVG_OFS (0)
#define AES2550_REGAD_DETECT_FGROFF (1 << 4)
#define AES2550_REGAD_ADVRANGE_2V (1 << 6)
#define AES2550_REGB1_ATE_CONT_IMAGE (1 << 1)
#define AES2550_REGB1_ANALOG_RESET (1 << 2)
#define AES2550_REGB1_ANALOG_PD (1 << 3)
#define AES2550_REGB1_TEST_EMBD_WORD (1 << 4)
#define AES2550_REGB1_ORIG_EMBD_WORD (1 << 5)
#define AES2550_REGB1_RESET_UHSM (1 << 6)
#define AES2550_REGB1_RESET_SENSOR (1 << 7)
#define AES2550_REGBD_LPO_IN_15_8_OFS (0)
#define AES2550_REGBE_LPO_IN_7_0_OFS (0)
#define AES2550_REGBF_RSR_LEVEL_DISABLED (0 << 0)
#define AES2550_REGBF_RSR_LEVEL_LEADING_RSR (1 << 0)
#define AES2550_REGBF_RSR_LEVEL_SIMPLE_RSR (2 << 0)
#define AES2550_REGBF_RSR_LEVEL_SUPER_RSR (3 << 0)
#define AES2550_REGBF_RSR_DIR_DOWN_MOTION (0 << 2)
#define AES2550_REGBF_RSR_DIR_UP_MOTION (1 << 2)
#define AES2550_REGBF_RSR_DIR_UPDOWN_MOTION (2 << 2)
#define AES2550_REGBF_NOISE_FLOOR_MODE (1 << 4)
#define AES2550_REGBF_QUADRATURE_MODE (1 << 5)
#define AES2550_REGCF_INTERFERENCE_CHK_EN (1 << 0)
#define AES2550_REGCF_INTERFERENCE_AVG_EN (1 << 1)
#define AES2550_REGCF_INTERFERENCE_AVG_OFFS (4)
#define AES2550_REGDC_BP_NUM_REF_SWEEP_OFS (0)
#define AES2550_REGDC_DEBUG_CTRL2_OFS (3)
#define AES2550_REGDD_DEBUG_CTRL1_OFS (0)
/* Commands */
enum aes2550_cmds {
AES2550_CMD_SET_IDLE_MODE = 0x00,
AES2550_CMD_RUN_FD = 0x01,
AES2550_CMD_GET_ENROLL_IMG = 0x02,
AES2550_CMD_CALIBRATE = 0x06,
AES2550_CMD_READ_CALIBRATION_DATA = 0x10,
AES2550_CMD_HEARTBEAT = 0x70,
};
/* Messages */
#define AES2550_STRIP_SIZE (0x31e + 3)
#define AES2550_HEARTBEAT_SIZE (4 + 3)
#define AES2550_EDATA_MAGIC 0xe0
#define AES2550_HEARTBEAT_MAGIC 0xdb
#define AES2550_EP_IN_BUF_SIZE 8192
#endif

View File

@ -1,114 +0,0 @@
/*
* AuthenTec AES2660 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "aes2660"
#include "drivers_api.h"
#include "aeslib.h"
#include "aesx660.h"
#include "aes2660.h"
#define FRAME_WIDTH 192
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = AESX660_FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = g_malloc0(sizeof(struct aesX660_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
aesdev->buffer = g_malloc0(AES2660_FRAME_SIZE + AESX660_HEADER_SIZE);
/* No scaling for AES2660 */
aesdev->init_seqs[0] = aes2660_init_1;
aesdev->init_seqs_len[0] = G_N_ELEMENTS(aes2660_init_1);
aesdev->init_seqs[1] = aes2660_init_2;
aesdev->init_seqs_len[1] = G_N_ELEMENTS(aes2660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes2660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes2660_start_imaging_cmd);
aesdev->assembling_ctx = &assembling_ctx;
aesdev->extra_img_flags = FP_IMG_PARTIAL;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev;
aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x2660 },
{ .vendor = 0x08ff, .product = 0x2680 },
{ .vendor = 0x08ff, .product = 0x2681 },
{ .vendor = 0x08ff, .product = 0x2682 },
{ .vendor = 0x08ff, .product = 0x2683 },
{ .vendor = 0x08ff, .product = 0x2684 },
{ .vendor = 0x08ff, .product = 0x2685 },
{ .vendor = 0x08ff, .product = 0x2686 },
{ .vendor = 0x08ff, .product = 0x2687 },
{ .vendor = 0x08ff, .product = 0x2688 },
{ .vendor = 0x08ff, .product = 0x2689 },
{ .vendor = 0x08ff, .product = 0x268a },
{ .vendor = 0x08ff, .product = 0x268b },
{ .vendor = 0x08ff, .product = 0x268c },
{ .vendor = 0x08ff, .product = 0x268d },
{ .vendor = 0x08ff, .product = 0x268e },
{ .vendor = 0x08ff, .product = 0x268f },
{ .vendor = 0x08ff, .product = 0x2691 },
{ 0, 0, 0, },
};
struct fp_img_driver aes2660_driver = {
.driver = {
.id = AES2660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2660",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,
.activate = aesX660_dev_activate,
.deactivate = aesX660_dev_deactivate,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,183 +0,0 @@
/*
* AuthenTec AES3500 driver for libfprint
*
* AES3500 is a press-typed sensor, which captures image in 128x128
* pixels.
*
* Thanks Rafael Toledo for the Windows driver and the help.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2011-2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#define FP_COMPONENT "aes3500"
#include "drivers_api.h"
#include "aeslib.h"
#include "aes3k.h"
#define DATA_BUFLEN 0x2089
/* image size = FRAME_WIDTH x FRAME_WIDTH */
#define FRAME_WIDTH 128
#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
#define ENLARGE_FACTOR 2
static struct aes_regwrite init_reqs[] = {
/* master reset */
{ 0x80, 0x01 },
{ 0, 0 },
{ 0x80, 0x00 },
{ 0, 0 },
{ 0x81, 0x00 },
{ 0x80, 0x00 },
{ 0, 0 },
/* scan reset */
{ 0x80, 0x02 },
{ 0, 0 },
{ 0x80, 0x00 },
{ 0, 0 },
/* disable register buffering */
{ 0x80, 0x04 },
{ 0, 0 },
{ 0x80, 0x00 },
{ 0, 0 },
{ 0x81, 0x00 },
{ 0, 0 },
/* windows driver reads registers now (81 02) */
{ 0x80, 0x00 },
{ 0x81, 0x00 },
/* set excitation bias current: 2mhz drive ring frequency,
* 4V drive ring voltage, 16.5mA excitation bias */
{ 0x82, 0x04 },
/* continuously sample drive ring for finger detection,
* 62.50ms debounce delay */
{ 0x83, 0x13 },
{ 0x84, 0x07 }, /* set calibration resistance to 12 kiloohms */
{ 0x85, 0x3d }, /* set calibration capacitance */
{ 0x86, 0x03 }, /* detect drive voltage */
{ 0x87, 0x01 }, /* set detection frequency to 125khz */
{ 0x88, 0x02 }, /* set column scan period */
{ 0x89, 0x02 }, /* set measure drive */
{ 0x8a, 0x33 }, /* set measure frequency and sense amplifier bias */
{ 0x8b, 0x33 }, /* set matrix pattern */
{ 0x8c, 0x0f }, /* set demodulation phase 1 */
{ 0x8d, 0x04 }, /* set demodulation phase 2 */
{ 0x8e, 0x23 }, /* set sensor gain */
{ 0x8f, 0x07 }, /* set image parameters */
{ 0x90, 0x00 }, /* carrier offset null */
{ 0x91, 0x1c }, /* set A/D reference high */
{ 0x92, 0x08 }, /* set A/D reference low */
{ 0x93, 0x00 }, /* set start row to 0 */
{ 0x94, 0x07 }, /* set end row */
{ 0x95, 0x00 }, /* set start column to 0 */
{ 0x96, 0x1f }, /* set end column */
{ 0x97, 0x04 }, /* data format and thresholds */
{ 0x98, 0x28 }, /* image data control */
{ 0x99, 0x00 }, /* disable general purpose outputs */
{ 0x9a, 0x0b }, /* set initial scan state */
{ 0x9b, 0x00 }, /* clear challenge word bits */
{ 0x9c, 0x00 }, /* clear challenge word bits */
{ 0x9d, 0x09 }, /* set some challenge word bits */
{ 0x9e, 0x53 }, /* clear challenge word bits */
{ 0x9f, 0x6b }, /* set some challenge word bits */
{ 0, 0 },
{ 0x80, 0x00 },
{ 0x81, 0x00 },
{ 0, 0 },
{ 0x81, 0x04 },
{ 0, 0 },
{ 0x81, 0x00 },
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
struct aes3k_dev *aesdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = g_malloc0(sizeof(struct aes3k_dev));
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
if (!aesdev)
return -ENOMEM;
aesdev->data_buflen = DATA_BUFLEN;
aesdev->frame_width = FRAME_WIDTH;
aesdev->frame_size = FRAME_SIZE;
aesdev->frame_number = FRAME_NUMBER;
aesdev->enlarge_factor = ENLARGE_FACTOR;
aesdev->init_reqs = init_reqs;
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
return r;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(aesdev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x5731 },
{ 0, 0, 0, },
};
struct fp_img_driver aes3500_driver = {
.driver = {
.id = AES3500_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES3500",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
.img_height = FRAME_WIDTH * ENLARGE_FACTOR,
.img_width = FRAME_WIDTH * ENLARGE_FACTOR,
/* temporarily lowered until image quality improves */
.bz3_threshold = 9,
.open = dev_init,
.close = dev_deinit,
.activate = aes3k_dev_activate,
.deactivate = aes3k_dev_deactivate,
};

View File

@ -1,158 +0,0 @@
/*
* AuthenTec AES3500/AES4000 common routines
*
* The AES3500 and AES4000 sensors are press-typed, and could capture
* fingerprint images in 128x128 and 96x96 pixels respectively. They
* share a same communication interface: a number of frames are
* transferred and captured, from which a final image could be
* assembled. Each frame has fixed height of 16 pixels.
*
* As the imaging area is a bit small, only a part of finger could be
* captured, the detected minutiae are not so many that the NBIS
* matching works not so good. The verification rate is very low at the
* moment.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#define FP_COMPONENT "aes3k"
#include "drivers_api.h"
#include "aeslib.h"
#include "aes3k.h"
#define CTRL_TIMEOUT 1000
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
static void do_capture(struct fp_img_dev *dev);
static void aes3k_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output)
{
size_t row, column;
for (column = 0; column < width; column++) {
for (row = 0; row < height; row += 2) {
output[width * row + column] = (*input & 0x0f) * 17;
output[width * (row + 1) + column] = ((*input & 0xf0) >> 4) * 17;
input++;
}
}
}
static void img_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *ptr = transfer->buffer;
struct fp_img *tmp;
struct fp_img *img;
int i;
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
goto err;
} else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_imgdev_session_error(dev, -EIO);
goto err;
} else if (transfer->length != transfer->actual_length) {
fpi_imgdev_session_error(dev, -EPROTO);
goto err;
}
fpi_imgdev_report_finger_status(dev, TRUE);
tmp = fpi_img_new(aesdev->frame_width * aesdev->frame_width);
tmp->width = aesdev->frame_width;
tmp->height = aesdev->frame_width;
tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
for (i = 0; i < aesdev->frame_number; i++) {
fp_dbg("frame header byte %02x", *ptr);
ptr++;
aes3k_assemble_image(ptr, aesdev->frame_width, AES3K_FRAME_HEIGHT, tmp->data + (i * aesdev->frame_width * AES3K_FRAME_HEIGHT));
ptr += aesdev->frame_size;
}
/* FIXME: this is an ugly hack to make the image big enough for NBIS
* to process reliably */
img = fpi_img_resize(tmp, aesdev->enlarge_factor, aesdev->enlarge_factor);
fp_img_free(tmp);
fpi_imgdev_image_captured(dev, img);
/* FIXME: rather than assuming finger has gone, we should poll regs until
* it really has, then restart the capture */
fpi_imgdev_report_finger_status(dev, FALSE);
do_capture(dev);
err:
g_free(transfer->buffer);
aesdev->img_trf = NULL;
libusb_free_transfer(transfer);
}
static void do_capture(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data;
int r;
aesdev->img_trf = fpi_usb_alloc();
data = g_malloc(aesdev->data_buflen);
libusb_fill_bulk_transfer(aesdev->img_trf, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data,
aesdev->data_buflen, img_cb, dev, 0);
r = libusb_submit_transfer(aesdev->img_trf);
if (r < 0) {
g_free(data);
libusb_free_transfer(aesdev->img_trf);
aesdev->img_trf = NULL;
fpi_imgdev_session_error(dev, r);
}
}
static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
{
fpi_imgdev_activate_complete(dev, result);
if (result == 0)
do_capture(dev);
}
int aes3k_dev_activate(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
aes_write_regv(dev, aesdev->init_reqs, aesdev->init_reqs_len, init_reqs_cb, NULL);
return 0;
}
void aes3k_dev_deactivate(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
/* FIXME: should wait for cancellation to complete before returning
* from deactivation, otherwise app may legally exit before we've
* cleaned up */
if (aesdev->img_trf)
libusb_cancel_transfer(aesdev->img_trf);
fpi_imgdev_deactivate_complete(dev);
}

View File

@ -1,58 +0,0 @@
/*
* AuthenTec AES3500/AES4000 common routines
*
* The AES3500 and AES4000 sensors are press-typed, and could capture
* fingerprint images in 128x128 and 96x96 pixels respectively. They
* share a same communication interface: a number of frames are
* transferred and captured, from which a final image could be
* assembled. Each frame has fixed height of 16 pixels.
*
* As the imaging area is a bit small, only a part of finger could be
* captured, the detected minutiae are not so many that the NBIS
* matching works not so good. The verification rate is very low at the
* moment.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __AES3K_H
#define __AES3K_H
#define AES3K_FRAME_HEIGHT 16
struct aes3k_dev {
struct libusb_transfer *img_trf;
size_t frame_width; /* image size = frame_width x frame_width */
size_t frame_size; /* 4 bits/pixel: frame_width x AES3K_FRAME_HEIGHT / 2 */
size_t frame_number; /* number of frames */
size_t enlarge_factor;
size_t data_buflen; /* buffer length of usb bulk transfer */
struct aes_regwrite *init_reqs; /* initial values sent to device */
size_t init_reqs_len;
};
int aes3k_dev_activate(struct fp_img_dev *dev);
void aes3k_dev_deactivate(struct fp_img_dev *dev);
#endif

View File

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

View File

@ -1,177 +0,0 @@
/*
* Shared functions between libfprint Authentec drivers
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "aeslib"
#include "fp_internal.h"
#include <errno.h>
#include <string.h>
#include <glib.h>
#include "fpi-usb.h"
#include "fpi-assembling.h"
#include "aeslib.h"
#define MAX_REGWRITES_PER_REQUEST 16
#define BULK_TIMEOUT 4000
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
struct write_regv_data {
struct fp_img_dev *imgdev;
unsigned int num_regs;
const struct aes_regwrite *regs;
unsigned int offset;
aes_write_regv_cb callback;
void *user_data;
};
static void continue_write_regv(struct write_regv_data *wdata);
/* libusb bulk callback for regv write completion transfer. continues the
* transaction */
static void write_regv_trf_complete(struct libusb_transfer *transfer)
{
struct write_regv_data *wdata = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
wdata->callback(wdata->imgdev, -EIO, wdata->user_data);
g_free(wdata);
} else if (transfer->length != transfer->actual_length) {
wdata->callback(wdata->imgdev, -EPROTO, wdata->user_data);
g_free(wdata);
} else {
continue_write_regv(wdata);
}
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
/* write from wdata->offset to upper_bound (inclusive) of wdata->regs */
static int do_write_regv(struct write_regv_data *wdata, int upper_bound)
{
unsigned int offset = wdata->offset;
unsigned int num = upper_bound - offset + 1;
size_t alloc_size = num * 2;
unsigned char *data = g_malloc(alloc_size);
unsigned int i;
size_t data_offset = 0;
struct libusb_transfer *transfer = fpi_usb_alloc();
int r;
for (i = offset; i < offset + num; i++) {
const struct aes_regwrite *regwrite = &wdata->regs[i];
data[data_offset++] = regwrite->reg;
data[data_offset++] = regwrite->value;
}
libusb_fill_bulk_transfer(transfer, FP_DEV(wdata->imgdev)->udev, EP_OUT, data,
alloc_size, write_regv_trf_complete, wdata, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
}
return r;
}
/* write the next batch of registers to be written, or if there are no more,
* indicate completion to the caller */
static void continue_write_regv(struct write_regv_data *wdata)
{
unsigned int offset = wdata->offset;
unsigned int regs_remaining;
unsigned int limit;
unsigned int upper_bound;
int i;
int r;
/* skip all zeros and ensure there is still work to do */
while (TRUE) {
if (offset >= wdata->num_regs) {
fp_dbg("all registers written");
wdata->callback(wdata->imgdev, 0, wdata->user_data);
g_free(wdata);
return;
}
if (wdata->regs[offset].reg)
break;
offset++;
}
wdata->offset = offset;
regs_remaining = wdata->num_regs - offset;
limit = MIN(regs_remaining, MAX_REGWRITES_PER_REQUEST);
upper_bound = offset + limit - 1;
/* determine if we can write the entire of the regs at once, or if there
* is a zero dividing things up */
for (i = offset; i <= upper_bound; i++)
if (!wdata->regs[i].reg) {
upper_bound = i - 1;
break;
}
r = do_write_regv(wdata, upper_bound);
if (r < 0) {
wdata->callback(wdata->imgdev, r, wdata->user_data);
g_free(wdata);
return;
}
wdata->offset = upper_bound + 1;
}
/* write a load of registers to the device, combining multiple writes in a
* single URB up to a limit. insert writes to non-existent register 0 to force
* specific groups of writes to be separated by different URBs. */
void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
unsigned int num_regs, aes_write_regv_cb callback, void *user_data)
{
struct write_regv_data *wdata;
fp_dbg("write %d regs", num_regs);
wdata = g_malloc(sizeof(*wdata));
wdata->imgdev = dev;
wdata->num_regs = num_regs;
wdata->regs = regs;
wdata->offset = 0;
wdata->callback = callback;
wdata->user_data = user_data;
continue_write_regv(wdata);
}
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y)
{
unsigned char ret;
ret = frame->data[x * (ctx->frame_height >> 1) + (y >> 1)];
ret = y % 2 ? ret >> 4 : ret & 0xf;
ret *= 17;
return ret;
}

View File

@ -1,45 +0,0 @@
/*
* Shared functions between libfprint Authentec drivers
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AESLIB_H__
#define __AESLIB_H__
#include <fprint.h>
struct aes_regwrite {
unsigned char reg;
unsigned char value;
};
struct fpi_frame;
struct fpi_frame_asmbl_ctx;
typedef void (*aes_write_regv_cb)(struct fp_img_dev *dev, int result,
void *user_data);
void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
unsigned int num_regs, aes_write_regv_cb callback, void *user_data);
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y);
#endif

View File

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

View File

@ -1,122 +0,0 @@
/*
* AuthenTec AES1660/AES2660 common definitions
* Copyright (c) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AESX660_H
#define __AESX660_H
#define AESX660_HEADER_SIZE 3
#define AESX660_RESPONSE_TYPE_OFFSET 0x00
#define AESX660_RESPONSE_SIZE_LSB_OFFSET 0x01
#define AESX660_RESPONSE_SIZE_MSB_OFFSET 0x02
#define AESX660_CALIBRATE_RESPONSE 0x06
#define AESX660_FINGER_DET_RESPONSE 0x40
#define AESX660_FINGER_PRESENT_OFFSET 0x03
#define AESX660_FINGER_PRESENT 0x01
#define AESX660_IMAGE_OK_OFFSET 0x03
#define AESX660_IMAGE_OK 0x0d
#define AESX660_LAST_FRAME_OFFSET 0x04
#define AESX660_LAST_FRAME_BIT 0x01
#define AESX660_FRAME_DELTA_X_OFFSET 16
#define AESX660_FRAME_DELTA_Y_OFFSET 17
#define AESX660_IMAGE_OFFSET 43
#define AESX660_BULK_TRANSFER_SIZE 4096
#define AESX660_FRAME_HEIGHT 8
struct aesX660_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
struct aesX660_cmd *init_seq;
size_t init_seq_len;
unsigned int init_cmd_idx;
unsigned int init_seq_idx;
struct libusb_transfer *fd_data_transfer;
unsigned char *buffer;
size_t buffer_size;
size_t buffer_max;
/* Device-specific stuff */
struct aesX660_cmd *init_seqs[2];
size_t init_seqs_len[2];
unsigned char *start_imaging_cmd;
size_t start_imaging_cmd_len;
struct fpi_frame_asmbl_ctx *assembling_ctx;
uint16_t extra_img_flags;
};
struct aesX660_cmd {
const unsigned char *cmd;
size_t len;
};
/* 0x77 cmd seems to control LED, this sequence
* makes LED blink
*/
static const unsigned char led_blink_cmd[] = {
0x77, 0x18, 0x00,
0x00, 0x3f, 0x00, 0xff, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0xf3, 0x01, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xf3,
0x01, 0x00, 0x7f
};
/* This sequence makes LED light solid
*/
static const unsigned char led_solid_cmd[] = {
0x77, 0x18, 0x00, 0x00, 0x3f, 0x00, 0xff, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0xe7, 0x03, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f
};
static const unsigned char wait_for_finger_cmd[] = {
0x20,
0x40, 0x04, 0x00, 0x02, 0x1e, 0x00, 0x32
};
/* 0x40 cmd response
*
static const unsigned char pkt1371[] = {
0x40, 0x01, 0x00, 0x01
};
*/
static const unsigned char set_idle_cmd[] = {
0x0d, /* Reset or "set idle"? */
};
static const unsigned char read_id_cmd[] = {
0x44, 0x02, 0x00, 0x08, 0x00, /* Max transfer size is 8 */
0x07, /* Read ID? */
};
static const unsigned char calibrate_cmd[] = {
0x44, 0x02, 0x00, 0x04, 0x00,
0x06,
};
int aesX660_dev_activate(struct fp_img_dev *dev);
void aesX660_dev_deactivate(struct fp_img_dev *dev);
#endif

View File

@ -1,47 +0,0 @@
/*
* Driver IDs
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __DRIVER_IDS
#define __DRIVER_IDS
enum {
UPEKTS_ID = 1,
URU4000_ID = 2,
AES4000_ID = 3,
AES2501_ID = 4,
UPEKTC_ID = 5,
AES1610_ID = 6,
/* FDU2000_ID = 7, */
VCOM5S_ID = 8,
UPEKSONLY_ID = 9,
VFS101_ID = 10,
VFS301_ID = 11,
AES2550_ID = 12,
/* UPEKE2_ID = 13 */
AES1660_ID = 14,
AES2660_ID = 15,
AES3500_ID = 16,
UPEKTC_IMG_ID = 17,
ETES603_ID = 18,
VFS5011_ID = 19,
VFS0050_ID = 20,
ELAN_ID = 21,
};
#endif

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,24 +0,0 @@
/*
* LGPL CRC code copied from GStreamer-0.10.10:
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version
* 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include <stddef.h>
uint16_t udf_crc(unsigned char *buffer, size_t size);

File diff suppressed because it is too large Load Diff

View File

@ -1,319 +0,0 @@
/*
* UPEK TouchStrip Sensor-Only driver for libfprint
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* TCS4C (USB ID 147e:1000) support:
* Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
*
* TCRD5B (USB ID 147e:1001) support:
* Copyright (C) 2014 Vasily Khoruzhick <anarsoul@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define IMG_WIDTH_2016 288
#define IMG_WIDTH_1000 288
#define IMG_WIDTH_1001 216
struct sonly_regwrite {
uint8_t reg;
uint8_t value;
};
/***** AWAIT FINGER *****/
static const struct sonly_regwrite awfsm_2016_writev_1[] = {
{ 0x0a, 0x00 }, { 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x3b },
{ 0x00, 0x67 }, { 0x00, 0x67 },
};
static const struct sonly_regwrite awfsm_1000_writev_1[] = {
/* Initialize sensor settings */
{ 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x37 }, { 0x00, 0x5f },
{ 0x01, 0x6e }, { 0x01, 0xee }, { 0x0c, 0x13 }, { 0x0d, 0x0d },
{ 0x0e, 0x0e }, { 0x0f, 0x0d },
{ 0x13, 0x05 }, { 0x13, 0x45 },
/* Initialize finger detection registers (not enabling yet) */
{ 0x30, 0xe0 }, { 0x15, 0x26 },
{ 0x12, 0x01 }, { 0x20, 0x01 }, { 0x07, 0x10 },
{ 0x10, 0x00 }, { 0x11, 0xbf },
};
static const struct sonly_regwrite awfsm_2016_writev_2[] = {
{ 0x01, 0xc6 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x0b, 0x00 },
};
static const struct sonly_regwrite awfsm_1000_writev_2[] = {
/* Enable finger detection */
{ 0x30, 0xe1 }, { 0x15, 0x06 }, { 0x15, 0x86 },
};
static const struct sonly_regwrite awfsm_2016_writev_3[] = {
{ 0x13, 0x45 }, { 0x30, 0xe0 }, { 0x12, 0x01 }, { 0x20, 0x01 },
{ 0x09, 0x20 }, { 0x0a, 0x00 }, { 0x30, 0xe0 }, { 0x20, 0x01 },
};
static const struct sonly_regwrite awfsm_2016_writev_4[] = {
{ 0x08, 0x00 }, { 0x10, 0x00 }, { 0x12, 0x01 }, { 0x11, 0xbf },
{ 0x12, 0x01 }, { 0x07, 0x10 }, { 0x07, 0x10 }, { 0x04, 0x00 },\
{ 0x05, 0x00 }, { 0x0b, 0x00 },
/* enter finger detection mode */
{ 0x15, 0x20 }, { 0x30, 0xe1 }, { 0x15, 0x24 }, { 0x15, 0x04 },
{ 0x15, 0x84 },
};
/***** CAPTURE MODE *****/
static const struct sonly_regwrite capsm_2016_writev[] = {
/* enter capture mode */
{ 0x09, 0x28 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, { 0x04, 0x00 },
{ 0x05, 0x00 },
};
static const struct sonly_regwrite capsm_1000_writev[] = {
{ 0x08, 0x80 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, /* Enter capture mode */
};
static const struct sonly_regwrite capsm_1001_writev_1[] = {
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4e, 0x05 },
};
static const struct sonly_regwrite capsm_1001_writev_2[] = {
{ 0x4d, 0xc0 }, { 0x4e, 0x09 },
};
static const struct sonly_regwrite capsm_1001_writev_3[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x0b, 0x00 },
{ 0x04, 0x00 },
{ 0x05, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4d, 0x40 }, { 0x4e, 0x09 },
};
static const struct sonly_regwrite capsm_1001_writev_4[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4e, 0x08 },
};
static const struct sonly_regwrite capsm_1001_writev_5[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x00, 0x5f }, { 0x01, 0xee },
{ 0x03, 0x2c },
{ 0x07, 0x00 }, { 0x08, 0x00 }, { 0x09, 0x29 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x10, 0x00 }, { 0x11, 0x8f }, { 0x12, 0x01 }, { 0x13, 0x45 },
{ 0x15, 0x26 },
{ 0x1e, 0x02 },
{ 0x20, 0x01 },
{ 0x25, 0x8f },
{ 0x27, 0x23 },
{ 0x30, 0xe0 },
{ 0x07, 0x10 },
{ 0x09, 0x21 },
{ 0x13, 0x75 },
{ 0x0b, 0x80 },
};
/***** DEINITIALIZATION *****/
static const struct sonly_regwrite deinitsm_2016_writev[] = {
/* reset + enter low power mode */
{ 0x0b, 0x00 }, { 0x09, 0x20 }, { 0x13, 0x45 }, { 0x13, 0x45 },
};
static const struct sonly_regwrite deinitsm_1000_writev[] = {
{ 0x15, 0x26 }, { 0x30, 0xe0 }, /* Disable finger detection */
{ 0x0b, 0x00 }, { 0x13, 0x45 }, { 0x08, 0x00 }, /* Disable capture mode */
};
static const struct sonly_regwrite deinitsm_1001_writev[] = {
{ 0x0b, 0x00 },
{ 0x13, 0x45 },
{ 0x09, 0x29 },
{ 0x1a, 0x00 },
};
/***** INITIALIZATION *****/
static const struct sonly_regwrite initsm_2016_writev_1[] = {
{ 0x49, 0x00 },
/* BSAPI writes different values to register 0x3e each time. I initially
* thought this was some kind of clever authentication, but just blasting
* these sniffed values each time seems to work. */
{ 0x3e, 0x83 }, { 0x3e, 0x4f }, { 0x3e, 0x0f }, { 0x3e, 0xbf },
{ 0x3e, 0x45 }, { 0x3e, 0x35 }, { 0x3e, 0x1c }, { 0x3e, 0xae },
{ 0x44, 0x01 }, { 0x43, 0x06 }, { 0x43, 0x05 }, { 0x43, 0x04 },
{ 0x44, 0x00 }, { 0x0b, 0x00 },
};
static const struct sonly_regwrite initsm_1000_writev_1[] = {
{ 0x49, 0x00 }, /* Encryption disabled */
/* Setting encryption key. Doesn't need to be random since we don't use any
* encryption. */
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x04, 0x00 }, { 0x05, 0x00 },
{ 0x0b, 0x00 }, { 0x08, 0x00 }, /* Initialize capture control registers */
};
static const struct sonly_regwrite initsm_1001_writev_1[] = {
{ 0x4a, 0x9d },
{ 0x4f, 0x06 },
{ 0x4f, 0x05 },
{ 0x4f, 0x04 },
{ 0x4a, 0x9c },
{ 0x3e, 0xa6 },
{ 0x3e, 0x01 },
{ 0x3e, 0x68 },
{ 0x3e, 0xfd },
{ 0x3e, 0x72 },
{ 0x3e, 0xef },
{ 0x3e, 0x5d },
{ 0x3e, 0xc5 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4c, 0x1f }, { 0x4d, 0xb8 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_2[] = {
{ 0x4c, 0x03 }, { 0x4d, 0xb8 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_3[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4c, 0xff }, { 0x4d, 0xc0 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_4[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x09, 0x27 },
{ 0x1a, 0x02 },
{ 0x49, 0x01 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x49, 0x00 },
{ 0x3e, 0x90 },
{ 0x3e, 0xbd },
{ 0x3e, 0xbf },
{ 0x3e, 0x48 },
{ 0x3e, 0x2a },
{ 0x3e, 0xe3 },
{ 0x3e, 0xd2 },
{ 0x3e, 0x58 },
{ 0x09, 0x2f },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4d, 0x40 }, { 0x4e, 0x03 },
};
static const struct sonly_regwrite initsm_1001_writev_5[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
};

View File

@ -1,478 +0,0 @@
/*
* UPEK TouchChip driver for libfprint
* Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "upektc"
#include "drivers_api.h"
#include "upektc.h"
#define UPEKTC_EP_IN (2 | LIBUSB_ENDPOINT_IN)
#define UPEKTC_EP_OUT (3 | LIBUSB_ENDPOINT_OUT)
#define UPEKET_EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define UPEKET_EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
struct upektc_dev {
gboolean deactivating;
const struct setup_cmd *setup_commands;
size_t setup_commands_len;
int ep_in;
int ep_out;
int init_idx;
int sum_threshold;
};
enum upektc_driver_data {
UPEKTC_2015,
UPEKTC_3001,
};
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
static void start_finger_detection(struct fp_img_dev *dev);
/****** INITIALIZATION/DEINITIALIZATION ******/
enum activate_states {
WRITE_INIT,
READ_DATA,
ACTIVATE_NUM_STATES,
};
static void
upektc_next_init_cmd(fpi_ssm *ssm,
struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
upekdev->init_idx += 1;
if (upekdev->init_idx == upekdev->setup_commands_len)
fpi_ssm_mark_completed(ssm);
else
fpi_ssm_jump_to_state(ssm, WRITE_INIT);
}
static void write_init_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
if (upekdev->setup_commands[upekdev->init_idx].response_len)
fpi_ssm_next_state(ssm);
else
upektc_next_init_cmd(ssm, dev);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void read_init_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
upektc_next_init_cmd(ssm, dev);
else
fpi_ssm_mark_failed(ssm, -EIO);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(_dev);
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_out,
(unsigned char*)upekdev->setup_commands[upekdev->init_idx].cmd,
UPEKTC_CMD_LEN, write_init_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, -ENOMEM);
}
}
break;
case READ_DATA:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
data = g_malloc(upekdev->setup_commands[upekdev->init_idx].response_len);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_in, data,
upekdev->setup_commands[upekdev->init_idx].response_len,
read_init_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
break;
}
}
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
/****** FINGER PRESENCE DETECTION ******/
static int finger_present(unsigned char *img, size_t len, int sum_threshold)
{
int i, sum;
sum = 0;
for (i = 0; i < len; i++) {
if (img[i] < 160) {
sum++;
}
}
fp_dbg("finger_present: sum is %d\n", sum);
return sum < sum_threshold ? 0 : 1;
}
static void finger_det_data_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
unsigned char *data = transfer->buffer;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("data transfer status %d\n", transfer->status);
fpi_imgdev_session_error(dev, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fp_dbg("expected %d, got %d bytes", transfer->length,
transfer->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
}
if (finger_present(data, IMAGE_SIZE, upekdev->sum_threshold)) {
/* finger present, start capturing */
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
} else {
/* no finger, poll for a new histogram */
start_finger_detection(dev);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_cmd_cb(struct libusb_transfer *t)
{
struct libusb_transfer *transfer;
unsigned char *data;
int r;
struct fp_img_dev *dev = t->user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("req transfer status %d\n", t->status);
fpi_imgdev_session_error(dev, -EIO);
goto exit_free_transfer;
} else if (t->length != t->actual_length) {
fp_dbg("expected %d, sent %d bytes", t->length, t->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
goto exit_free_transfer;
}
transfer = fpi_usb_alloc();
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_in, data, IMAGE_SIZE,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
exit_free_transfer:
libusb_free_transfer(t);
}
static void start_finger_detection(struct fp_img_dev *dev)
{
int r;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
struct libusb_transfer *transfer;
G_DEBUG_HERE();
if (upekdev->deactivating) {
complete_deactivation(dev);
return;
}
transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
finger_det_cmd_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
}
/****** CAPTURE ******/
enum capture_states {
CAPTURE_WRITE_CMD,
CAPTURE_READ_DATA,
CAPTURE_NUM_STATES,
};
static void capture_cmd_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_failed(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_read_data_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
unsigned char *data = transfer->buffer;
struct fp_img *img;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("request is not completed, %d", transfer->status);
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fp_dbg("expected %d, sent %d bytes", transfer->length, transfer->actual_length);
fpi_ssm_mark_failed(ssm, -EPROTO);
goto out;
}
img = fpi_img_new(IMAGE_SIZE);
memcpy(img->data, data, IMAGE_SIZE);
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(_dev);
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_CMD:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
capture_cmd_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, -ENOMEM);
}
}
break;
case CAPTURE_READ_DATA:
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_in, data, IMAGE_SIZE,
capture_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
break;
};
}
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct upektc_dev *upekdev = FP_INSTANCE_DATA(_dev);
fp_dbg("Capture completed");
if (upekdev->deactivating)
complete_deactivation(dev);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static void start_capture(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm;
if (upekdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
G_DEBUG_HERE();
fpi_ssm_start(ssm, capture_sm_complete);
}
static int dev_activate(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
ACTIVATE_NUM_STATES, dev);
upekdev->init_idx = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
upekdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
upekdev->deactivating = FALSE;
fpi_imgdev_deactivate_complete(dev);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct upektc_dev *upekdev;
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
upekdev = g_malloc0(sizeof(struct upektc_dev));
fp_dev_set_instance_data(FP_DEV(dev), upekdev);
switch (driver_data) {
case UPEKTC_2015:
upekdev->ep_in = UPEKTC_EP_IN;
upekdev->ep_out = UPEKTC_EP_OUT;
upekdev->setup_commands = upektc_setup_commands;
upekdev->setup_commands_len = G_N_ELEMENTS(upektc_setup_commands);
upekdev->sum_threshold = UPEKTC_SUM_THRESHOLD;
break;
case UPEKTC_3001:
upekdev->ep_in = UPEKET_EP_IN;
upekdev->ep_out = UPEKET_EP_OUT;
upekdev->setup_commands = upeket_setup_commands;
upekdev->setup_commands_len = G_N_ELEMENTS(upeket_setup_commands);
upekdev->sum_threshold = UPEKET_SUM_THRESHOLD;
break;
default:
fp_err("Device variant %lu is not known\n", driver_data);
g_free(upekdev);
fp_dev_set_instance_data(FP_DEV(dev), NULL);
return -ENODEV;
break;
}
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
void *user_data;
user_data = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(user_data);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x0483, .product = 0x2015, .driver_data = UPEKTC_2015 },
{ .vendor = 0x147e, .product = 0x3001, .driver_data = UPEKTC_3001 },
{ 0, 0, 0, },
};
struct fp_img_driver upektc_driver = {
.driver = {
.id = UPEKTC_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchChip/Eikon Touch 300",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
.img_height = IMAGE_HEIGHT,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 30,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,144 +0,0 @@
/*
* Upek TouchChip Fingerprint Coprocessor definitions
* Copyright (c) 2013 Vasily Khoruzhick <anarsoul@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __UPEKTC_IMG_H
#define __UPEKTC_IMG_H
static const unsigned char upek2020_init_1[] = {
'C', 'i', 'a', 'o',
0x04,
0x00, 0x0d,
0x01, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x00,
0xda, 0xc1
};
static const unsigned char upek2020_init_2[] = {
0x43, 0x69, 0x61, 0x6f,
0x07,
0x00, 0x01,
0x01,
0x3d, 0x72
};
static const unsigned char upek2020_init_3[] = {
'C', 'i', 'a', 'o',
0x04,
0x00, 0x0d,
0x01, 0x00, 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x00,
0x55, 0x2f
};
static const unsigned char upek2020_init_4[] = {
'C', 'i', 'a', 'o',
0x00,
0x00, 0x07,
0x28, 0x04, 0x00, 0x00, 0x00, 0x06, 0x04,
0xc0, 0xd6
};
static const unsigned char upek2020_deinit[] = {
'C', 'i', 'a', 'o',
0x07,
0x00, 0x01,
0x01,
0x3d,
0x72
};
static const unsigned char upek2020_init_capture[] = {
'C', 'i', 'a', 'o',
0x00,
0x00, 0x0e, /* Seq = 7, len = 0x00e */
0x28, /* CMD = 0x28 */
0x0b, 0x00, /* Inner len = 0x000b */
0x00, 0x00,
0x0e, /* SUBCMD = 0x0e */
0x02,
0xfe, 0xff, 0xff, 0xff, /* timeout = -2 = 0xfffffffe = infinite time */
0x02,
0x00, /* Wait for acceptable finger */
0x02,
0x14, 0x9a /* CRC */
};
#if 0
static const unsigned char finger_status[] = {
'C', 'i', 'a', 'o',
0x00,
0x70, 0x14, /* Seq = 7, len = 0x014 */
0x28, /* CMD = 0x28 */
0x11, 0x00, /* Inner len = 0x0011 */
0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
0x26, 0x03, /* CRC */
0x00, 0x00, 0x00, /* Rest is garbage */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif
static const unsigned char upek2020_ack_00_28[] = {
'C', 'i', 'a', 'o',
0x00,
0x80, 0x08, /* Seq = 8, len = 0x008 */
0x28, /* CMD = 0x28 */
0x05, 0x00, /* Inner len = 0x0005 */
0x00, 0x00, 0x00, 0x30, 0x01,
0x6a, 0xc4 /* CRC */
};
#if 0
/* No seq should be tracked here */
static const unsigned char got_finger[] = {
'C', 'i', 'a', 'o',
0x08,
0x00, 0x00, /* Seq = 0, len = 0x000 */
0xa1, 0xa9, /* CRC */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Rest is garbage */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif
/* No seq should be put in there */
static const unsigned char upek2020_ack_08[] = {
'C', 'i', 'a', 'o',
0x09,
0x00, 0x00, /* Seq = 0, len = 0x0 */
0x91, 0x9e /* CRC */
};
static const unsigned char upek2020_ack_frame[] = {
'C', 'i', 'a', 'o',
0x00,
0x50, 0x01, /* Seq = 5, len = 0x001 */
0x30,
0xac, 0x5b /* CRC */
};
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,375 +0,0 @@
/*
* Veridicom 5thSense driver for libfprint
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "vcom5s"
#include "drivers_api.h"
/* TODO:
* calibration?
* image size: windows gets 300x300 through vpas enrollment util?
* (probably just increase bulk read size?)
* powerdown? does windows do anything special on exit?
*/
#define CTRL_IN 0xc0
#define CTRL_OUT 0x40
#define CTRL_TIMEOUT 1000
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define IMG_WIDTH 300
#define IMG_HEIGHT 288
#define ROWS_PER_RQ 12
#define NR_REQS (IMG_HEIGHT / ROWS_PER_RQ)
#define RQ_SIZE (IMG_WIDTH * ROWS_PER_RQ)
#define IMG_SIZE (IMG_WIDTH * IMG_HEIGHT)
struct v5s_dev {
int capture_iteration;
struct fp_img *capture_img;
gboolean loop_running;
gboolean deactivating;
};
enum v5s_regs {
/* when using gain 0x29:
* a value of 0x00 produces mostly-black image
* 0x09 destroys ridges (too white)
* 0x01 or 0x02 seem good values */
REG_CONTRAST = 0x02,
/* when using contrast 0x01:
* a value of 0x00 will produce an all-black image.
* 0x29 produces a good contrast image: ridges quite dark, but some
* light grey noise as background
* 0x46 produces all-white image with grey ridges (not very dark) */
REG_GAIN = 0x03,
};
enum v5s_cmd {
/* scan one row. has parameter, at a guess this is which row to scan? */
CMD_SCAN_ONE_ROW = 0xc0,
/* scan whole image */
CMD_SCAN = 0xc1,
};
/***** REGISTER I/O *****/
static void sm_write_reg_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_failed(ssm, -EIO);
else
fpi_ssm_next_state(ssm);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void
sm_write_reg(fpi_ssm *ssm,
struct fp_img_dev *dev,
unsigned char reg,
unsigned char value)
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
int r;
fp_dbg("set %02x=%02x", reg, value);
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(data, CTRL_OUT, reg, value, 0, 0);
libusb_fill_control_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), data, sm_write_reg_cb,
ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
static void sm_exec_cmd_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
fpi_ssm_mark_failed(ssm, -EIO);
else
fpi_ssm_next_state(ssm);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void
sm_exec_cmd(fpi_ssm *ssm,
struct fp_img_dev *dev,
unsigned char cmd,
unsigned char param)
{
struct libusb_transfer *transfer = fpi_usb_alloc();
unsigned char *data;
int r;
fp_dbg("cmd %02x param %02x", cmd, param);
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(data, CTRL_IN, cmd, param, 0, 0);
libusb_fill_control_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), data, sm_exec_cmd_cb,
ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
/***** FINGER DETECTION *****/
/* We take 64x64 pixels at the center of the image, determine the average
* pixel intensity, and threshold it. */
#define DETBOX_ROW_START 111
#define DETBOX_COL_START 117
#define DETBOX_ROWS 64
#define DETBOX_COLS 64
#define DETBOX_ROW_END (DETBOX_ROW_START + DETBOX_ROWS)
#define DETBOX_COL_END (DETBOX_COL_START + DETBOX_COLS)
#define FINGER_PRESENCE_THRESHOLD 100
static gboolean finger_is_present(unsigned char *data)
{
int row;
uint16_t imgavg = 0;
for (row = DETBOX_ROW_START; row < DETBOX_ROW_END; row++) {
unsigned char *rowdata = data + (row * IMG_WIDTH);
uint16_t rowavg = 0;
int col;
for (col = DETBOX_COL_START; col < DETBOX_COL_END; col++)
rowavg += rowdata[col];
rowavg /= DETBOX_COLS;
imgavg += rowavg;
}
imgavg /= DETBOX_ROWS;
fp_dbg("img avg %d", imgavg);
return (imgavg <= FINGER_PRESENCE_THRESHOLD);
}
/***** IMAGE ACQUISITION *****/
static void capture_iterate(fpi_ssm *ssm, struct fp_img_dev *dev);
static void capture_cb(struct libusb_transfer *transfer)
{
fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_failed(ssm, -EIO);
goto out;
}
if (++vdev->capture_iteration == NR_REQS) {
struct fp_img *img = vdev->capture_img;
/* must clear this early, otherwise the call chain takes us into
* loopsm_complete where we would free it, when in fact we are
* supposed to be handing off this image */
vdev->capture_img = NULL;
fpi_imgdev_report_finger_status(dev, finger_is_present(img->data));
fpi_imgdev_image_captured(dev, img);
fpi_ssm_next_state(ssm);
} else {
capture_iterate(ssm, dev);
}
out:
libusb_free_transfer(transfer);
}
static void
capture_iterate(fpi_ssm *ssm,
struct fp_img_dev *dev)
{
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
int iteration = vdev->capture_iteration;
struct libusb_transfer *transfer = fpi_usb_alloc();
int r;
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN,
vdev->capture_img->data + (RQ_SIZE * iteration), RQ_SIZE,
capture_cb, ssm, CTRL_TIMEOUT);
transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_failed(ssm, r);
}
}
static void
sm_do_capture(fpi_ssm *ssm,
struct fp_img_dev *dev)
{
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
G_DEBUG_HERE();
vdev->capture_img = fpi_img_new_for_imgdev(dev);
vdev->capture_iteration = 0;
capture_iterate(ssm, dev);
}
/***** CAPTURE LOOP *****/
enum loop_states {
LOOP_SET_CONTRAST,
LOOP_SET_GAIN,
LOOP_CMD_SCAN,
LOOP_CAPTURE,
LOOP_CAPTURE_DONE,
LOOP_NUM_STATES,
};
static void loop_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct v5s_dev *vdev = FP_INSTANCE_DATA(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case LOOP_SET_CONTRAST:
sm_write_reg(ssm, dev, REG_CONTRAST, 0x01);
break;
case LOOP_SET_GAIN:
sm_write_reg(ssm, dev, REG_GAIN, 0x29);
break;
case LOOP_CMD_SCAN:
if (vdev->deactivating) {
fp_dbg("deactivating, marking completed");
fpi_ssm_mark_completed(ssm);
} else
sm_exec_cmd(ssm, dev, CMD_SCAN, 0x00);
break;
case LOOP_CAPTURE:
sm_do_capture(ssm, dev);
break;
case LOOP_CAPTURE_DONE:
fpi_ssm_jump_to_state(ssm, LOOP_CMD_SCAN);
break;
}
}
static void loopsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
struct v5s_dev *vdev = FP_INSTANCE_DATA(_dev);
int r = fpi_ssm_get_error(ssm);
fpi_ssm_free(ssm);
fp_img_free(vdev->capture_img);
vdev->capture_img = NULL;
vdev->loop_running = FALSE;
if (r)
fpi_imgdev_session_error(dev, r);
if (vdev->deactivating)
fpi_imgdev_deactivate_complete(dev);
}
static int dev_activate(struct fp_img_dev *dev)
{
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), loop_run_state,
LOOP_NUM_STATES, dev);
vdev->deactivating = FALSE;
fpi_ssm_start(ssm, loopsm_complete);
vdev->loop_running = TRUE;
fpi_imgdev_activate_complete(dev, 0);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
if (vdev->loop_running)
vdev->deactivating = TRUE;
else
fpi_imgdev_deactivate_complete(dev);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
struct v5s_dev *v5s_dev;
v5s_dev = g_malloc0(sizeof(struct v5s_dev));
fp_dev_set_instance_data(FP_DEV(dev), v5s_dev);
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0)
fp_err("could not claim interface 0: %s", libusb_error_name(r));
if (r == 0)
fpi_imgdev_open_complete(dev, 0);
return r;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct v5s_dev *v5s_dev;
v5s_dev = FP_INSTANCE_DATA(FP_DEV(dev));
g_free(v5s_dev);
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x061a, .product = 0x0110 },
{ 0, 0, 0, },
};
struct fp_img_driver vcom5s_driver = {
.driver = {
.id = VCOM5S_ID,
.name = FP_COMPONENT,
.full_name = "Veridicom 5thSense",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
.img_height = IMG_HEIGHT,
.img_width = IMG_WIDTH,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,293 +0,0 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "vfs301"
#include "drivers_api.h"
#include "vfs301_proto.h"
#ifndef ETIME
#define ETIME ETIMEDOUT /* For kFreeBSD */
#endif
/************************** GENERIC STUFF *************************************/
/* Submit asynchronous sleep */
static void
async_sleep(unsigned int msec,
fpi_ssm *ssm,
struct fp_img_dev *dev)
{
/* Add timeout */
if (fpi_timeout_add(msec, fpi_ssm_next_state_timeout_cb, FP_DEV(dev), ssm) == NULL) {
/* Failed to add timeout */
fp_err("failed to add timeout");
fpi_imgdev_session_error(dev, -ETIME);
fpi_ssm_mark_failed(ssm, -ETIME);
}
}
static int
submit_image(fpi_ssm *ssm,
struct fp_img_dev *dev)
{
vfs301_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
int height;
struct fp_img *img;
#if 0
/* XXX: This is probably handled by libfprint automagically? */
if (vdev->scanline_count < 20) {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
return 0;
}
#endif
img = fpi_img_new(VFS301_FP_OUTPUT_WIDTH * vdev->scanline_count);
if (img == NULL)
return 0;
vfs301_extract_image(vdev, img->data, &height);
/* TODO: how to detect flip? should the resulting image be
* oriented so that it is equal e.g. to a fingerprint on a paper,
* or to the finger when I look at it?) */
img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED;
img->width = VFS301_FP_OUTPUT_WIDTH;
img->height = height;
img = fpi_img_realloc(img, img->height * img->width);
fpi_imgdev_image_captured(dev, img);
return 1;
}
/* Loop ssm states */
enum
{
/* Step 0 - Scan finger */
M_REQUEST_PRINT,
M_WAIT_PRINT,
M_CHECK_PRINT,
M_READ_PRINT_START,
M_READ_PRINT_WAIT,
M_READ_PRINT_POLL,
M_SUBMIT_PRINT,
/* Number of states */
M_LOOP_NUM_STATES,
};
/* Exec loop sequential state machine */
static void m_loop_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
vfs301_dev_t *vdev = FP_INSTANCE_DATA(_dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case M_REQUEST_PRINT:
vfs301_proto_request_fingerprint(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
fpi_ssm_next_state(ssm);
break;
case M_WAIT_PRINT:
/* Wait fingerprint scanning */
async_sleep(200, ssm, dev);
break;
case M_CHECK_PRINT:
if (!vfs301_proto_peek_event(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev))
fpi_ssm_jump_to_state(ssm, M_WAIT_PRINT);
else
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_START:
fpi_imgdev_report_finger_status(dev, TRUE);
vfs301_proto_process_event_start(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_WAIT:
/* Wait fingerprint scanning */
async_sleep(200, ssm, dev);
break;
case M_READ_PRINT_POLL:
{
int rv = vfs301_proto_process_event_poll(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
g_assert(rv != VFS301_FAILURE);
if (rv == VFS301_ONGOING)
fpi_ssm_jump_to_state(ssm, M_READ_PRINT_WAIT);
else
fpi_ssm_next_state(ssm);
}
break;
case M_SUBMIT_PRINT:
if (submit_image(ssm, dev)) {
fpi_ssm_mark_completed(ssm);
/* NOTE: finger off is expected only after submitting image... */
fpi_imgdev_report_finger_status(dev, FALSE);
} else {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
}
break;
}
}
/* Complete loop sequential state machine */
static void m_loop_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
/* Free sequential state machine */
fpi_ssm_free(ssm);
}
/* Exec init sequential state machine */
static void m_init_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
vfs301_dev_t *vdev = FP_INSTANCE_DATA(_dev);
g_assert(fpi_ssm_get_cur_state(ssm) == 0);
vfs301_proto_init(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
fpi_ssm_mark_completed(ssm);
}
/* Complete init sequential state machine */
static void m_init_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
{
struct fp_img_dev *dev = user_data;
fpi_ssm *ssm_loop;
if (!fpi_ssm_get_error(ssm)) {
/* Notify activate complete */
fpi_imgdev_activate_complete(dev, 0);
/* Start loop ssm */
ssm_loop = fpi_ssm_new(FP_DEV(dev), m_loop_state, M_LOOP_NUM_STATES, dev);
fpi_ssm_start(ssm_loop, m_loop_complete);
}
/* Free sequential state machine */
fpi_ssm_free(ssm);
}
/* Activate device */
static int dev_activate(struct fp_img_dev *dev)
{
fpi_ssm *ssm;
/* Start init ssm */
ssm = fpi_ssm_new(FP_DEV(dev), m_init_state, 1, dev);
fpi_ssm_start(ssm, m_init_complete);
return 0;
}
/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *dev)
{
vfs301_dev_t *vdev;
vdev = FP_INSTANCE_DATA(FP_DEV(dev));
vfs301_proto_deinit(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
fpi_imgdev_deactivate_complete(dev);
}
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
vfs301_dev_t *vdev = NULL;
int r;
/* Claim usb interface */
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
if (r < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
/* Initialize private structure */
vdev = g_malloc0(sizeof(vfs301_dev_t));
fp_dev_set_instance_data(FP_DEV(dev), vdev);
vdev->scanline_buf = malloc(0);
vdev->scanline_count = 0;
/* Notify open complete */
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_close(struct fp_img_dev *dev)
{
vfs301_dev_t *vdev;
/* Release private structure */
vdev = FP_INSTANCE_DATA(FP_DEV(dev));
free(vdev->scanline_buf);
g_free(vdev);
/* Release usb interface */
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
/* Notify close complete */
fpi_imgdev_close_complete(dev);
}
/* Usb id table of device */
static const struct usb_id id_table[] =
{
{ .vendor = 0x138a, .product = 0x0005 /* vfs301 */ },
{ .vendor = 0x138a, .product = 0x0008 /* vfs300 */ },
{ 0, 0, 0, },
};
/* Device driver definition */
struct fp_img_driver vfs301_driver =
{
/* Driver specification */
.driver =
{
.id = VFS301_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS301",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
/* Image specification */
.flags = 0,
.img_width = VFS301_FP_WIDTH,
.img_height = -1,
.bz3_threshold = 24,
/* Routine specification */
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

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

View File

@ -1,137 +0,0 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <libusb.h>
enum {
VFS301_DEFAULT_WAIT_TIMEOUT = 300,
VFS301_SEND_ENDPOINT = 0x01,
VFS301_RECEIVE_ENDPOINT_CTRL = 0x81,
VFS301_RECEIVE_ENDPOINT_DATA = 0x82
};
#define VFS301_FP_RECV_LEN_1 (84032)
#define VFS301_FP_RECV_LEN_2 (84096)
typedef struct {
/* buffer for received data */
unsigned char recv_buf[0x20000];
int recv_len;
/* buffer to hold raw scanlines */
unsigned char *scanline_buf;
int scanline_count;
enum {
VFS301_ONGOING = 0,
VFS301_ENDED = 1,
VFS301_FAILURE = -1
} recv_progress;
int recv_exp_amt;
} vfs301_dev_t;
enum {
/* Width of the scanned data in px */
VFS301_FP_WIDTH = 200,
/* sizeof(fp_line_t) */
VFS301_FP_FRAME_SIZE = 288,
/* Width of output line */
#ifndef OUTPUT_RAW
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_WIDTH,
#else
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_FRAME_SIZE,
#endif
VFS301_FP_SUM_LINES = 3,
#ifdef SCAN_FINISH_DETECTION
/* TODO: The following changes (seen ~60 and ~80) In that
* case we'll need to calibrate this from empty data somehow... */
VFS301_FP_SUM_MEDIAN = 60,
VFS301_FP_SUM_EMPTY_RANGE = 5,
#endif
/* Minimum average difference between returned lines */
VFS301_FP_LINE_DIFF_THRESHOLD = 15,
/* Maximum waiting time for a single fingerprint frame */
VFS301_FP_RECV_TIMEOUT = 2000
};
/* Arrays of this structure is returned during the initialization as a response
* to the 0x02D0 messages.
* It seems to be always the same - what is it for? Some kind of confirmation?
*/
typedef struct {
unsigned char sync_0x01;
unsigned char sync_0xfe;
unsigned char counter_lo;
unsigned char counter_hi; /* FIXME ? */
unsigned char flags[3];
unsigned char sync_0x00;
unsigned char scan[VFS301_FP_WIDTH];
} vfs301_init_line_t;
typedef struct {
unsigned char sync_0x01;
unsigned char sync_0xfe;
unsigned char counter_lo;
unsigned char counter_hi;
unsigned char sync_0x08[2]; /* XXX: always? 0x08 0x08 */
/* 0x08 | 0x18 - Looks like 0x08 marks good quality lines */
unsigned char flag_1;
unsigned char sync_0x00;
unsigned char scan[VFS301_FP_WIDTH];
/* A offsetted, stretched, inverted copy of scan... probably could
* serve finger motion speed detection?
* Seems to be subdivided to some 10B + 53B + 1B blocks */
unsigned char mirror[64];
/* Some kind of sum of the scan, very low contrast */
unsigned char sum1[2];
unsigned char sum2[11];
unsigned char sum3[3];
} vfs301_line_t;
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_request_fingerprint(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
/** returns 0 if no event is ready, or 1 if there is one... */
int vfs301_proto_peek_event(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_process_event_start(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
int vfs301_proto_process_event_poll(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_extract_image(vfs301_dev_t *vfs, unsigned char *output, int *output_height);

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +0,0 @@
/*
* Driver API definitions
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __DRIVERS_API_H__
#define __DRIVERS_API_H__
#include <config.h>
#include "fprint.h"
#include "fpi-log.h"
#include "fpi-dev.h"
#include "fpi-dev-img.h"
#include "fpi-core.h"
#include "fpi-ssm.h"
#include "fpi-poll.h"
#include "fpi-dev.h"
#include "fpi-usb.h"
#include "fpi-img.h"
#include "fpi-assembling.h"
#include "drivers/driver_ids.h"
#endif

View File

@ -1 +0,0 @@

View File

@ -1,241 +0,0 @@
/*
* Internal/private definitions for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __FPRINT_INTERNAL_H__
#define __FPRINT_INTERNAL_H__
#include <config.h>
#include <stdint.h>
#include <errno.h>
#include <glib.h>
#include <libusb.h>
#include "nbis-helpers.h"
#include "fprint.h"
#include "fpi-dev.h"
#include "fpi-core.h"
#include "fpi-log.h"
#include "fpi-dev-img.h"
#include "fpi-data.h"
#include "fpi-img.h"
#include "drivers/driver_ids.h"
/* Global variables */
extern libusb_context *fpi_usb_ctx;
extern GSList *opened_devices;
/* fp_print_data structure definition */
enum fp_print_data_type {
PRINT_DATA_RAW = 0, /* memset-imposed default */
PRINT_DATA_NBIS_MINUTIAE
};
struct fp_print_data {
uint16_t driver_id;
uint32_t devtype;
enum fp_print_data_type type;
GSList *prints;
};
/* fp_dev structure definition */
enum fp_dev_state {
DEV_STATE_INITIAL = 0,
DEV_STATE_ERROR,
DEV_STATE_INITIALIZING,
DEV_STATE_INITIALIZED,
DEV_STATE_DEINITIALIZING,
DEV_STATE_DEINITIALIZED,
DEV_STATE_ENROLL_STARTING,
DEV_STATE_ENROLLING,
DEV_STATE_ENROLL_STOPPING,
DEV_STATE_VERIFY_STARTING,
DEV_STATE_VERIFYING,
DEV_STATE_VERIFY_DONE,
DEV_STATE_VERIFY_STOPPING,
DEV_STATE_IDENTIFY_STARTING,
DEV_STATE_IDENTIFYING,
DEV_STATE_IDENTIFY_DONE,
DEV_STATE_IDENTIFY_STOPPING,
DEV_STATE_CAPTURE_STARTING,
DEV_STATE_CAPTURING,
DEV_STATE_CAPTURE_DONE,
DEV_STATE_CAPTURE_STOPPING,
};
struct fp_dev {
struct fp_driver *drv;
uint32_t devtype;
/* only valid if drv->type == DRIVER_IMAGING */
struct fp_img_dev *img_dev;
/* Link to the instance specific struct */
void *instance_data;
int nr_enroll_stages;
/* FIXME: This will eventually have a bus type */
libusb_device_handle *udev;
/* read-only to drivers */
struct fp_print_data *verify_data;
/* drivers should not mess with any of the below */
enum fp_dev_state state;
int __enroll_stage;
int unconditional_capture;
/* async I/O callbacks and data */
/* FIXME: convert this to generic state operational data mechanism? */
fp_dev_open_cb open_cb;
void *open_cb_data;
fp_operation_stop_cb close_cb;
void *close_cb_data;
fp_enroll_stage_cb enroll_stage_cb;
void *enroll_stage_cb_data;
fp_operation_stop_cb enroll_stop_cb;
void *enroll_stop_cb_data;
fp_img_operation_cb verify_cb;
void *verify_cb_data;
fp_operation_stop_cb verify_stop_cb;
void *verify_stop_cb_data;
fp_identify_cb identify_cb;
void *identify_cb_data;
fp_operation_stop_cb identify_stop_cb;
void *identify_stop_cb_data;
fp_img_operation_cb capture_cb;
void *capture_cb_data;
fp_operation_stop_cb capture_stop_cb;
void *capture_stop_cb_data;
/* FIXME: better place to put this? */
struct fp_print_data **identify_gallery;
};
/* fp_img_dev structure definition */
struct fp_img_dev {
struct fp_dev *parent;
enum fp_imgdev_action action;
int action_state;
struct fp_print_data *acquire_data;
struct fp_print_data *enroll_data;
struct fp_img *acquire_img;
int enroll_stage;
int action_result;
/* FIXME: better place to put this? */
size_t identify_match_offset;
};
/* fp_driver structure definition */
/* fp_img_driver structure definition */
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define fpi_driver_to_img_driver(drv) \
container_of((drv), struct fp_img_driver, driver)
/* fp_dscv_dev structure definition */
struct fp_dscv_dev {
struct libusb_device *udev;
struct fp_driver *drv;
unsigned long driver_data;
uint32_t devtype;
};
/* fp_dscv_print structure definition */
struct fp_dscv_print {
uint16_t driver_id;
uint32_t devtype;
enum fp_finger finger;
char *path;
};
/* fp_minutia structure definition */
struct fp_minutia {
int x;
int y;
int ex;
int ey;
int direction;
double reliability;
int type;
int appearing;
int feature_id;
int *nbrs;
int *ridge_counts;
int num_nbrs;
};
/* fp_minutiae structure definition */
struct fp_minutiae {
int alloc;
int num;
struct fp_minutia **list;
};
/* Defined in fpi-dev-img.c */
void fpi_img_driver_setup(struct fp_img_driver *idriver);
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
/* Exported for use in command-line tools
* Defined in fpi-core.c */
struct fp_driver **fprint_get_drivers (void);
/* Defined in fpi-core.c */
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
/* Defined in fpi-data.c */
void fpi_data_exit(void);
gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
enum fp_print_data_type type2);
/* Defined in fpi-img.c */
gboolean fpi_img_is_sane(struct fp_img *img);
int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
struct fp_print_data **ret);
int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
struct fp_print_data *new_print);
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
struct fp_print_data **gallery, int match_threshold, size_t *match_offset);
/* Defined in fpi-poll.c */
void fpi_timeout_cancel_all_for_dev(struct fp_dev *dev);
void fpi_poll_init(void);
void fpi_poll_exit(void);
/* Defined in fpi-async.c */
void fpi_drvcb_capture_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
struct fp_img *img);
void fpi_drvcb_capture_stopped(struct fp_dev *dev);
void fpi_drvcb_identify_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
size_t match_offset, struct fp_img *img);
void fpi_drvcb_identify_stopped(struct fp_dev *dev);
#include "drivers_definitions.h"
#endif

View File

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

View File

@ -1,115 +0,0 @@
/*
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2015 Vasily Khoruzhick <anarsoul@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __FPI_ASSEMBLING_H__
#define __FPI_ASSEMBLING_H__
#include <fprint.h>
/**
* fpi_frame:
* @delta_x: X offset of the frame
* @delta_y: Y offset of the frame
* @data: bitmap
*
* #fpi_frame is used to store frames for swipe sensors. Drivers should
* populate delta_x and delta_y if the device supports hardware movement
* estimation.
*/
struct fpi_frame {
int delta_x;
int delta_y;
unsigned char data[0];
};
/**
* fpi_frame_asmbl_ctx:
* @frame_width: width of the frame
* @frame_height: height of the frame
* @image_width: resulting image width
* @get_pixel: pixel accessor, returns pixel brightness at x,y of frame
*
* #fpi_frame_asmbl_ctx is a structure holding the context for frame
* assembling routines.
*
* Drivers should define their own #fpi_frame_asmbl_ctx depending on
* hardware parameters of scanner. @image_width is usually 25% wider than
* @frame_width to take horizontal movement into account.
*/
struct fpi_frame_asmbl_ctx {
unsigned int frame_width;
unsigned int frame_height;
unsigned int image_width;
unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y);
};
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes);
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes);
/**
* fpi_line_asmbl_ctx:
* @line_width: width of line
* @max_height: maximal height of assembled image
* @resolution: scale factor used for line assembling routines.
* @median_filter_size: size of median filter for movement estimation
* @max_search_offset: the number of lines to search for the next line
* @get_deviation: pointer to a function that returns the numerical difference
* between two lines
* @get_pixel: pixel accessor, returns pixel brightness at x of line
*
* #fpi_line_asmbl_ctx is a structure holding the context for line assembling
* routines.
*
* Drivers should define their own #fpi_line_asmbl_ctx depending on
* the hardware parameters of the scanner. Swipe scanners of this type usually
* return two lines, the second line is often narrower than first and is used
* for movement estimation.
*
* The @max_search_offset value indicates how many lines forward the assembling
* routines should look while searching for next line. This value depends on
* how fast the hardware sends frames.
*
* The function pointed to by @get_deviation should return the numerical difference
* between two lines. Higher values means lines are more different. If the reader
* returns two lines at a time, this function should be used to estimate the
* difference between pairs of lines.
*/
struct fpi_line_asmbl_ctx {
unsigned int line_width;
unsigned int max_height;
unsigned int resolution;
unsigned int median_filter_size;
unsigned int max_search_offset;
int (*get_deviation)(struct fpi_line_asmbl_ctx *ctx,
GSList *line1, GSList *line2);
unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx,
GSList *line,
unsigned int x);
};
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *lines, size_t num_lines);
#endif

View File

@ -1,706 +0,0 @@
/*
* Asynchronous I/O functionality
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "async"
#include "fp_internal.h"
#include "fpi-async.h"
#include <config.h>
#include <errno.h>
#include <glib.h>
/*
* SECTION:fpi-async
* @title: Asynchronous operations reporting
* @short_description: Asynchronous operations reporting functions
*
* Those functions are used by primitive drivers to report back their
* current status. Most drivers, imaging ones, do not need to use them.
*/
/* Drivers call this when device initialisation has completed */
void fpi_drvcb_open_complete(struct fp_dev *dev, int status)
{
fp_dbg("status %d", status);
BUG_ON(dev->state != DEV_STATE_INITIALIZING);
dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_INITIALIZED;
opened_devices = g_slist_prepend(opened_devices, dev);
if (dev->open_cb)
dev->open_cb(dev, status, dev->open_cb_data);
}
/**
* fp_async_dev_open:
* @ddev: the struct #fp_dscv_dev discovered device to open
* @callback: the callback to call when the device has been opened
* @user_data: user data to pass to the callback
*
* Opens and initialises a device. This is the function you call in order
* to convert a #fp_dscv_dev discovered device into an actual device handle
* that you can perform operations with.
*
* The error status of the opening will be provided as an argument to the
* #fp_dev_open_cb callback.
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb callback,
void *user_data)
{
struct fp_driver *drv;
struct fp_dev *dev;
libusb_device_handle *udevh;
int r;
g_return_val_if_fail(ddev != NULL, -ENODEV);
g_return_val_if_fail (callback != NULL, -EINVAL);
drv = ddev->drv;
G_DEBUG_HERE();
r = libusb_open(ddev->udev, &udevh);
if (r < 0) {
fp_err("usb_open failed, error %d", r);
return r;
}
dev = g_malloc0(sizeof(*dev));
dev->drv = drv;
dev->udev = udevh;
dev->__enroll_stage = -1;
dev->state = DEV_STATE_INITIALIZING;
dev->open_cb = callback;
dev->open_cb_data = user_data;
if (!drv->open) {
fpi_drvcb_open_complete(dev, 0);
return 0;
}
dev->state = DEV_STATE_INITIALIZING;
r = drv->open(dev, ddev->driver_data);
if (r) {
fp_err("device initialisation failed, driver=%s", drv->name);
libusb_close(udevh);
g_free(dev);
}
return r;
}
/* Drivers call this when device deinitialisation has completed */
void fpi_drvcb_close_complete(struct fp_dev *dev)
{
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
dev->state = DEV_STATE_DEINITIALIZED;
fpi_timeout_cancel_all_for_dev(dev);
libusb_close(dev->udev);
if (dev->close_cb)
dev->close_cb(dev, dev->close_cb_data);
g_free(dev);
}
/**
* fp_async_dev_close:
* @dev: the struct #fp_dev device
* @callback: the callback to call when the device has been closed
* @user_data: user data to pass to the callback
*
* Closes a device. You must call this function when you have finished using
* a fingerprint device.
*/
API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv;
g_return_if_fail (dev != NULL);
drv = dev->drv;
g_return_if_fail (drv->close != NULL);
if (g_slist_index(opened_devices, (gconstpointer) dev) == -1)
fp_err("device %p not in opened list!", dev);
opened_devices = g_slist_remove(opened_devices, (gconstpointer) dev);
dev->close_cb = callback;
dev->close_cb_data = user_data;
dev->state = DEV_STATE_DEINITIALIZING;
drv->close(dev);
}
/* Drivers call this when enrollment has started */
void fpi_drvcb_enroll_started(struct fp_dev *dev, int status)
{
fp_dbg("status %d", status);
BUG_ON(dev->state != DEV_STATE_ENROLL_STARTING);
if (status) {
if (status > 0) {
status = -status;
fp_dbg("adjusted to %d", status);
}
dev->state = DEV_STATE_ERROR;
if (dev->enroll_stage_cb)
dev->enroll_stage_cb(dev, status, NULL, NULL,
dev->enroll_stage_cb_data);
} else {
dev->state = DEV_STATE_ENROLLING;
}
}
/**
* fp_async_enroll_start:
* @dev: the struct #fp_dev device
* @callback: the callback to call for each stage of the enrollment
* @user_data: user data to pass to the callback
*
* Starts an enrollment and calls @callback for each enrollment stage.
* See [Enrolling](libfprint-Devices-operations.html#enrolling)
* for an explanation of enroll stages.
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_enroll_start(struct fp_dev *dev,
fp_enroll_stage_cb callback, void *user_data)
{
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
g_return_val_if_fail (callback != NULL, -EINVAL);
drv = dev->drv;
if (!dev->nr_enroll_stages || !drv->enroll_start) {
fp_err("driver %s has 0 enroll stages or no enroll func",
drv->name);
return -ENOTSUP;
}
fp_dbg("starting enrollment");
dev->enroll_stage_cb = callback;
dev->enroll_stage_cb_data = user_data;
dev->state = DEV_STATE_ENROLL_STARTING;
r = drv->enroll_start(dev);
if (r < 0) {
dev->enroll_stage_cb = NULL;
fp_err("failed to start enrollment");
dev->state = DEV_STATE_ERROR;
}
return r;
}
/* Drivers call this when an enroll stage has completed */
void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
struct fp_print_data *data, struct fp_img *img)
{
BUG_ON(dev->state != DEV_STATE_ENROLLING);
fp_dbg("result %d", result);
if (!dev->enroll_stage_cb) {
fp_dbg("ignoring enroll result as no callback is subscribed");
return;
}
if (result == FP_ENROLL_COMPLETE && !data) {
fp_err("BUG: complete but no data?");
result = FP_ENROLL_FAIL;
}
dev->enroll_stage_cb(dev, result, data, img, dev->enroll_stage_cb_data);
}
/* Drivers call this when enrollment has stopped */
void fpi_drvcb_enroll_stopped(struct fp_dev *dev)
{
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_ENROLL_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->enroll_stop_cb)
dev->enroll_stop_cb(dev, dev->enroll_stop_cb_data);
}
/**
* fp_async_enroll_stop:
* @dev: the struct #fp_dev device
* @callback: the callback to call when the enrollment has been cancelled
* @user_data: user data to pass to the callback
*
* Stops an ongoing enrollment started with fp_async_enroll_start().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_enroll_stop(struct fp_dev *dev,
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->enroll_start)
return -ENOTSUP;
dev->enroll_stage_cb = NULL;
dev->enroll_stop_cb = callback;
dev->enroll_stop_cb_data = user_data;
dev->state = DEV_STATE_ENROLL_STOPPING;
if (!drv->enroll_stop) {
fpi_drvcb_enroll_stopped(dev);
return 0;
}
r = drv->enroll_stop(dev);
if (r < 0) {
fp_err("failed to stop enrollment");
dev->enroll_stop_cb = NULL;
}
return r;
}
/**
* fp_async_verify_start:
* @dev: the struct #fp_dev device
* @data: the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan
* @callback: the callback to call when the verification has finished
* @user_data: user data to pass to the callback
*
* Starts a verification and calls @callback when the verification has
* finished. See fp_verify_finger_img() for the synchronous API. When the
* @callback has been called, you must call fp_async_verify_stop().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
struct fp_print_data *data, fp_img_operation_cb callback, void *user_data)
{
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
g_return_val_if_fail (callback != NULL, -EINVAL);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->verify_start)
return -ENOTSUP;
dev->state = DEV_STATE_VERIFY_STARTING;
dev->verify_cb = callback;
dev->verify_cb_data = user_data;
dev->verify_data = data;
r = drv->verify_start(dev);
if (r < 0) {
dev->verify_cb = NULL;
dev->state = DEV_STATE_ERROR;
fp_err("failed to start verification, error %d", r);
}
return r;
}
/* Drivers call this when verification has started */
void fpi_drvcb_verify_started(struct fp_dev *dev, int status)
{
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_VERIFY_STARTING);
if (status) {
if (status > 0) {
status = -status;
fp_dbg("adjusted to %d", status);
}
dev->state = DEV_STATE_ERROR;
if (dev->verify_cb)
dev->verify_cb(dev, status, NULL, dev->verify_cb_data);
} else {
dev->state = DEV_STATE_VERIFYING;
}
}
/* Drivers call this to report a verify result (which might mark completion) */
void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
struct fp_img *img)
{
fp_dbg("result %d", result);
BUG_ON(dev->state != DEV_STATE_VERIFYING);
if (result < 0 || result == FP_VERIFY_NO_MATCH
|| result == FP_VERIFY_MATCH)
dev->state = DEV_STATE_VERIFY_DONE;
if (dev->verify_cb)
dev->verify_cb(dev, result, img, dev->verify_cb_data);
else
fp_dbg("ignoring verify result as no callback is subscribed");
}
/* Drivers call this when verification has stopped */
void fpi_drvcb_verify_stopped(struct fp_dev *dev)
{
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_VERIFY_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->verify_stop_cb)
dev->verify_stop_cb(dev, dev->verify_stop_cb_data);
}
/**
* fp_async_verify_stop:
* @dev: the struct #fp_dev device
* @callback: the callback to call to finish a verification
* @user_data: user data to pass to the callback
*
* Finishes an ongoing verification started with fp_async_verify_start().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv;
gboolean iterating = (dev->state == DEV_STATE_VERIFYING);
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
G_DEBUG_HERE();
if (dev->state == DEV_STATE_VERIFY_STOPPING) {
fp_dbg ("Already stopping verification, returning -EINPROGRESS");
return -EINPROGRESS;
}
if (dev->state == DEV_STATE_INITIALIZED) {
if (callback)
callback(dev, user_data);
return 0;
}
drv = dev->drv;
BUG_ON(dev->state != DEV_STATE_ERROR
&& dev->state != DEV_STATE_VERIFYING
&& dev->state != DEV_STATE_VERIFY_DONE);
dev->verify_cb = NULL;
dev->verify_stop_cb = callback;
dev->verify_stop_cb_data = user_data;
dev->state = DEV_STATE_VERIFY_STOPPING;
if (!drv->verify_start)
return -ENOTSUP;
if (!drv->verify_stop) {
dev->state = DEV_STATE_INITIALIZED;
fpi_drvcb_verify_stopped(dev);
return 0;
}
r = drv->verify_stop(dev, iterating);
if (r < 0) {
fp_err("failed to stop verification");
dev->verify_stop_cb = NULL;
}
return r;
}
/**
* fp_async_identify_start:
* @dev: the struct #fp_dev device
* @gallery: NULL-terminated array of pointers to the prints to
* identify against. Each one must have been previously enrolled with a device
* compatible to the device selected to perform the scan
* @callback: the callback to call when the identification has finished
* @user_data: user data to pass to the callback
*
* Performs a new scan and verifies it against a previously enrolled print.
* See also: fp_verify_finger_img()
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_identify_start(struct fp_dev *dev,
struct fp_print_data **gallery, fp_identify_cb callback, void *user_data)
{
struct fp_driver *drv;
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->identify_start)
return -ENOTSUP;
dev->state = DEV_STATE_IDENTIFY_STARTING;
dev->identify_cb = callback;
dev->identify_cb_data = user_data;
dev->identify_gallery = gallery;
r = drv->identify_start(dev);
if (r < 0) {
fp_err("identify_start failed with error %d", r);
dev->identify_cb = NULL;
dev->state = DEV_STATE_ERROR;
}
return r;
}
/* Driver-lib: identification has started, expect results soon */
void fpi_drvcb_identify_started(struct fp_dev *dev, int status)
{
fp_dbg("status %d", status);
BUG_ON(dev->state != DEV_STATE_IDENTIFY_STARTING);
if (status) {
if (status > 0) {
status = -status;
fp_dbg("adjusted to %d", status);
}
dev->state = DEV_STATE_ERROR;
if (dev->identify_cb)
dev->identify_cb(dev, status, 0, NULL, dev->identify_cb_data);
} else {
dev->state = DEV_STATE_IDENTIFYING;
}
}
/* Drivers report an identify result (which might mark completion) */
void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
size_t match_offset, struct fp_img *img)
{
fp_dbg("result %d", result);
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
&& dev->state != DEV_STATE_ERROR);
if (result < 0 || result == FP_VERIFY_NO_MATCH
|| result == FP_VERIFY_MATCH)
dev->state = DEV_STATE_IDENTIFY_DONE;
if (dev->identify_cb)
dev->identify_cb(dev, result, match_offset, img, dev->identify_cb_data);
else
fp_dbg("ignoring verify result as no callback is subscribed");
}
/**
* fp_async_identify_stop:
* @dev: the struct #fp_dev device
* @callback: the callback to call when the identification has stopped
* @user_data: user data to pass to the callback
*
* Stops an ongoing identification started with fp_async_identify_start().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv;
gboolean iterating = (dev->state == DEV_STATE_IDENTIFYING);
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
G_DEBUG_HERE();
if (dev->state == DEV_STATE_IDENTIFY_STOPPING) {
fp_dbg ("Already stopping identification, returning -EINPROGRESS");
return -EINPROGRESS;
}
if (dev->state == DEV_STATE_INITIALIZED) {
if (callback)
callback(dev, user_data);
return 0;
}
drv = dev->drv;
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
&& dev->state != DEV_STATE_IDENTIFY_DONE);
dev->state = DEV_STATE_IDENTIFY_STOPPING;
dev->identify_cb = NULL;
dev->identify_stop_cb = callback;
dev->identify_stop_cb_data = user_data;
if (!drv->identify_start)
return -ENOTSUP;
if (!drv->identify_stop) {
dev->state = DEV_STATE_INITIALIZED;
fpi_drvcb_identify_stopped(dev);
return 0;
}
r = drv->identify_stop(dev, iterating);
if (r < 0) {
fp_err("failed to stop identification");
dev->identify_stop_cb = NULL;
}
return r;
}
/* Drivers call this when identification has stopped */
void fpi_drvcb_identify_stopped(struct fp_dev *dev)
{
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_IDENTIFY_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->identify_stop_cb)
dev->identify_stop_cb(dev, dev->identify_stop_cb_data);
}
/**
* fp_async_capture_start:
* @dev: the struct #fp_dev device
* @unconditional: whether to unconditionally capture an image, or to only capture when a finger is detected
* @callback: the callback to call when the capture has finished
* @user_data: user data to pass to the callback
*
* Start the capture of an #fp_img from a device. When the @callback has been called,
* you must call fp_async_capture_stop().
*
* Returns: 0 on success, non-zero on error. -ENOTSUP indicates that either the
* @unconditional flag was set but the device does not support this, or that the
* device does not support imaging
*/
API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional,
fp_img_operation_cb callback, void *user_data)
{
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
g_return_val_if_fail (callback != NULL, -EINVAL);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->capture_start)
return -ENOTSUP;
dev->state = DEV_STATE_CAPTURE_STARTING;
dev->capture_cb = callback;
dev->capture_cb_data = user_data;
dev->unconditional_capture = unconditional;
r = drv->capture_start(dev);
if (r < 0) {
dev->capture_cb = NULL;
dev->state = DEV_STATE_ERROR;
fp_err("failed to start capture, error %d", r);
}
return r;
}
/* Drivers call this when capture has started */
void fpi_drvcb_capture_started(struct fp_dev *dev, int status)
{
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_CAPTURE_STARTING);
if (status) {
if (status > 0) {
status = -status;
fp_dbg("adjusted to %d", status);
}
dev->state = DEV_STATE_ERROR;
if (dev->capture_cb)
dev->capture_cb(dev, status, NULL, dev->capture_cb_data);
} else {
dev->state = DEV_STATE_CAPTURING;
}
}
/* Drivers call this to report a capture result (which might mark completion) */
void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
struct fp_img *img)
{
fp_dbg("result %d", result);
BUG_ON(dev->state != DEV_STATE_CAPTURING);
if (result < 0 || result == FP_CAPTURE_COMPLETE)
dev->state = DEV_STATE_CAPTURE_DONE;
if (dev->capture_cb)
dev->capture_cb(dev, result, img, dev->capture_cb_data);
else
fp_dbg("ignoring capture result as no callback is subscribed");
}
/* Drivers call this when capture has stopped */
void fpi_drvcb_capture_stopped(struct fp_dev *dev)
{
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_CAPTURE_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->capture_stop_cb)
dev->capture_stop_cb(dev, dev->capture_stop_cb_data);
}
/**
* fp_async_capture_stop:
* @dev: the struct #fp_dev device
* @callback: the callback to call when the capture has been stopped
* @user_data: user data to pass to the callback
*
* Stops an ongoing verification started with fp_async_capture_start().
*
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_ERROR
&& dev->state != DEV_STATE_CAPTURING
&& dev->state != DEV_STATE_CAPTURE_DONE);
dev->capture_cb = NULL;
dev->capture_stop_cb = callback;
dev->capture_stop_cb_data = user_data;
dev->state = DEV_STATE_CAPTURE_STOPPING;
if (!drv->capture_start)
return -ENOTSUP;
if (!drv->capture_stop) {
dev->state = DEV_STATE_INITIALIZED;
fpi_drvcb_capture_stopped(dev);
return 0;
}
r = drv->capture_stop(dev);
if (r < 0) {
fp_err("failed to stop capture");
dev->capture_stop_cb = NULL;
}
return r;
}

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