Compare commits
1162 commits
Author | SHA1 | Date | |
---|---|---|---|
f579a77bfd | |||
03deb3011b | |||
c7650b6ec9 | |||
128d809227 | |||
9356e895a2 | |||
3c2883b992 | |||
eb568a62aa | |||
d763f8f41a | |||
df41ed56f6 | |||
aff063c23c | |||
e2511095d1 | |||
9515cc2e59 | |||
b3cfc40dea | |||
c162b895c0 | |||
40b3923ca6 | |||
d7e7d8e036 | |||
ec53abfc3a | |||
83541a2ddc | |||
e22497d51b | |||
0dcb4be4d3 | |||
8f93aef122 | |||
8dfa12e41d | |||
88cb452e05 | |||
909865ed8d | |||
39333a0642 | |||
4340be728c | |||
dba5ca5535 | |||
2a70cd7e02 | |||
3108ac3144 | |||
c928d7bd8f | |||
ec42b2ade1 | |||
4edfa48608 | |||
1a5df96751 | |||
62448492af | |||
874513e79a | |||
5c89bda7f3 | |||
8147372bdd | |||
43336a204f | |||
968331c383 | |||
d547c000fc | |||
ff6caca2e3 | |||
77756e111d | |||
23a4f5b77a | |||
5b7c5e7c09 | |||
da28731adc | |||
6440a7d12f | |||
71e0c29f28 | |||
a2d950044d | |||
96e5888110 | |||
dd476c0ccf | |||
4cdca4da24 | |||
a68fce0f2c | |||
1f5e0821e0 | |||
d6b4adec73 | |||
9e7bfa05b3 | |||
9ecd6236ee | |||
a07011bac2 | |||
f7290255e0 | |||
29048c51db | |||
42676dd300 | |||
45c5d17f3b | |||
fc76db562e | |||
9f93f5ded7 | |||
74c4125827 | |||
4f6d908390 | |||
575bd369d5 | |||
304219b65c | |||
23bca2a8ac | |||
4bf064d873 | |||
d2c2410a6f | |||
e8f9cc1fce | |||
0ee274946d | |||
0c26205a1e | |||
d957bbd0f4 | |||
ec9e6f1947 | |||
24658fb351 | |||
d5fda36bc0 | |||
cdaa3497d7 | |||
8a5bec6619 | |||
145f7287fa | |||
dbd89929b9 | |||
01663c1fb5 | |||
4ef13d971d | |||
a267e30fc6 | |||
2ba60d0a52 | |||
947420d2ce | |||
793cad57f3 | |||
f37e20b8a0 | |||
e2f199bb6a | |||
059ab65081 | |||
7893278cc6 | |||
0697191387 | |||
8be666bb05 | |||
019a294ec4 | |||
f6e80456d9 | |||
51cab75b1e | |||
1ed2b23902 | |||
9dd72611bf | |||
4bcb55e412 | |||
5bda7aef38 | |||
b4f564cafc | |||
a3f568db3d | |||
ba920aa41b | |||
db1e88138b | |||
7ff95dc39a | |||
098ff97edd | |||
90cbcd7fb5 | |||
182367a079 | |||
daaafc80c3 | |||
c989cc4b95 | |||
0edae7b641 | |||
49e3963783 | |||
040d0d34fd | |||
82c406dace | |||
046607add6 | |||
9c0cd3fb23 | |||
439223cac3 | |||
992a207ede | |||
ae6be6837b | |||
261ba3a4a4 | |||
e9dddcc87a | |||
7e02f3faf9 | |||
b61303500e | |||
668b3517a9 | |||
657fe85c25 | |||
4d5e2775b2 | |||
8a04578847 | |||
77e95aa545 | |||
b9df7a4e70 | |||
1ca56adff5 | |||
d683b271d4 | |||
94e86875ae | |||
511d456006 | |||
11e379050f | |||
9416f91c75 | |||
c4ae89575a | |||
04f6cac7ec | |||
d2981fc6a4 | |||
8c9167d836 | |||
9aa3060d32 | |||
9a1dcaa801 | |||
683ac48e21 | |||
3b34fc9b5b | |||
41f8737b48 | |||
ef805f2341 | |||
bd99f865d8 | |||
3717468a8a | |||
8d545a0b95 | |||
355957919e | |||
07778f6bfa | |||
7fcce7891a | |||
b0d9d00762 | |||
e95056aa86 | |||
9321791d0e | |||
4031bb62d7 | |||
59767af552 | |||
3fb8860dc4 | |||
03e0efe7ea | |||
df9483e723 | |||
870468c741 | |||
47223e551f | |||
e0de8c67b6 | |||
4a700758a6 | |||
e8a7ff1033 | |||
8ae27b4672 | |||
b81c6857f2 | |||
dbd20ec669 | |||
e7eaecedc6 | |||
52d0409241 | |||
f2d0d0bc57 | |||
8fd1fcbe49 | |||
e4a297887b | |||
966703057d | |||
9e164485f0 | |||
3bb38e2ff6 | |||
6a62d32c81 | |||
b2a64cc980 | |||
88117c172e | |||
27a62443a1 | |||
2f7c78eb97 | |||
74f8a8ee27 | |||
acd0a10e76 | |||
27c2466bda | |||
33ba248c44 | |||
43cf28b9da | |||
4da52f78f6 | |||
e4e0937848 | |||
25a6c916aa | |||
51009b48a0 | |||
e1e3f6955e | |||
c495b82000 | |||
d90ee96df8 | |||
36304b736b | |||
31e34bd4bd | |||
ec4c7ca5a9 | |||
8d21a9c27c | |||
c4069065f9 | |||
31541edc58 | |||
549718753f | |||
cfde050220 | |||
88a38c38af | |||
7ffcc2f9e7 | |||
1dae6796f7 | |||
0bb0492025 | |||
3db0858fb0 | |||
2382506491 | |||
08f4be707c | |||
3693c39bc5 | |||
993109a7f8 | |||
18db20d160 | |||
89b4c4ee75 | |||
153b24a95a | |||
8c45c0952e | |||
c3ece8621d | |||
67cb61cc18 | |||
33ffadf402 | |||
162a83e484 | |||
dfb27222eb | |||
81e53c422d | |||
be0b4ae2bb | |||
56bcf1ffdd | |||
665de7813b | |||
a291d17f26 | |||
e8886dbc6b | |||
3d6fb15b5c | |||
43d0dfdd8f | |||
50f522583e | |||
f0443ba2f3 | |||
546f35132c | |||
ce9527d2cb | |||
89890dbd1f | |||
e0c41c5444 | |||
3b83157e9b | |||
57f836a0f6 | |||
170924ee4f | |||
63bfaf4f60 | |||
2f6adce2fa | |||
018641ad20 | |||
8ded064e65 | |||
3f7a638eed | |||
253750ec08 | |||
5df14206d8 | |||
2f2da87240 | |||
533180a2e6 | |||
99c269b3fe | |||
66fc93eeff | |||
284f6f1ef8 | |||
1f2d723485 | |||
f6179d6cc4 | |||
cbce56c142 | |||
55a2bb5536 | |||
16095a21fd | |||
80dbc9c0cb | |||
944e0d0383 | |||
349fbeb834 | |||
17a8bacfaf | |||
6d4b498dae | |||
7c2a67a954 | |||
a6c2509ca8 | |||
8254b9e99e | |||
943c64d96f | |||
f852d972a5 | |||
35d2d78e67 | |||
3d5db6a391 | |||
2ee0d16784 | |||
e6712fbcca | |||
ee928db5b2 | |||
d6ca8ff2b0 | |||
b1b20f8ab9 | |||
7e2b89791e | |||
3560a0f1e7 | |||
ed5339c4f5 | |||
2d10d864d8 | |||
c96958582f | |||
c02771d16b | |||
989d498eb9 | |||
91ee03eb7a | |||
28ba6a0de9 | |||
faade91c39 | |||
499de3e442 | |||
0ff7a07671 | |||
0d9d7dcb46 | |||
fb23f8690f | |||
6ca8441df9 | |||
8112da0358 | |||
f2ea3e784e | |||
74810a8472 | |||
91fb8d8cb4 | |||
0688288c6d | |||
c1e832e7a7 | |||
b5496fd257 | |||
de271a0e8d | |||
12b0120a3d | |||
2783ac3e60 | |||
abb0b1267c | |||
5cb91a4189 | |||
0bb132b167 | |||
ce39f27b5e | |||
7d0956513b | |||
2b7cfa751a | |||
0eee6a56dd | |||
8962e14fde | |||
e246e00ba3 | |||
fa3bdb874d | |||
2caeb8cbb3 | |||
dda3587b76 | |||
fb5854213a | |||
21ee241f0c | |||
280f916ace | |||
1b5dd0057f | |||
8a6f1932f8 | |||
0051ff6352 | |||
b6dd522459 | |||
656bf3d175 | |||
fe498c56c7 | |||
251ccef9ba | |||
3b993fabb6 | |||
0c56e0de6d | |||
893ff9c033 | |||
3c382cac7f | |||
42e4506b1b | |||
ae3baadcf9 | |||
4f29a32da8 | |||
e5fa54e8e7 | |||
d3076039d0 | |||
a748f4d30f | |||
3ee5536a13 | |||
f56aacc7ef | |||
96fa0a96eb | |||
1754bd0204 | |||
90a1abf2f8 | |||
59824d2122 | |||
31319d9c6f | |||
e27b65c930 | |||
b03f9a502a | |||
44ef20d5ac | |||
4719b30f16 | |||
7eb361087a | |||
33d50e4e30 | |||
994690cfa3 | |||
52b2d10887 | |||
81e0f4dfe5 | |||
c7cab77fc1 | |||
a63dcc96d5 | |||
fab349f356 | |||
62edf93958 | |||
8c4ff253cb | |||
3ce6a15547 | |||
174aa2c091 | |||
9efe25b91c | |||
bcce8876e2 | |||
3962372f47 | |||
f67f61c638 | |||
d5f7f4dfaa | |||
ce6961d165 | |||
30e1a68344 | |||
5b087ed848 | |||
5d0481b031 | |||
596d22a449 | |||
c85f385191 | |||
eb2aaaaa20 | |||
e3c009c5b3 | |||
a4f7293f32 | |||
8b64312f4b | |||
b7e27bfdc6 | |||
37b19674f1 | |||
a5f4ad507a | |||
1f96077e36 | |||
ed26976ac5 | |||
e4d292b595 | |||
c6b8430c72 | |||
cbf1dcca29 | |||
7f7d099ba0 | |||
b6f965c1d9 | |||
fd2875aa3e | |||
4b2816db64 | |||
4af3e59174 | |||
24b1faffde | |||
49983c8ee7 | |||
d276c3489e | |||
3f51e6dcb6 | |||
b4dbbd667a | |||
7d9245505f | |||
570daf2321 | |||
60d0f84294 | |||
6633025437 | |||
40ed353666 | |||
32bdd8d5c4 | |||
ec4fc9aec5 | |||
390611d5c9 | |||
685052c605 | |||
4b83f8bfd9 | |||
b137807420 | |||
0936fc3597 | |||
b7f436e8de | |||
4f0b0fa526 | |||
f0abefa9fa | |||
7f58556011 | |||
cecb01bcb9 | |||
b95402bc72 | |||
484743f652 | |||
a5cfc1644f | |||
b3565b83e1 | |||
8f46de0a60 | |||
b3c5fe4b82 | |||
4cf5f92a52 | |||
297236b51a | |||
8626c64831 | |||
e4f9935706 | |||
8ba29606bb | |||
1b74813adf | |||
07ff03970f | |||
25ab4849a4 | |||
840bcc77a5 | |||
a464f602ca | |||
ad17011e68 | |||
744a71ce08 | |||
422fc5facf | |||
6d542edf8a | |||
8d4d56b1f1 | |||
6e30a1ee45 | |||
87c3b9c5ba | |||
0a08a6a7c0 | |||
9db89e00d0 | |||
3ad65b9589 | |||
48aa6d0252 | |||
eefc954f91 | |||
5bcf9ac008 | |||
d2402309ee | |||
a651b65401 | |||
bc3f622b2a | |||
5de49b33e6 | |||
81e198c034 | |||
c0895a858d | |||
5d5995f201 | |||
f71045b743 | |||
0274d0783b | |||
5c5a4f6907 | |||
5b6f5c9aad | |||
41e05b1133 | |||
579e01359b | |||
cc887c1a37 | |||
fdd2d6abf8 | |||
6bf29108a1 | |||
d0751ae06b | |||
a218a5efdd | |||
c6ae8e58a4 | |||
87c7894c28 | |||
e7ff4f705c | |||
c26588942a | |||
3d68cddfe7 | |||
96fba323b9 | |||
bd4f118b5e | |||
9d4b5ad682 | |||
ca788b6de2 | |||
90ccf9a0af | |||
2581f1aa32 | |||
ebe5cb58ba | |||
bd500b2235 | |||
8fa50d667c | |||
2ae8b74e60 | |||
f4ec816a6b | |||
9e2a7235e3 | |||
66c9e4a829 | |||
0bb8ad1313 | |||
6eb06697e9 | |||
355cae1bbd | |||
15a90eb451 | |||
82ba69b1df | |||
ccd42bdece | |||
e19a1a6550 | |||
5ac770c614 | |||
5faf8498d9 | |||
cfbd5d27b7 | |||
169ca1ba77 | |||
bb08d2e3c2 | |||
ca5143ffa5 | |||
7eb10178b8 | |||
2c9e252ca4 | |||
23fab3a20a | |||
24e9363a46 | |||
a12d316aa4 | |||
88461d53ec | |||
3b47113122 | |||
7a7bec5a80 | |||
8be861b876 | |||
8893840ffa | |||
4d6a7ec09d | |||
b9e546f05b | |||
05df5e2822 | |||
58a9214610 | |||
cdcc476325 | |||
a87e9c546f | |||
ad514c3775 | |||
3c5b7f8ea6 | |||
b09df0e40a | |||
027ac8d843 | |||
b3a4c2cf9a | |||
9f3272f296 | |||
456522397a | |||
0889ec20a8 | |||
30c783cbeb | |||
078cea1709 | |||
bc8a5859e3 | |||
05bc2e1c80 | |||
29a13a9b4a | |||
54286c7603 | |||
db14995c31 | |||
7aaeec3d6a | |||
0b8e2d6074 | |||
0c582230f3 | |||
829fb9f873 | |||
4d5c34e11a | |||
8292c449f7 | |||
3f3d4559b4 | |||
fcdf1a1ff1 | |||
ba07c74006 | |||
6716359fe8 | |||
c27a4faeca | |||
8992e559f8 | |||
6c6df626c8 | |||
87dee93633 | |||
516c1593bb | |||
dcc7e6de90 | |||
3c6ba0b678 | |||
4db1b84c7a | |||
36108f9f82 | |||
19f239ce61 | |||
f91e5310bb | |||
0d604fa34e | |||
d9bcf9b9cc | |||
b8e558452a | |||
c9e1a7f283 | |||
c5aedc9970 | |||
5b17eda011 | |||
022b4a75b1 | |||
bfc75de778 | |||
f3f768e738 | |||
dbb26c5ade | |||
0566f82219 | |||
c8e1269f61 | |||
2158c5e2d1 | |||
10945f8546 | |||
806ad10673 | |||
4562f9dae3 | |||
c57defda92 | |||
c806993cb9 | |||
95cb62fd3b | |||
d255a91e97 | |||
9ebb3fd231 | |||
68b5c5d98f | |||
2af0531994 | |||
bfd68bbc01 | |||
9c806e60f4 | |||
113bef8f3f | |||
1d1c39c234 | |||
4948a85e97 | |||
7e2db8e988 | |||
24d388f923 | |||
af42b3e468 | |||
edb09463f4 | |||
42b1deaeea | |||
fe828d0bb2 | |||
cf5473a55c | |||
0471edbf10 | |||
299a797423 | |||
c594863639 | |||
3bb1840750 | |||
f31b8483d4 | |||
324258bc8c | |||
f578ebe82d | |||
98fa6efce3 | |||
c7b7f78273 | |||
b4c3756ab0 | |||
da46f53e82 | |||
eeddd8c7bc | |||
43a8c909bf | |||
09576e5209 | |||
8184e33dd6 | |||
6f2b1f97da | |||
c3bf6fe863 | |||
5bed81025e | |||
d7100e41df | |||
ae7021e529 | |||
d9de941a47 | |||
7114d97f25 | |||
92a5278a74 | |||
d01bb41b7c | |||
ad88a5a78f | |||
ff0107fc0a | |||
107fdfde32 | |||
bb0ef04b85 | |||
0a475196e0 | |||
1dee7985b9 | |||
a1a3933191 | |||
9c8360ad67 | |||
0c7d2d8ecd | |||
6209b22e3b | |||
f404a69b73 | |||
50a837573d | |||
3e958ab7b4 | |||
a8a2a757ed | |||
702255b182 | |||
a64ac2296b | |||
a522e3fd6f | |||
65828e0e56 | |||
fe967d0ac2 | |||
10c5bdade7 | |||
8f21aa1b26 | |||
7b68344394 | |||
dccb5b3ab2 | |||
2fcc2deb43 | |||
c678b9021c | |||
b2e55308d6 | |||
a176fa1d34 | |||
cacce50ef9 | |||
5ab4d6c454 | |||
0c655be159 | |||
2b8c524928 | |||
2f2ea65d32 | |||
1d48b70f38 | |||
35e9f19c0c | |||
70a0d6f0fe | |||
7ed9b0c2f9 | |||
6a090656b6 | |||
e1d181887f | |||
e143f12e57 | |||
e64c18f8de | |||
7e70344b4a | |||
44af2173a8 | |||
e7c7f368c9 | |||
a29586f398 | |||
98cd1c2680 | |||
ae285e790d | |||
1e2f19ea3d | |||
b0effae990 | |||
ff67bf5a16 | |||
bac6382f67 | |||
e12978f402 | |||
1ba95db379 | |||
7ec2df2405 | |||
3ed73aa17c | |||
0241617713 | |||
20a52593eb | |||
42db16364d | |||
ee606ae49e | |||
f9b2c7f9c3 | |||
4115ae7ced | |||
8cc0fd321f | |||
a7541b1f76 | |||
b9ff75c4e9 | |||
4447a0d183 | |||
545af23536 | |||
db905a2048 | |||
712853d1e3 | |||
c6298ede72 | |||
b1d99e7608 | |||
5927a205e3 | |||
8c05f3b78c | |||
92db82e3d4 | |||
f6f689f9cd | |||
5dc3edf07c | |||
71625ec1cf | |||
c9216cf96c | |||
53713c0098 | |||
222c33ec32 | |||
19a50cfdc3 | |||
9892eb1c03 | |||
587131a6bd | |||
65d0d5e3e0 | |||
2642fc6560 | |||
a855c0cc79 | |||
876924df6a | |||
519b5acc91 | |||
e812653acd | |||
1b23f0efe1 | |||
f6559ba8b1 | |||
3b72b925b0 | |||
15d218a112 | |||
0a08a24896 | |||
cca6d3b04b | |||
be367988ae | |||
ea4da08af0 | |||
60ad1ab9e3 | |||
201b5a9614 | |||
8b270141f3 | |||
ceb62d7617 | |||
099fa9f005 | |||
dd7d1baece | |||
d8efa336e5 | |||
76dd4066f3 | |||
9b48864c5b | |||
14a41bdd48 | |||
25bc89a4f5 | |||
2f0824ab88 | |||
8ba6f4dad2 | |||
ada5d488fa | |||
b16245ad58 | |||
8b28133bee | |||
7a4dd96406 | |||
81a446db82 | |||
7bc62821ee | |||
1319daba54 | |||
39e3e2b794 | |||
ab804f7f49 | |||
f2b932960e | |||
d1fb1e26f3 | |||
fd5f511b33 | |||
cddd0f4653 | |||
256c7cea07 | |||
d91ec2d044 | |||
ad920f9597 | |||
728335581f | |||
9b37256175 | |||
951d482bc6 | |||
33530d62c7 | |||
65e602d8c7 | |||
6a1e7103f6 | |||
f25d0a0dc9 | |||
3b480caab1 | |||
dcc04089d1 | |||
0b87b21d52 | |||
b92e6d6acd | |||
538038867b | |||
e372311afe | |||
4a95f795cb | |||
fcfe82a7b8 | |||
a8d15bccba | |||
aec1b7caad | |||
5eba6067a3 | |||
664d18836e | |||
ac65cf455e | |||
b8bb08649d | |||
57866c45cd | |||
fbf4b45e76 | |||
431ed7210b | |||
043b31df70 | |||
2e30572364 | |||
fd64c46c74 | |||
61e49c2659 | |||
441b1238a5 | |||
0a47df7bb7 | |||
5e05afecf2 | |||
7ef64b5f5f | |||
0169fe8cf6 | |||
f119c273fd | |||
dac489b7f6 | |||
6ec11a2b26 | |||
4640e3f5b0 | |||
36777896c2 | |||
6e25a27870 | |||
0b4f682233 | |||
d184a7662c | |||
3f0a143037 | |||
b46d336d2b | |||
7d6b0c1376 | |||
dd40aeaa79 | |||
2b6f22b84d | |||
689aff0232 | |||
30a449841c | |||
56543e1311 | |||
9b175a7681 | |||
d67a801f1f | |||
95d7c0e800 | |||
059fc5ef7d | |||
7fed33fb49 | |||
ce9702571b | |||
01ec1c5777 | |||
ec8dd6410e | |||
45d7046f99 | |||
5fcd41b962 | |||
6ba8a15d3a | |||
5b615e33a0 | |||
823f2c1067 | |||
19732341d6 | |||
0e44eb4c1c | |||
50461b4d7d | |||
c11126181e | |||
658c301e3c | |||
dce52ed081 | |||
f309f586c9 | |||
ae1b10dba8 | |||
860a256f4b | |||
cb2f46ed08 | |||
13deaa66fd | |||
3597a5b0ed | |||
0352995cb3 | |||
e9041da7f4 | |||
252180e088 | |||
6361c208bd | |||
2ef8ace543 | |||
0400bcc85e | |||
76db6a5a16 | |||
5b171f9577 | |||
4cec28416e | |||
3b32baccf6 | |||
16875d7776 | |||
a9600e23a1 | |||
a4b6813ebf | |||
ef90938eb9 | |||
66891274a7 | |||
f52276bd06 | |||
7dce8dbfaa | |||
3b757ee738 | |||
0a45ed7af6 | |||
1db2dc3f58 | |||
953c75575b | |||
8c7ff259af | |||
3e666130c2 | |||
2babfa0625 | |||
83af40679a | |||
ce31c1d704 | |||
b20a74a22c | |||
66461e4c81 | |||
6ba9439bbb | |||
6764ec79ae | |||
6f4c378933 | |||
b121fa2cc9 | |||
ca26e85fd4 | |||
0714380360 | |||
b9af7952a4 | |||
ea6820ed51 | |||
a1e46de462 | |||
1abe213844 | |||
0372ae8ba5 | |||
a1e69a0e9d | |||
49e1e98914 | |||
34b316d7d5 | |||
9abc6791c7 | |||
5459823667 | |||
4fa8c5ec47 | |||
239034714f | |||
8a3a974ea3 | |||
db607c4a6f | |||
a53f07a8f6 | |||
c3e996b96c | |||
a218437cf4 | |||
0add0ca9b1 | |||
eaa4aa964c | |||
74bb899ce2 | |||
69fe7a1b8c | |||
ce856efa25 | |||
b54514df6e | |||
551616c3ab | |||
cec307ce7f | |||
e7bc8e03fc | |||
9a025bde8b | |||
6845e400cd | |||
25d0fa42e2 | |||
52208a6606 | |||
6725b22fd4 | |||
21bac43015 | |||
0ddd11f81b | |||
6d47c44335 | |||
3cbc908a6e | |||
95b1e75f1b | |||
bcfe0ad12c | |||
2088a6177f | |||
c9733d27df | |||
dd24cf57c6 | |||
451a4c0969 | |||
dabcae7b4d | |||
eae5721f8e | |||
ce1a675dd5 | |||
6ba1016024 | |||
b5f175b78e | |||
aff7efd891 | |||
1d00c2d4b4 | |||
61483a4c47 | |||
2b21430abe | |||
1781ad140e | |||
d2e3ad4dbc | |||
7615aaef7a | |||
92e3168309 | |||
ca148cbcf1 | |||
e57f037a10 | |||
46ad86fab2 | |||
511e164f17 | |||
6f18fc4bf4 | |||
8ca34303f2 | |||
882fc8ab54 | |||
9e5ae25abf | |||
9316dfed2e | |||
702932c69b | |||
29461fa910 | |||
f7173e6645 | |||
11b11a9d71 | |||
9da69dfc36 | |||
e5f4021a4f | |||
4dcbc6a3aa | |||
7a72d8fd58 | |||
5edfd55e00 | |||
2951daaa01 | |||
23f7c4aaf7 | |||
3dc5c4a6de | |||
a97ae3bc35 | |||
53d2fb3ad2 | |||
4f7e507716 | |||
f2e3a840db | |||
f45c18116a | |||
2365c608f9 | |||
6c6daaa619 | |||
857a399bfa | |||
6e230f0a07 | |||
7402401057 | |||
fe17dfe01d | |||
4885b38be5 | |||
5d4a5981d0 | |||
0fcb4533b5 | |||
422f81b60c | |||
e0d2f3ae53 | |||
531d9048b3 | |||
18495d122d | |||
9bcacd97df | |||
d91eae26e5 | |||
d113ed9d83 | |||
e113754312 | |||
b1afa9da5d | |||
700c5791f8 | |||
97d0a6dfe6 | |||
a54d020c97 | |||
36b696f433 | |||
f42cd6eefd | |||
46ebb39f65 | |||
c88a51cce8 | |||
3a7b03f022 | |||
94450a1d74 | |||
c35ad20249 | |||
5e8b4a81e9 | |||
21a779235d | |||
1006467f33 | |||
cffe49de5c | |||
7962d8cdab | |||
36dba33808 | |||
c273908471 | |||
bf8661bf45 | |||
499b522183 | |||
13bfe7a65c | |||
e90ec04441 | |||
a59a5caaf3 | |||
5b9f81fb46 | |||
d3aaebb352 | |||
a99f920fe0 | |||
fb5527c58c | |||
fcd3c1dd89 | |||
3a9500be67 | |||
1d93b86569 | |||
aca2cd41d8 | |||
1035f733aa | |||
2818d94010 | |||
1d1c34eb60 | |||
ca06fae22e | |||
36f527269b | |||
7cfe20e07f | |||
1fd82b5162 | |||
0ace5f64f8 | |||
e532524c7e | |||
ebd96f892e | |||
4b9b34fa4d | |||
bcc1e7ae73 | |||
27accf42f2 | |||
96f2e07cdd | |||
192c4f3cfc | |||
cb274032da | |||
b890fa56d8 | |||
b817b46494 | |||
49ba59369a | |||
d09cb88e9a | |||
44c3f4f772 | |||
948a67a51f | |||
de79609550 | |||
d18e10535e | |||
2954583373 | |||
ccdecdea11 | |||
946388d1e9 | |||
10ae8ffb55 | |||
bdba9990fb | |||
50166e88c0 | |||
f2bc826a20 | |||
ac48d66203 | |||
f68e7fcb9f | |||
0c3a22758b | |||
9cc859a318 | |||
7dfc8f3364 | |||
e397571f83 | |||
d34d7c26de | |||
77defa1d8b | |||
5d01497b8b | |||
cb8732b255 | |||
3f64b2ea5a | |||
57b38a2876 | |||
058f91909d | |||
4f75e63e25 | |||
2e50ff5e7c | |||
0b7477e72c | |||
4dc8aa6dfa | |||
161c3ccf1c | |||
170da7fec1 | |||
d7af9a4498 | |||
3cb3b1d63a | |||
fc66919e1f | |||
de5b4e7dcb | |||
561576961c | |||
2946dabd24 | |||
969eefc81f | |||
efee7262b6 | |||
1102d6e478 | |||
603aab0a81 | |||
10e934e234 | |||
48b9e6c517 | |||
5ae2ef5407 | |||
cafa05dab5 | |||
6345835b02 | |||
1d453b5732 | |||
d3a1b86ea4 | |||
bb27ca5720 | |||
065c0b6202 | |||
b4819701b4 | |||
daed05755a | |||
fb192f706c | |||
350b0a38e2 | |||
9ccbda4a40 | |||
821cbae187 | |||
528f3556ea | |||
489afea605 | |||
688a133f3f | |||
0cd37b0142 | |||
9fb789dc78 | |||
770444af55 | |||
703d9b2a07 | |||
a96509f286 | |||
8553f2e41c | |||
56c96dde8b | |||
9793d60c5a | |||
4ff97e7cbd | |||
75fe328f64 | |||
b28b006d61 | |||
c3b1c982bc | |||
b098399bbc | |||
65bbdff3fc | |||
fba3e682ea | |||
bccff4ffef | |||
9843c2d764 | |||
c9abbec48a | |||
bc79f9504b | |||
626b0f1a8f | |||
da95af0f48 | |||
28b6f643d8 | |||
22277c7277 | |||
19e7b217c1 | |||
29d3541b74 | |||
b9e5b3a55c | |||
b1ac865abd | |||
21504c0621 | |||
056ea541dd | |||
871fddf5fb | |||
c284858d06 | |||
bb4d888661 | |||
ff77cfc9b5 | |||
80f91ecb7b | |||
6cb77465ab | |||
6f6127cbb6 | |||
e1d85fb636 | |||
74b5c92787 | |||
ac1f97e2eb | |||
40f486b108 | |||
b62e67401c | |||
dd0a0134a6 | |||
5e24000799 | |||
c5cdfcb120 | |||
549a6694d2 | |||
14e34e1d15 | |||
e5393bf46a | |||
878a201bb1 | |||
b0e4619e0a | |||
aec65777a7 | |||
31bad8ddd2 | |||
79d65c907f | |||
92231d984f | |||
2fbc77955e | |||
c91819f551 | |||
b3f6ff5a36 | |||
52f84bee3c | |||
dda6857fee | |||
0215483fb3 | |||
37bb59df13 | |||
db34837d2d | |||
ba49677794 | |||
1a376c1bfa | |||
ef807d9d0e | |||
2a4893d946 | |||
cee061b363 | |||
1bbdf304ab | |||
6155068f9e | |||
0c4e3bb1c4 | |||
4547ff0c19 | |||
b7cce4d91d | |||
475250ce71 | |||
f40f231a63 | |||
d83d92adf2 | |||
d15282bff1 | |||
00637c4f0b | |||
2e035a7f45 | |||
17577175f8 | |||
933fab6ed4 | |||
7e5661caff | |||
19dfb138a6 | |||
0930f2614c | |||
1b20521e5c | |||
0e351db91a | |||
9cca501650 | |||
2481cbe4ab | |||
bc3959d1e0 | |||
1ac815e457 | |||
55b83062d0 | |||
391f77ce5e | |||
bc30a3d2e5 | |||
ff5de4ff03 | |||
f433a4d67c | |||
994061af44 | |||
501020921e | |||
53c09405b2 | |||
1f0079a274 | |||
15afe43cf0 | |||
eb8f7ba3b2 | |||
901a6f7fed | |||
fc92f62136 | |||
ff09456cf5 | |||
32fcfde86b | |||
363a1b3371 | |||
c376c6fb02 | |||
8e6e23b8d0 | |||
63e5d56441 | |||
e9bfd943fc | |||
614e2286c2 | |||
768a74c4bf | |||
612e9e11de | |||
06c72d54be | |||
be68bacc94 | |||
317d7bc988 | |||
b44e417bca | |||
9d67ce484d | |||
78b8602cf6 | |||
83a0a7681b | |||
c5e0e41ce7 | |||
b3fe4a1e91 | |||
f59bf389d9 | |||
231b8f9f92 | |||
dac153d24a | |||
3661d146a7 | |||
3bf55a3e07 | |||
5226467fc2 | |||
54deaa1b24 | |||
7ff667f58d | |||
58ba9b02ed | |||
d35da0ce99 | |||
041e6a1078 | |||
1c73c36ed7 | |||
69de32f700 | |||
4bfee76ead | |||
4d7afd9b4f | |||
cfe60c0640 |
113
.ci/check-abi
Executable file
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
|
||||
import argparse
|
||||
import contextlib
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def format_title(title):
|
||||
box = {
|
||||
'tl': '╔', 'tr': '╗', 'bl': '╚', 'br': '╝', 'h': '═', 'v': '║',
|
||||
}
|
||||
hline = box['h'] * (len(title) + 2)
|
||||
|
||||
return '\n'.join([
|
||||
f"{box['tl']}{hline}{box['tr']}",
|
||||
f"{box['v']} {title} {box['v']}",
|
||||
f"{box['bl']}{hline}{box['br']}",
|
||||
])
|
||||
|
||||
|
||||
def rm_rf(path):
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def sanitize_path(name):
|
||||
return name.replace('/', '-')
|
||||
|
||||
|
||||
def get_current_revision():
|
||||
revision = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
|
||||
encoding='utf-8').strip()
|
||||
|
||||
if revision == 'HEAD':
|
||||
# This is a detached HEAD, get the commit hash
|
||||
revision = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip().decode('utf-8')
|
||||
|
||||
return revision
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def checkout_git_revision(revision):
|
||||
current_revision = get_current_revision()
|
||||
subprocess.check_call(['git', 'checkout', '-q', revision])
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
subprocess.check_call(['git', 'checkout', '-q', current_revision])
|
||||
|
||||
|
||||
def build_install(revision):
|
||||
build_dir = '_build'
|
||||
dest_dir = os.path.abspath(sanitize_path(revision))
|
||||
print(format_title(f'# Building and installing {revision} in {dest_dir}'),
|
||||
end='\n\n', flush=True)
|
||||
|
||||
with checkout_git_revision(revision):
|
||||
rm_rf(build_dir)
|
||||
rm_rf(revision)
|
||||
|
||||
subprocess.check_call(['meson', build_dir,
|
||||
'--prefix=/usr', '--libdir=lib',
|
||||
'-Dx11-examples=false', '-Ddoc=false', '-Dgtk-examples=false'])
|
||||
subprocess.check_call(['ninja', '-v', '-C', build_dir])
|
||||
subprocess.check_call(['ninja', '-v', '-C', build_dir, 'install'],
|
||||
env={'DESTDIR': dest_dir})
|
||||
|
||||
return dest_dir
|
||||
|
||||
|
||||
def compare(old_tree, new_tree):
|
||||
print(format_title(f'# Comparing the two ABIs'), end='\n\n', flush=True)
|
||||
|
||||
old_headers = os.path.join(old_tree, 'usr', 'include')
|
||||
old_lib = os.path.join(old_tree, 'usr', 'lib', 'libfprint.so')
|
||||
|
||||
new_headers = os.path.join(new_tree, 'usr', 'include')
|
||||
new_lib = os.path.join(new_tree, 'usr', 'lib', 'libfprint.so')
|
||||
|
||||
subprocess.check_call([
|
||||
'abidiff', '--headers-dir1', old_headers, '--headers-dir2', new_headers,
|
||||
'--drop-private-types', '--fail-no-debug-info', '--no-added-syms', old_lib, new_lib])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('old', help='the previous revision, considered the reference')
|
||||
parser.add_argument('new', help='the new revision, to compare to the reference')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.old == args.new:
|
||||
print("Let's not waste time comparing something to itself")
|
||||
sys.exit(0)
|
||||
|
||||
old_tree = build_install(args.old)
|
||||
new_tree = build_install(args.new)
|
||||
|
||||
try:
|
||||
compare(old_tree, new_tree)
|
||||
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
|
||||
print(f'Hurray! {args.old} and {args.new} are ABI-compatible!')
|
10
.git-blame-ignore-revs
Normal file
|
@ -0,0 +1,10 @@
|
|||
# The commits that did automated reformatting. You can ignore them
|
||||
# during git-blame with `--ignore-rev` or `--ignore-revs-file`.
|
||||
#
|
||||
# $ git config --add 'blame.ignoreRevsFile' '.git-blame-ignore-revs'
|
||||
#
|
||||
|
||||
d1fb1e26f3b79e54febc94496c1184763cf2af3d
|
||||
e4f9935706be4c0e3253afe251c182019ff7ccef
|
||||
65e602d8c72baa7020efb62d10bf28e621feb05d
|
||||
4115ae7ced77d392ee11ea55212206d9404356f0
|
23
.gitignore
vendored
|
@ -1,24 +1,3 @@
|
|||
ltmain.sh
|
||||
missing
|
||||
stamp-h1
|
||||
libtool
|
||||
*.la
|
||||
*.lo
|
||||
*.o
|
||||
*.swp
|
||||
Makefile
|
||||
Makefile.in
|
||||
config.h*
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
config.guess
|
||||
config.log
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
.deps
|
||||
.libs
|
||||
compile
|
||||
ChangeLog
|
||||
_build
|
||||
|
|
174
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,174 @@
|
|||
include:
|
||||
- local: '.gitlab-ci/libfprint-templates.yaml'
|
||||
- project: 'freedesktop/ci-templates'
|
||||
ref: master
|
||||
file: '/templates/fedora.yml'
|
||||
- remote: 'https://gitlab.gnome.org/GNOME/citemplates/-/raw/master/flatpak/flatpak_ci_initiative.yml'
|
||||
|
||||
variables:
|
||||
extends: .libfprint_common_variables
|
||||
FDO_DISTRIBUTION_TAG: latest
|
||||
FDO_DISTRIBUTION_VERSION: rawhide
|
||||
FDO_UPSTREAM_REPO: "libfprint/$CI_PROJECT_NAME"
|
||||
FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG"
|
||||
BUNDLE: "org.freedesktop.libfprint.Demo.flatpak"
|
||||
LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546"
|
||||
|
||||
stages:
|
||||
- check-source
|
||||
- build
|
||||
- test
|
||||
- flatpak
|
||||
|
||||
image: $FEDORA_IMAGE
|
||||
|
||||
.build_one_driver_template: &build_one_driver
|
||||
script:
|
||||
# Build with a driver that doesn't need imaging, or nss
|
||||
- meson --werror -Ddrivers=$driver . _build
|
||||
- ninja -C _build
|
||||
- rm -rf _build/
|
||||
|
||||
.build_template: &build
|
||||
script:
|
||||
# And build with everything
|
||||
- meson --werror -Ddrivers=all . _build
|
||||
- ninja -C _build
|
||||
- ninja -C _build install
|
||||
|
||||
.build_template: &check_abi
|
||||
script:
|
||||
- ./.ci/check-abi ${LAST_ABI_BREAK} $(git rev-parse HEAD)
|
||||
|
||||
build:
|
||||
stage: build
|
||||
except:
|
||||
variables:
|
||||
- $CI_PIPELINE_SOURCE == "schedule"
|
||||
variables:
|
||||
driver: virtual_image
|
||||
<<: *build_one_driver
|
||||
<<: *build
|
||||
# <<: *check_abi
|
||||
artifacts:
|
||||
expose_as: "HTML Documentation"
|
||||
paths:
|
||||
- _build/doc/html
|
||||
- _build/doc/html/index.html
|
||||
expire_in: 1 week
|
||||
|
||||
test:
|
||||
stage: test
|
||||
except:
|
||||
variables:
|
||||
- $CI_PIPELINE_SOURCE == "schedule"
|
||||
script:
|
||||
- meson --werror -Ddrivers=all -Db_coverage=true . _build
|
||||
- ninja -C _build
|
||||
- meson test -C _build --print-errorlogs --no-stdsplit --timeout-multiplier 3
|
||||
- ninja -C _build coverage
|
||||
- cat _build/meson-logs/coverage.txt
|
||||
artifacts:
|
||||
expose_as: 'Coverage Report'
|
||||
when: always
|
||||
paths:
|
||||
- _build/meson-logs
|
||||
- _build/meson-logs/coveragereport/index.html
|
||||
expire_in: 1 week
|
||||
coverage: '/^TOTAL.*\s+(\d+\%)$/'
|
||||
|
||||
test_valgrind:
|
||||
stage: test
|
||||
except:
|
||||
variables:
|
||||
- $CI_PIPELINE_SOURCE == "schedule"
|
||||
script:
|
||||
- meson -Ddrivers=all . _build
|
||||
- ninja -C _build
|
||||
- meson test -C _build --print-errorlogs --no-stdsplit --setup=valgrind
|
||||
artifacts:
|
||||
expose_as: 'Valgrind test logs'
|
||||
when: always
|
||||
paths:
|
||||
- _build/meson-logs
|
||||
- _build/meson-logs/testlog-valgrind.txt
|
||||
expire_in: 1 week
|
||||
|
||||
|
||||
test_scan_build:
|
||||
stage: test
|
||||
except:
|
||||
variables:
|
||||
- $CI_PIPELINE_SOURCE == "schedule"
|
||||
allow_failure: true
|
||||
script:
|
||||
- meson -Ddrivers=all . _build
|
||||
# Wrapper to add --status-bugs and disable malloc checker
|
||||
- SCANBUILD=$CI_PROJECT_DIR/.gitlab-ci/scan-build ninja -C _build scan-build
|
||||
artifacts:
|
||||
paths:
|
||||
- _build/meson-logs
|
||||
expire_in: 1 week
|
||||
|
||||
test_indent:
|
||||
stage: check-source
|
||||
except:
|
||||
variables:
|
||||
- $CI_PIPELINE_SOURCE == "schedule"
|
||||
script:
|
||||
- scripts/uncrustify.sh
|
||||
- git diff
|
||||
- "! git status -s | grep -q ."
|
||||
|
||||
test_unsupported_list:
|
||||
stage: check-source
|
||||
except:
|
||||
variables:
|
||||
- $CI_PIPELINE_SOURCE == "schedule"
|
||||
allow_failure: true
|
||||
script:
|
||||
- tests/hwdb-check-unsupported.py
|
||||
|
||||
flatpak:
|
||||
stage: flatpak
|
||||
extends: .flatpak
|
||||
image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:3.36
|
||||
variables:
|
||||
MANIFEST_PATH: "demo/org.freedesktop.libfprint.Demo.json"
|
||||
FLATPAK_MODULE: "libfprint"
|
||||
APP_ID: "org.freedesktop.libfprint.Demo"
|
||||
rules:
|
||||
- if: '$CI_PROJECT_PATH != "libfprint/libfprint"'
|
||||
when: never
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
when: never
|
||||
- if: '$CI_COMMIT_BRANCH == "master"'
|
||||
when: always
|
||||
- if: '$CI_COMMIT_TAG'
|
||||
when: always
|
||||
# For any other (commit), allow manual run.
|
||||
# This excludes MRs which would create a duplicate pipeline
|
||||
- if: '$CI_COMMIT_BRANCH'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
|
||||
# CONTAINERS creation stage
|
||||
container_fedora_build:
|
||||
extends: .fdo.container-build@fedora
|
||||
only:
|
||||
variables:
|
||||
- $CI_PIPELINE_SOURCE == "schedule" && $CRON_TASK == "BUILD_CI_IMAGES"
|
||||
variables:
|
||||
GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image
|
||||
FDO_FORCE_REBUILD: 1
|
||||
# a list of packages to install
|
||||
FDO_DISTRIBUTION_PACKAGES:
|
||||
$LIBFPRINT_DEPENDENCIES
|
||||
vala
|
||||
libpcap-devel
|
||||
libudev-devel
|
||||
FDO_DISTRIBUTION_EXEC: |
|
||||
git clone https://github.com/martinpitt/umockdev.git && \
|
||||
cd umockdev && \
|
||||
meson _build --prefix=/usr && \
|
||||
ninja -C _build && ninja -C _build install
|
29
.gitlab-ci/libfprint-templates.yaml
Normal file
|
@ -0,0 +1,29 @@
|
|||
.libfprint_common_variables:
|
||||
LIBFPRINT_DEPENDENCIES:
|
||||
doxygen
|
||||
flatpak-builder
|
||||
gcc
|
||||
gcc-c++
|
||||
gcovr
|
||||
git
|
||||
glib2-devel
|
||||
glibc-devel
|
||||
gobject-introspection-devel
|
||||
gtk-doc
|
||||
gtk3-devel
|
||||
libabigail
|
||||
libgusb-devel
|
||||
libgudev-devel
|
||||
libX11-devel
|
||||
libXv-devel
|
||||
meson
|
||||
nss-devel
|
||||
pixman-devel
|
||||
python3-cairo
|
||||
python3-gobject
|
||||
systemd
|
||||
umockdev
|
||||
uncrustify
|
||||
valgrind
|
||||
clang-analyzer
|
||||
diffutils
|
4
.gitlab-ci/scan-build
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This wrapper just disables the malloc checker
|
||||
exec /usr/bin/scan-build --status-bugs -disable-checker unix.Malloc "$@"
|
96
HACKING
|
@ -1,96 +0,0 @@
|
|||
Copyright notices
|
||||
=================
|
||||
|
||||
If you make a contribution substantial enough to add or update a copyright
|
||||
notice on a file, such notice must be mirrored in the AUTHORS file. This is
|
||||
to make it easy for people to comply to section 6 of the LGPL, which states
|
||||
that a "work that uses the Library" must include copyright notices from
|
||||
this library. By providing them all in one place, hopefully we save such
|
||||
users some time.
|
||||
|
||||
|
||||
USB
|
||||
===
|
||||
|
||||
At the time of development, there are no known consumer fingerprint readers
|
||||
which do not operate over the USB bus. Therefore the library is designed around
|
||||
the fact that each driver drivers USB devices, and each device is a USB device.
|
||||
If we were to ever support a non-USB device, some rearchitecting would be
|
||||
needed, but this would not be a substantial task.
|
||||
|
||||
|
||||
GLib
|
||||
====
|
||||
|
||||
Although the library uses GLib internally, libfprint is designed to provide
|
||||
a completely neutral interface to it's application users. So, the public
|
||||
APIs should never return GLib data types or anything like that.
|
||||
|
||||
|
||||
Two-faced-ness
|
||||
==============
|
||||
|
||||
Like any decent library, this one is designed to provide a stable and
|
||||
documented API to it's users: applications. Clear distinction is made between
|
||||
data available internally in the library, and data/functions available to
|
||||
the applications.
|
||||
|
||||
This library is confused a little by the fact that there is another 'interface'
|
||||
at hand: the internal interface provided to drivers. So, we effectively end
|
||||
up with 2 APIs:
|
||||
|
||||
1. The external-facing API for applications
|
||||
2. The internal API for fingerprint drivers
|
||||
|
||||
Non-static functions which are intended for internal use only are prepended
|
||||
with the "fpi_" prefix.
|
||||
|
||||
|
||||
API stability
|
||||
=============
|
||||
|
||||
No API stability has been promised to anyone: go wild, there's no issue with
|
||||
breaking APIs at this point in time.
|
||||
|
||||
|
||||
Portability
|
||||
===========
|
||||
|
||||
libfprint is primarily written for Linux. However, I'm interested in
|
||||
supporting efforts to port this to other operating systems too.
|
||||
|
||||
You should ensure code is portable wherever possible. Try and use GLib rather
|
||||
than OS-specific features.
|
||||
|
||||
Endianness must be considered in all code. libfprint must support both big-
|
||||
and little-endian systems.
|
||||
|
||||
|
||||
Coding Style
|
||||
============
|
||||
|
||||
This project follows Linux kernel coding style but with a tab width of 4.
|
||||
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
All additions of public API functions must be accompanied with doxygen
|
||||
comments.
|
||||
|
||||
All changes which potentially change the behaviour of the public API must
|
||||
be reflected by updating the appropriate doxygen comments.
|
||||
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
Patches should be sent to the fprint bugzilla:
|
||||
https://bugs.freedesktop.org/enter_bug.cgi?product=libfprint
|
||||
|
||||
Information about libfprint development repositories can be found here:
|
||||
http://www.freedesktop.org/wiki/Software/fprint/libfprint
|
||||
|
||||
If you're looking for ideas for things to work on, look at the TODO file or
|
||||
grep the source code for FIXMEs.
|
||||
|
73
HACKING.md
Normal file
|
@ -0,0 +1,73 @@
|
|||
# Contributing to libfprint
|
||||
|
||||
## GLib
|
||||
|
||||
Although the library uses GLib internally, libfprint is designed to provide
|
||||
a completely neutral interface to its application users. So, the public
|
||||
APIs should never return GLib data types.
|
||||
|
||||
## License clarification
|
||||
|
||||
Although this library's license could allow for shims that hook up into
|
||||
proprietary blobs to add driver support for some unsupported devices, the
|
||||
intent of the original authors, and of current maintainers of the library,
|
||||
was for this license to allow _integration into_ proprietary stacks, not
|
||||
_integration of_ proprietary code in the library.
|
||||
|
||||
As such, no code to integrate proprietary drivers will be accepted in libfprint
|
||||
upstream. Proprietary drivers would make it impossible to debug problems in
|
||||
libfprint, as we wouldn't know what the proprietary driver does behind the
|
||||
library's back. The closed source nature of drivers is usually used to hide
|
||||
parts of the hardware setup, such as encryption keys, or protocols, in order
|
||||
to protect the hardware's integrity. Unfortunately, this is only [security through
|
||||
obscurity](https://en.wikipedia.org/wiki/Security_through_obscurity).
|
||||
|
||||
We however encourage potential contributors to take advantage of libfprint's
|
||||
source availability to create such shims to make it easier to reverse-engineer
|
||||
proprietary drivers in order to create new free software drivers, to the extent
|
||||
permitted by local laws.
|
||||
|
||||
## Two-faced-ness
|
||||
|
||||
Like any decent library, this one is designed to provide a stable and
|
||||
documented API to its users: applications. Clear distinction is made between
|
||||
data available internally in the library, and data/functions available to
|
||||
the applications.
|
||||
|
||||
This library is confused a little by the fact that there is another 'interface'
|
||||
at hand: the internal interface provided to drivers. So, we effectively end
|
||||
up with 2 APIs:
|
||||
|
||||
1. The [external-facing API for applications](libfprint/fprint.h)
|
||||
2. The [internal API for fingerprint drivers](libfprint/drivers_api.h)
|
||||
|
||||
Non-static functions which are intended for internal use only are prepended
|
||||
with the "fpi_" prefix.
|
||||
|
||||
## Documentation
|
||||
|
||||
All additions of public API functions must be accompanied with gtk-doc
|
||||
comments.
|
||||
|
||||
All changes which potentially change the behaviour of the public API must
|
||||
be reflected by updating the appropriate gtk-doc comments.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Patches should be sent as merge requests to the gitlab page:
|
||||
https://gitlab.freedesktop.org/libfprint/libfprint/merge_requests
|
||||
|
||||
Drivers are not usually written by libfprint developers, but when they
|
||||
are, we require:
|
||||
- 3 stand-alone devices. Not in a laptop or another embedded device, as
|
||||
space is scarce, unless the device has special integration with that
|
||||
hardware.
|
||||
- specifications of the protocol.
|
||||
|
||||
If you are an end-user, you can file a feature request with the "Driver Request"
|
||||
tag on [libfprint's issue page](https://gitlab.freedesktop.org/libfprint/libfprint/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=Driver%20Request),
|
||||
or subscribe to an existing feature request there.
|
||||
|
||||
If you are an enterprising hacker, please file a new merge request with
|
||||
the driver patches integrated.
|
238
INSTALL
|
@ -1,234 +1,6 @@
|
|||
Installation Instructions
|
||||
*************************
|
||||
|
||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
||||
2006 Free Software Foundation, Inc.
|
||||
|
||||
This file is free documentation; the Free Software Foundation gives
|
||||
unlimited permission to copy, distribute and modify it.
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
Briefly, the shell commands `./configure; make; make install' should
|
||||
configure, build, and install this package. The following
|
||||
more-detailed instructions are generic; see the `README' file for
|
||||
instructions specific to this package.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, and a
|
||||
file `config.log' containing compiler output (useful mainly for
|
||||
debugging `configure').
|
||||
|
||||
It can also use an optional file (typically called `config.cache'
|
||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||
the results of its tests to speed up reconfiguring. Caching is
|
||||
disabled by default to prevent problems with accidental use of stale
|
||||
cache files.
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If you are using the cache, and at
|
||||
some point `config.cache' contains results you don't want to keep, you
|
||||
may remove or edit it.
|
||||
|
||||
The file `configure.ac' (or `configure.in') is used to create
|
||||
`configure' by a program called `autoconf'. You need `configure.ac' if
|
||||
you want to change it or regenerate `configure' using a newer version
|
||||
of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system.
|
||||
|
||||
Running `configure' might take a while. While running, it prints
|
||||
some messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Optionally, type `make check' to run any self-tests that come with
|
||||
the package.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
5. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'. There is
|
||||
also a `make maintainer-clean' target, but that is intended mainly
|
||||
for the package's developers. If you use it, you may have to get
|
||||
all sorts of other programs in order to regenerate files that came
|
||||
with the distribution.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that the
|
||||
`configure' script does not know about. Run `./configure --help' for
|
||||
details on some of the pertinent environment variables.
|
||||
|
||||
You can give `configure' initial values for configuration parameters
|
||||
by setting variables in the command line or in the environment. Here
|
||||
is an example:
|
||||
|
||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
||||
|
||||
*Note Defining Variables::, for more details.
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
With a non-GNU `make', it is safer to compile the package for one
|
||||
architecture at a time in the source code directory. After you have
|
||||
installed the package for one architecture, use `make distclean' before
|
||||
reconfiguring for another architecture.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' installs the package's commands under
|
||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||
can specify an installation prefix other than `/usr/local' by giving
|
||||
`configure' the option `--prefix=PREFIX'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
||||
PREFIX as the prefix for installing programs and libraries.
|
||||
Documentation and other data files still use the regular prefix.
|
||||
|
||||
In addition, if you use an unusual directory layout you can give
|
||||
options like `--bindir=DIR' to specify different values for particular
|
||||
kinds of files. Run `configure --help' for a list of the directories
|
||||
you can set and what kinds of files go in them.
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' cannot figure out automatically,
|
||||
but needs to determine by the type of machine the package will run on.
|
||||
Usually, assuming the package is built to be run on the _same_
|
||||
architectures, `configure' can figure that out, but if it prints a
|
||||
message saying it cannot guess the machine type, give it the
|
||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
where SYSTEM can have one of these forms:
|
||||
|
||||
OS KERNEL-OS
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the machine type.
|
||||
|
||||
If you are _building_ compiler tools for cross-compiling, you should
|
||||
use the option `--target=TYPE' to select the type of system they will
|
||||
produce code for.
|
||||
|
||||
If you want to _use_ a cross compiler, that generates code for a
|
||||
platform different from the build platform, you should specify the
|
||||
"host" platform (i.e., that on which the generated programs will
|
||||
eventually be run) with `--host=TYPE'.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share, you
|
||||
can create a site shell script called `config.site' that gives default
|
||||
values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Defining Variables
|
||||
==================
|
||||
|
||||
Variables not defined in a site shell script can be set in the
|
||||
environment passed to `configure'. However, some packages may run
|
||||
configure again during the build, and the customized values of these
|
||||
variables may be lost. In order to avoid this problem, you should set
|
||||
them in the `configure' command line, using `VAR=value'. For example:
|
||||
|
||||
./configure CC=/usr/local2/bin/gcc
|
||||
|
||||
causes the specified `gcc' to be used as the C compiler (unless it is
|
||||
overridden in the site shell script).
|
||||
|
||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||
|
||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
|
||||
`configure' Invocation
|
||||
======================
|
||||
|
||||
`configure' recognizes the following options to control how it operates.
|
||||
|
||||
`--help'
|
||||
`-h'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--version'
|
||||
`-V'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Enable the cache: use and save the results of the tests in FILE,
|
||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||
disable caching.
|
||||
|
||||
`--config-cache'
|
||||
`-C'
|
||||
Alias for `--cache-file=config.cache'.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made. To
|
||||
suppress all normal output, redirect it to `/dev/null' (any error
|
||||
messages will still be shown).
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
libfprint uses the Meson build system. See
|
||||
http://mesonbuild.com/Quick-guide.html for details on how to
|
||||
compile libfprint.
|
||||
|
||||
The "meson configure" command will give you a list of available
|
||||
command-line options.
|
||||
|
|
13
MAINTAINERS
Normal file
|
@ -0,0 +1,13 @@
|
|||
Current maintainers of libfprint are:
|
||||
|
||||
* Benjamin Berg <bberg@redhat.com>
|
||||
* Marco Trevisan (Treviño) <mail@3v1n0.net>
|
||||
|
||||
Many drivers are not actively maintained and may not be fully functional.
|
||||
We are happy to receive contributions, but the support we can give is
|
||||
limitted unfortunately. For many drivers we may not even have test devices.
|
||||
|
||||
Maintained drivers are:
|
||||
* synaptics:
|
||||
Contributed and maintained by Synaptics Inc.
|
||||
Contact: Vincent Huang <vincent.huang@tw.synaptics.com>
|
25
Makefile.am
|
@ -1,25 +0,0 @@
|
|||
ACLOCAL_AMFLAGS = -I m4
|
||||
EXTRA_DIST = THANKS TODO HACKING libfprint.pc.in
|
||||
DISTCLEANFILES = ChangeLog libfprint.pc
|
||||
|
||||
SUBDIRS = libfprint doc
|
||||
|
||||
if BUILD_EXAMPLES
|
||||
SUBDIRS += examples
|
||||
endif
|
||||
|
||||
DIST_SUBDIRS = libfprint doc examples
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS = --with-drivers=all --enable-examples-build --enable-x11-examples-build --with-udev-rules-dir='$${libdir}/udev/rules.d-distcheck'
|
||||
|
||||
pkgconfigdir=$(libdir)/pkgconfig
|
||||
pkgconfig_DATA=libfprint.pc
|
||||
|
||||
.PHONY: ChangeLog dist-up
|
||||
ChangeLog:
|
||||
git --git-dir $(top_srcdir)/.git log > ChangeLog || touch ChangeLog
|
||||
|
||||
dist-hook: ChangeLog
|
||||
|
||||
dist-up: dist
|
||||
rsync $(distdir).tar.bz2 frs.sourceforge.net:uploads/
|
293
NEWS
|
@ -1,6 +1,299 @@
|
|||
This file lists notable changes in each release. For the full history of all
|
||||
changes, see ChangeLog.
|
||||
|
||||
2021-06-30: v1.94.0 release
|
||||
|
||||
Highlights:
|
||||
* Implement suspend/resume handling including USB wakeup configuration.
|
||||
This requires writing the "persist" and "wakeup" sysfs attributes.
|
||||
* Add simple temperature module to prevent devices from becoming too hot
|
||||
* Add feature for continuous scanning
|
||||
* New internal "critical section" API to simplify driver development
|
||||
* elan: new PID 0x0c58
|
||||
* elanmoc: Fixes for multi-user handling and FW changes
|
||||
* virtual-device: Do not time out for SCAN command
|
||||
|
||||
2021-06-30: v1.92.1 release
|
||||
|
||||
Highlights:
|
||||
* elanmoc: New driver for ELAN match-on-chip devices
|
||||
* egis0570: New driver for some Egis Technology devices
|
||||
* synaptics: Fix empty identify causing enroll issues
|
||||
* elan: Support more PIDs
|
||||
* misc: Architecture related bugfixes
|
||||
|
||||
2021-06-30: v1.92.0 release
|
||||
|
||||
Highlights:
|
||||
* Support for SPI devices was added together with the elanspi driver
|
||||
* Generate hwdb for autosuspend (which is now pulled by systemd)
|
||||
* An API was added to clear the device storage.
|
||||
Note: Devices may not implement the "list" API anymore.
|
||||
* Device features can now be queried using a common API
|
||||
|
||||
New drivers:
|
||||
* vfs7552
|
||||
* nb1010
|
||||
* elanspi
|
||||
|
||||
Driver changes:
|
||||
* uru4000: Fix deactivation when unplugged unexpectedly
|
||||
* goodixmoc: Correctly complete verify/identify after retry condition
|
||||
* goodixmoc: Support power shield feature
|
||||
* goodixmoc: Support new PIDs
|
||||
* synaptics: Fix driver lockup when sequence counter overflows (#358)
|
||||
* synaptics: Remove unnecessary device reset
|
||||
* synaptics: Support new PIDs
|
||||
* synaptics: Add clear_storage and remove list support
|
||||
* synaptics: Fix initialization if the device is still busy when opening
|
||||
* upeksonly: Fix double free in USB transfer callbacks
|
||||
* elan: Support new PIDs
|
||||
* vfs301: Fix leak of USB transfer
|
||||
* uru4000: Silence warning happening during startup
|
||||
|
||||
Internal API changes:
|
||||
* ssm: Add getter for the device
|
||||
* ssm: Add cleanup state feature
|
||||
* image-device: Allow overriding number of enroll stages
|
||||
* context: Support udev based device discovery
|
||||
* spi-transfer: Add SPI transfer helper routines
|
||||
|
||||
Other:
|
||||
* Use pcap based USB replay for CI
|
||||
* New virtual drivers for more advanced testing
|
||||
* Ensure async operations are run in the thread local main context
|
||||
* Disable drivers on big-endian unless they are verified to work
|
||||
* Add missing gobject-introspection dependency
|
||||
|
||||
|
||||
2020-12-01: v1.90.7 release
|
||||
|
||||
Highlights:
|
||||
* vfs5011: Fix possible use-after-free
|
||||
* goodixmoc: Add two new PIDs (0x63AC, 0x639C)
|
||||
* goodixmoc: Support finger status API
|
||||
* synaptics: Only identify within provided prints
|
||||
* synaptics: Reject devices with old firmware during probe (#239)
|
||||
|
||||
|
||||
2020-12-01: v1.90.6 release
|
||||
|
||||
This release is primarily a bugfix release for some older issues.
|
||||
|
||||
The major change is that fp_print_deserialize will now correctly return a
|
||||
sunken reference rather than a floating one. Most API users will have
|
||||
assumed this was true, and issues could happen at a later point.
|
||||
If any API user worked around this libfprint bug, they will now leak the
|
||||
returned print.
|
||||
|
||||
Highlights:
|
||||
* Object reference management fixes for FpPrint and identify
|
||||
* Fixed issues that caused problem on non-x86 machines (#236)
|
||||
* Fix building with older GLib versions
|
||||
* synaptics: Support PID 00e7
|
||||
* goodix: Fix issue with long USB packages
|
||||
|
||||
|
||||
2020-12-01: v1.90.5 release
|
||||
|
||||
The 1.90.4 release caused a major regression, as it included a USB hub in
|
||||
UDEV the autosupend rule list.
|
||||
|
||||
Highlights:
|
||||
* Remove USB hub from udev autosupend rules
|
||||
* synaptics: Add PID 0x00c9 which is used in some HP laptops
|
||||
|
||||
|
||||
2020-11-27: v1.90.4 release
|
||||
|
||||
This release contains a number of important bugfixes. On the feature side,
|
||||
the USB hotplug support was improved. A lot of drivers received fixes and
|
||||
improvements.
|
||||
|
||||
Highlights:
|
||||
* Work around GUsb cancellation issue
|
||||
* Redefine internal image device state machine for more robustness
|
||||
* Add public finger-status reporting to FpDevice
|
||||
* Rework device removal API to be convenient (#330)
|
||||
* Enable powersave for unsupported USB devices
|
||||
* Improvements to examples
|
||||
* synaptics: Support identify operation
|
||||
* synaptics: Fix possible crash when the interrupt transfer is resubmitted
|
||||
* synaptics: Add support for PIDs 0x00f9, 0x00fc and 0x00c2
|
||||
* elan: Add PID 0x0c4d to supported device list
|
||||
* aes3k: Fix driver and add CI test (#306)
|
||||
* uru4000: Fix reference counting of image transfer
|
||||
* vfs301: Fix driver and add CI test (#320)
|
||||
|
||||
2020-06-08: v1.90.3 release
|
||||
|
||||
This release mostly contains support for a number of new match-on-chip
|
||||
devices. Most notable is the addition of the new goodixmoc driver.
|
||||
Currently the driver has the small caveat that we have no strategy to
|
||||
garbage collect old prints yet (a simple strategy could be implemented
|
||||
in fprintd).
|
||||
|
||||
Highlights:
|
||||
* New goodixmoc driver supporting Goodix USB devices:
|
||||
27C6:5840
|
||||
27C6:6496
|
||||
27C6:60A2
|
||||
* Newly added support for Synaptics device:
|
||||
06CB:00E9
|
||||
06CB:00DF
|
||||
* Fixed an issue with Synaptics devices sometimes not working at boot
|
||||
* Fix issue with aes3k driver (#306)
|
||||
|
||||
2020-06-08: v1.90.2 release
|
||||
|
||||
This release contains a large amount of bug and regression fixes. These
|
||||
are not listed explicitly, but affect the majority of drivers.
|
||||
|
||||
Highlights:
|
||||
* A patch for nbis required for some sensors was accidentally dropped in
|
||||
an earlier release. Users of these sensors/drivers (aes1610, aes2501,
|
||||
aes2550, aes1660, aes2660, elan, upektc_img) need to re-enroll (#142).
|
||||
|
||||
2019-11-20: v1.90.1 release
|
||||
|
||||
This release fixes a lot of the regressions introduced in 1.90.0. Please note
|
||||
that both the driver and external APIs have changed, as both the verify and
|
||||
the identify functions now have early reporting mechanisms.
|
||||
The soname for the library, as well as a number of file locations have also
|
||||
changed. While this allows installation in parallel with the 1.0 version of
|
||||
libfprint, we recommend installing only one, and migrating from version 1.0 to
|
||||
version 2.0 alongside its main consumer (fprintd).
|
||||
|
||||
Only major changes are listed below. A lot of other cleanup work and small
|
||||
fixes have also been merged.
|
||||
|
||||
* Library:
|
||||
- Add support to run tests in gdb/valgrind
|
||||
- Allow testing on all architectures
|
||||
- Avoid image device AWAIT_FINGER_ON to deactivate state transitions
|
||||
- Fix verify/identify error propagation to library user
|
||||
- Correctly read image device information from class data
|
||||
- Continue enroll after an image driver reported a retry error
|
||||
- Change external API to allow reporting match results early
|
||||
- A lot of new unit tests and integration tests have been added
|
||||
|
||||
* Drivers API
|
||||
- Support variadic arguments in error functions
|
||||
- Various re-definitions of ownership handling
|
||||
- Add convenience API to change state after a timeout
|
||||
- Add unit tests for all the drivers API
|
||||
|
||||
* Drivers:
|
||||
- elan: Ensure correct deactivation of device
|
||||
- uru4000: Fix IRQ handler registration and internal state handling
|
||||
- uru4000: Fix control transfer request type
|
||||
- synaptics: Ensure errors are only reported after finger removal
|
||||
|
||||
2019-11-20: v1.90.0 release
|
||||
|
||||
This release updates the core of the library to use GLib routines and Gio
|
||||
style APIs. While the API both for library users remain similar in most
|
||||
ways, there are some changes and all users will need to be ported.
|
||||
|
||||
A large motivation for the in-depth changes was the requirement to add
|
||||
new API to support sensors that store the prints on the sensor. This
|
||||
support is already used by the new synaptics driver, which will support
|
||||
the current generation of the Prometheus MIS (match-in-sensor) chipset
|
||||
by Synaptics (USB ID 06cb:00bd).
|
||||
|
||||
The current codebase is considered stable at this point. However, due to
|
||||
the lack of wider testing it is only released as a 1.90.0 release which
|
||||
can be considered a beta-release for 2.0.
|
||||
|
||||
With the rewrite, it is now also possible to support devices that are not
|
||||
connected through USB (e.g. I2C). Another major improvement is that the
|
||||
library has now a test suite, testing both the library core and allowing
|
||||
tests of the drivers using umockdev.
|
||||
|
||||
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
|
||||
|
|
16
README
|
@ -2,7 +2,7 @@ libfprint
|
|||
=========
|
||||
|
||||
libfprint is part of the fprint project:
|
||||
http://www.reactivated.net/fprint
|
||||
https://fprint.freedesktop.org/
|
||||
|
||||
libfprint was originally developed as part of an academic project at the
|
||||
University of Manchester with the aim of hiding differences between different
|
||||
|
@ -22,7 +22,7 @@ university computers and the project is not hosted at the university either.
|
|||
|
||||
For more information on libfprint, supported devices, API documentation, etc.,
|
||||
see the homepage:
|
||||
http://www.reactivated.net/fprint/Libfprint
|
||||
https://fprint.freedesktop.org/
|
||||
|
||||
libfprint is licensed under the GNU LGPL version 2.1. See the COPYING file
|
||||
for the license text.
|
||||
|
@ -37,5 +37,15 @@ libfprint includes code from NIST's NBIS software distribution:
|
|||
http://fingerprint.nist.gov/NBIS/index.html
|
||||
We include bozorth3 from the US export controlled distribution. We have
|
||||
determined that it is fine to ship bozorth3 in an open source project,
|
||||
see http://reactivated.net/fprint/wiki/US_export_control
|
||||
see https://fprint.freedesktop.org/us-export-control.html
|
||||
|
||||
## Historical links
|
||||
|
||||
Older versions of libfprint are available at:
|
||||
https://sourceforge.net/projects/fprint/files/
|
||||
|
||||
Historical mailing-list archives:
|
||||
http://www.reactivated.net/fprint_list_archives/
|
||||
|
||||
Historical website:
|
||||
http://web.archive.org/web/*/https://www.freedesktop.org/wiki/Software/fprint/
|
||||
|
|
20
autogen.sh
|
@ -1,20 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
srcdir=`dirname $0`
|
||||
test -z "$srcdir" && srcdir=.
|
||||
|
||||
olddir="`pwd`"
|
||||
|
||||
cd "$srcdir"
|
||||
|
||||
libtoolize --copy --force || exit 1
|
||||
aclocal || exit 1
|
||||
autoheader || exit 1
|
||||
autoconf || exit 1
|
||||
automake -a -c || exit 1
|
||||
cd "$olddir"
|
||||
|
||||
if test -z "$NOCONFIGURE"; then
|
||||
$srcdir/configure --enable-maintainer-mode --enable-examples-build \
|
||||
--enable-x11-examples-build --enable-debug-log $*
|
||||
fi
|
3
code-of-conduct.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
This project and its community follow the [Freedesktop.org code of conduct]
|
||||
|
||||
[Freedesktop.org code of conduct]: https://www.freedesktop.org/wiki/CodeOfConduct/
|
428
configure.ac
|
@ -1,428 +0,0 @@
|
|||
AC_INIT([libfprint], [0.7.0])
|
||||
AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz check-news subdir-objects])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_SRCDIR([libfprint/core.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
# Enable silent build when available (Automake 1.11)
|
||||
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||
|
||||
AC_PREREQ([2.50])
|
||||
AC_PROG_CC
|
||||
AC_PROG_LIBTOOL
|
||||
AC_C_INLINE
|
||||
AM_PROG_CC_C_O
|
||||
AC_PROG_CXX
|
||||
AC_DEFINE([_GNU_SOURCE], [], [Use GNU extensions])
|
||||
|
||||
# Library versioning
|
||||
lt_major="0"
|
||||
lt_revision="0"
|
||||
lt_age="0"
|
||||
AC_SUBST(lt_major)
|
||||
AC_SUBST(lt_revision)
|
||||
AC_SUBST(lt_age)
|
||||
|
||||
all_drivers="upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes3500 aes4000 vfs101 vfs301 vfs5011 upektc_img etes603 vfs0050"
|
||||
|
||||
require_imaging='no'
|
||||
require_aeslib='no'
|
||||
require_aesX660='no'
|
||||
require_aes3k='no'
|
||||
enable_upeke2='no'
|
||||
enable_upekts='no'
|
||||
enable_upektc='no'
|
||||
enable_upeksonly='no'
|
||||
enable_vcom5s='no'
|
||||
enable_uru4000='no'
|
||||
enable_fdu2000='no'
|
||||
enable_aes1610='no'
|
||||
enable_aes1660='no'
|
||||
enable_aes2501='no'
|
||||
enable_aes2550='no'
|
||||
enable_aes2660='no'
|
||||
enable_aes3500='no'
|
||||
enable_aes4000='no'
|
||||
enable_vfs101='no'
|
||||
enable_vfs301='no'
|
||||
enable_vfs5011='no'
|
||||
enable_upektc_img='no'
|
||||
enable_etes603='no'
|
||||
enable_vfs0050='no'
|
||||
|
||||
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
|
||||
[List of drivers to enable])],
|
||||
[drivers="$withval"],
|
||||
[drivers="$all_drivers"])
|
||||
|
||||
if test "x$drivers" = "xall" ; then
|
||||
drivers=$all_drivers
|
||||
fi
|
||||
|
||||
for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
|
||||
case ${driver} in
|
||||
upekts)
|
||||
AC_DEFINE([ENABLE_UPEKTS], [], [Build UPEK TouchStrip driver])
|
||||
enable_upekts="yes"
|
||||
;;
|
||||
upeke2)
|
||||
AC_DEFINE([ENABLE_UPEKE2], [], [Build UPEK Eikon 2])
|
||||
enable_upeke2="yes"
|
||||
;;
|
||||
upektc)
|
||||
AC_DEFINE([ENABLE_UPEKTC], [], [Build UPEK TouchChip driver])
|
||||
enable_upektc="yes"
|
||||
;;
|
||||
upeksonly)
|
||||
AC_DEFINE([ENABLE_UPEKSONLY], [], [Build UPEK TouchStrip sensor-only driver])
|
||||
enable_upeksonly="yes"
|
||||
;;
|
||||
uru4000)
|
||||
AC_DEFINE([ENABLE_URU4000], [], [Build Digital Persona U.are.U 4000 driver])
|
||||
enable_uru4000="yes"
|
||||
;;
|
||||
fdu2000)
|
||||
AC_DEFINE([ENABLE_FDU2000], [], [Build Secugen FDU 2000 driver])
|
||||
enable_fdu2000="no"
|
||||
# Driver not ported
|
||||
;;
|
||||
vcom5s)
|
||||
AC_DEFINE([ENABLE_VCOM5S], [], [Build Veridicom 5thSense driver])
|
||||
enable_vcom5s="yes"
|
||||
;;
|
||||
aes2501)
|
||||
AC_DEFINE([ENABLE_AES2501], [], [Build AuthenTec AES2501 driver])
|
||||
require_aeslib="yes"
|
||||
enable_aes2501="yes"
|
||||
;;
|
||||
aes2550)
|
||||
AC_DEFINE([ENABLE_AES2550], [], [Build AuthenTec AES2550/AES2810 driver])
|
||||
require_aeslib="yes"
|
||||
enable_aes2550="yes"
|
||||
;;
|
||||
aes1610)
|
||||
AC_DEFINE([ENABLE_AES1610], [], [Build AuthenTec AES1610 driver])
|
||||
require_aeslib="yes"
|
||||
enable_aes1610="yes"
|
||||
;;
|
||||
aes1660)
|
||||
AC_DEFINE([ENABLE_AES1660], [], [Build AuthenTec AES1660 driver])
|
||||
require_aeslib="yes"
|
||||
require_aesX660="yes"
|
||||
enable_aes1660="yes"
|
||||
;;
|
||||
aes2660)
|
||||
AC_DEFINE([ENABLE_AES2660], [], [Build AuthenTec AES1660 driver])
|
||||
require_aeslib="yes"
|
||||
require_aesX660="yes"
|
||||
enable_aes2660="yes"
|
||||
;;
|
||||
aes3500)
|
||||
AC_DEFINE([ENABLE_AES3500], [], [Build AuthenTec AES3500 driver])
|
||||
require_aeslib="yes"
|
||||
require_imaging="yes"
|
||||
require_aes3k="yes"
|
||||
enable_aes3500="yes"
|
||||
;;
|
||||
aes4000)
|
||||
AC_DEFINE([ENABLE_AES4000], [], [Build AuthenTec AES4000 driver])
|
||||
require_aeslib="yes"
|
||||
require_imaging="yes"
|
||||
require_aes3k="yes"
|
||||
enable_aes4000="yes"
|
||||
;;
|
||||
vfs101)
|
||||
AC_DEFINE([ENABLE_VFS101], [], [Build Validity VFS101 driver])
|
||||
enable_vfs101="yes"
|
||||
;;
|
||||
vfs301)
|
||||
AC_DEFINE([ENABLE_VFS301], [], [Build Validity VFS301/VFS300 driver])
|
||||
enable_vfs301="yes"
|
||||
;;
|
||||
vfs5011)
|
||||
AC_DEFINE([ENABLE_VFS5011], [], [Build Validity VFS5011 driver])
|
||||
enable_vfs5011="yes"
|
||||
;;
|
||||
upektc_img)
|
||||
AC_DEFINE([ENABLE_UPEKTC_IMG], [], [Build Upek TouchChip Fingerprint Coprocessor driver])
|
||||
enable_upektc_img="yes"
|
||||
;;
|
||||
etes603)
|
||||
AC_DEFINE([ENABLE_ETES603], [], [Build EgisTec ES603 driver])
|
||||
enable_etes603="yes"
|
||||
;;
|
||||
vfs0050)
|
||||
AC_DEFINE([ENABLE_VFS0050], [], [Build Validity VFS0050 driver])
|
||||
enable_vfs0050="yes"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
AM_CONDITIONAL([ENABLE_UPEKTS], [test "$enable_upekts" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_UPEKE2], [test "$enable_upeke2" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_UPEKTC], [test "$enable_upektc" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_UPEKSONLY], [test "$enable_upeksonly" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_VCOM5S], [test "$enable_vcom5s" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_URU4000], [test "$enable_uru4000" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_FDU2000], [test "$enable_fdu2000" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES1660], [test "$enable_aes1660" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES2550], [test "$enable_aes2550" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES2660], [test "$enable_aes2660" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES3500], [test "$enable_aes3500" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" = "yes"])
|
||||
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"])
|
||||
AM_CONDITIONAL([REQUIRE_AESX660], [test "$require_aesX660" = "yes"])
|
||||
AM_CONDITIONAL([REQUIRE_AES3K], [test "$require_aes3k" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_VFS101], [test "$enable_vfs101" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_VFS301], [test "$enable_vfs301" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_VFS5011], [test "$enable_vfs5011" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_UPEKTC_IMG], [test "$enable_upektc_img" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_ETES603], [test "$enable_etes603" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_VFS0050], [test "$enable_vfs0050" = "yes"])
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
|
||||
AC_SUBST(LIBUSB_CFLAGS)
|
||||
AC_SUBST(LIBUSB_LIBS)
|
||||
|
||||
# check for OpenSSL's libcrypto
|
||||
PKG_CHECK_MODULES(CRYPTO, nss)
|
||||
AC_SUBST(CRYPTO_CFLAGS)
|
||||
AC_SUBST(CRYPTO_LIBS)
|
||||
|
||||
PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.28])
|
||||
AC_SUBST(GLIB_CFLAGS)
|
||||
AC_SUBST(GLIB_LIBS)
|
||||
|
||||
pixman_found=no
|
||||
|
||||
AC_ARG_ENABLE(udev-rules,
|
||||
AC_HELP_STRING([--enable-udev-rules],[Update the udev rules]),
|
||||
[case "${enableval}" in
|
||||
yes) ENABLE_UDEV_RULES=yes ;;
|
||||
no) ENABLE_UDEV_RULES=no ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-udev-rules) ;;
|
||||
esac],
|
||||
[ENABLE_UDEV_RULES=yes]) dnl Default value
|
||||
AM_CONDITIONAL(ENABLE_UDEV_RULES, test x$ENABLE_UDEV_RULES = "xyes")
|
||||
|
||||
if test $ENABLE_UDEV_RULES = no && test -d .git/ ; then
|
||||
AC_MSG_ERROR(--disable-udev-rules can only be used when building from tarballs)
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(udev-rules-dir,
|
||||
AS_HELP_STRING([--with-udev-rules-dir=DIR],[Installation path for udev rules @<:@auto@:>@]),
|
||||
[ac_with_udev_rules_dir=$withval],
|
||||
[ac_with_udev_rules_dir=""])
|
||||
|
||||
if test "${ac_with_udev_rules_dir}" = ""; then
|
||||
ac_with_udev_rules_dir=`$PKG_CONFIG --variable=udevdir udev`/rules.d
|
||||
fi
|
||||
AC_MSG_NOTICE([installing udev rules in ${ac_with_udev_rules_dir}])
|
||||
AC_SUBST([udev_rulesdir],[${ac_with_udev_rules_dir}])
|
||||
|
||||
if test "$require_imaging" = "yes"; then
|
||||
PKG_CHECK_MODULES(IMAGING, pixman-1, [pixman_found=yes], [pixman_found=no])
|
||||
if test "$pixman_found" != "yes"; then
|
||||
AC_MSG_ERROR([pixman is required for imaging support])
|
||||
fi
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([REQUIRE_PIXMAN], [test "$pixman_found" = "yes"])
|
||||
AC_SUBST(IMAGING_CFLAGS)
|
||||
AC_SUBST(IMAGING_LIBS)
|
||||
|
||||
# Examples build
|
||||
AC_ARG_ENABLE([examples-build], [AS_HELP_STRING([--enable-examples-build],
|
||||
[build example applications (default n)])],
|
||||
[build_examples=$enableval],
|
||||
[build_examples='no'])
|
||||
AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != "xno"])
|
||||
|
||||
# Examples build
|
||||
AC_ARG_ENABLE([x11-examples-build], [AS_HELP_STRING([--enable-x11-examples-build],
|
||||
[build X11 example applications (default n)])],
|
||||
[build_x11_examples=$enableval],
|
||||
[build_x11_examples='no'])
|
||||
AM_CONDITIONAL([BUILD_X11_EXAMPLES], [test "x$build_x11_examples" != "xno"])
|
||||
|
||||
|
||||
if test "x$build_x11_examples" != "xno"; then
|
||||
# check for Xv extensions
|
||||
# imported from Coriander
|
||||
AC_DEFUN([AC_CHECK_XV],[
|
||||
AC_SUBST(XV_CFLAGS)
|
||||
AC_SUBST(XV_LIBS)
|
||||
AC_MSG_CHECKING(for Xv extensions)
|
||||
AC_TRY_COMPILE([
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xvlib.h>],[
|
||||
int main(void) { (void) XvGetPortAttribute(0, 0, 0, 0); return 0; }
|
||||
],xv=yes,xv=no);
|
||||
AC_MSG_RESULT($xv)
|
||||
if test x$xv = xyes; then
|
||||
XV_LIBS="-lXv -lXext"
|
||||
XV_CFLAGS=""
|
||||
AC_DEFINE(HAVE_XV,1,[defined if XV video overlay is available])
|
||||
else
|
||||
AC_MSG_ERROR([XV is required for X11 examples])
|
||||
fi
|
||||
])
|
||||
AC_CHECK_XV
|
||||
fi
|
||||
|
||||
# Message logging
|
||||
AC_ARG_ENABLE([log], [AS_HELP_STRING([--disable-log], [disable all logging])],
|
||||
[log_enabled=$enableval],
|
||||
[log_enabled='yes'])
|
||||
if test "x$log_enabled" != "xno"; then
|
||||
AC_DEFINE([ENABLE_LOGGING], 1, [Message logging])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([debug-log], [AS_HELP_STRING([--enable-debug-log],
|
||||
[enable debug logging (default n)])],
|
||||
[debug_log_enabled=$enableval],
|
||||
[debug_log_enabled='no'])
|
||||
if test "x$debug_log_enabled" != "xno"; then
|
||||
AC_DEFINE([ENABLE_DEBUG_LOGGING], 1, [Debug message logging])
|
||||
fi
|
||||
|
||||
# Restore gnu89 inline semantics on gcc 4.3 and newer
|
||||
saved_cflags="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fgnu89-inline"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[]])], inline_cflags="-fgnu89-inline", inline_cflags="")
|
||||
CFLAGS="$saved_cflags"
|
||||
|
||||
AC_DEFINE([API_EXPORTED], [__attribute__((visibility("default")))], [Default visibility])
|
||||
AM_CFLAGS="-std=gnu99 $inline_cflags -Wall -Wundef -Wunused -Wstrict-prototypes -Werror-implicit-function-declaration -Wno-pointer-sign -Wshadow"
|
||||
AC_SUBST(AM_CFLAGS)
|
||||
|
||||
if test "$require_imaging" = "yes"; then
|
||||
if test x$pixman_found != no; then
|
||||
AC_MSG_NOTICE([** Using pixman for imaging])
|
||||
fi
|
||||
else
|
||||
AC_MSG_NOTICE([ Imaging support disabled])
|
||||
fi
|
||||
|
||||
if test x$enable_upekts != xno ; then
|
||||
AC_MSG_NOTICE([** upekts driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ upekts driver disabled])
|
||||
fi
|
||||
if test x$enable_upeke2 != xno ; then
|
||||
AC_MSG_NOTICE([** upeke2 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ upeke2 driver disabled (handled by upektc_img driver)])
|
||||
fi
|
||||
if test x$enable_upektc != xno ; then
|
||||
AC_MSG_NOTICE([** upektc driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ upektc driver disabled])
|
||||
fi
|
||||
if test x$enable_upeksonly != xno ; then
|
||||
AC_MSG_NOTICE([** upeksonly driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ upeksonly driver disabled])
|
||||
fi
|
||||
if test x$enable_vcom5s != xno ; then
|
||||
AC_MSG_NOTICE([** vcom5s driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ vcom5s driver disabled])
|
||||
fi
|
||||
if test x$enable_uru4000 != xno ; then
|
||||
AC_MSG_NOTICE([** uru4000 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ uru4000 driver disabled])
|
||||
fi
|
||||
if test x$enable_fdu2000 != xno ; then
|
||||
AC_MSG_NOTICE([** fdu2000 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ fdu2000 driver disabled (not ported)])
|
||||
fi
|
||||
if test x$enable_aes1610 != xno ; then
|
||||
AC_MSG_NOTICE([** aes1610 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes1610 driver disabled])
|
||||
fi
|
||||
if test x$enable_aes1660 != xno ; then
|
||||
AC_MSG_NOTICE([** aes1660 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes1660 driver disabled])
|
||||
fi
|
||||
if test x$enable_aes2501 != xno ; then
|
||||
AC_MSG_NOTICE([** aes2501 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes2501 driver disabled])
|
||||
fi
|
||||
if test x$enable_aes2550 != xno ; then
|
||||
AC_MSG_NOTICE([** aes2550/aes2810 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes2550/aes2810 driver disabled])
|
||||
fi
|
||||
if test x$enable_aes2660 != xno ; then
|
||||
AC_MSG_NOTICE([** aes2660 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes2660 driver disabled])
|
||||
fi
|
||||
if test x$enable_aes3500 != xno ; then
|
||||
AC_MSG_NOTICE([** aes3500 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes3500 driver disabled])
|
||||
fi
|
||||
if test x$enable_aes4000 != xno ; then
|
||||
AC_MSG_NOTICE([** aes4000 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes4000 driver disabled])
|
||||
fi
|
||||
if test x$enable_vfs101 != xno ; then
|
||||
AC_MSG_NOTICE([** vfs101 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ vfs101 driver disabled])
|
||||
fi
|
||||
if test x$enable_vfs301 != xno ; then
|
||||
AC_MSG_NOTICE([** vfs301 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ vfs301 driver disabled])
|
||||
fi
|
||||
if test x$enable_vfs5011 != xno ; then
|
||||
AC_MSG_NOTICE([** vfs5011 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ vfs5011 driver disabled])
|
||||
fi
|
||||
if test x$enable_upektc_img != xno ; then
|
||||
AC_MSG_NOTICE([** upektc_img driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ upektc_img driver disabled])
|
||||
fi
|
||||
if test x$enable_etes603 != xno ; then
|
||||
AC_MSG_NOTICE([** etes603 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ etes603 driver disabled])
|
||||
fi
|
||||
if test x$enable_vfs0050 != xno ; then
|
||||
AC_MSG_NOTICE([** vfs0050 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ vfs0050 driver disabled])
|
||||
fi
|
||||
if test x$require_aeslib != xno ; then
|
||||
AC_MSG_NOTICE([** aeslib helper functions enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aeslib helper functions disabled])
|
||||
fi
|
||||
if test x$require_aesX660 != xno ; then
|
||||
AC_MSG_NOTICE([** aesX660 common routines enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aesX660 common routines disabled])
|
||||
fi
|
||||
if test x$require_aes3k != xno ; then
|
||||
AC_MSG_NOTICE([** aes3k common routines enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes3k common routines disabled])
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] [examples/Makefile] [doc/Makefile])
|
||||
AC_OUTPUT
|
||||
|
328
data/autosuspend.hwdb
Normal file
|
@ -0,0 +1,328 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# This file has been generated using fprint-list-udev-hwdb with all drivers enabled
|
||||
|
||||
# Supported by libfprint driver aes1610
|
||||
usb:v08FFp1600*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes1660
|
||||
usb:v08FFp1660*
|
||||
usb:v08FFp1680*
|
||||
usb:v08FFp1681*
|
||||
usb:v08FFp1682*
|
||||
usb:v08FFp1683*
|
||||
usb:v08FFp1684*
|
||||
usb:v08FFp1685*
|
||||
usb:v08FFp1686*
|
||||
usb:v08FFp1687*
|
||||
usb:v08FFp1688*
|
||||
usb:v08FFp1689*
|
||||
usb:v08FFp168A*
|
||||
usb:v08FFp168B*
|
||||
usb:v08FFp168C*
|
||||
usb:v08FFp168D*
|
||||
usb:v08FFp168E*
|
||||
usb:v08FFp168F*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes2501
|
||||
usb:v08FFp2500*
|
||||
usb:v08FFp2580*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes2550
|
||||
usb:v08FFp2550*
|
||||
usb:v08FFp2810*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes2660
|
||||
usb:v08FFp2660*
|
||||
usb:v08FFp2680*
|
||||
usb:v08FFp2681*
|
||||
usb:v08FFp2682*
|
||||
usb:v08FFp2683*
|
||||
usb:v08FFp2684*
|
||||
usb:v08FFp2685*
|
||||
usb:v08FFp2686*
|
||||
usb:v08FFp2687*
|
||||
usb:v08FFp2688*
|
||||
usb:v08FFp2689*
|
||||
usb:v08FFp268A*
|
||||
usb:v08FFp268B*
|
||||
usb:v08FFp268C*
|
||||
usb:v08FFp268D*
|
||||
usb:v08FFp268E*
|
||||
usb:v08FFp268F*
|
||||
usb:v08FFp2691*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes3500
|
||||
usb:v08FFp5731*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver aes4000
|
||||
usb:v5501p08FF*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver egis0570
|
||||
usb:v1C7Ap0570*
|
||||
usb:v1C7Ap0571*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver elan
|
||||
usb:v04F3p0903*
|
||||
usb:v04F3p0907*
|
||||
usb:v04F3p0C01*
|
||||
usb:v04F3p0C02*
|
||||
usb:v04F3p0C03*
|
||||
usb:v04F3p0C04*
|
||||
usb:v04F3p0C05*
|
||||
usb:v04F3p0C06*
|
||||
usb:v04F3p0C07*
|
||||
usb:v04F3p0C08*
|
||||
usb:v04F3p0C09*
|
||||
usb:v04F3p0C0A*
|
||||
usb:v04F3p0C0B*
|
||||
usb:v04F3p0C0C*
|
||||
usb:v04F3p0C0D*
|
||||
usb:v04F3p0C0E*
|
||||
usb:v04F3p0C0F*
|
||||
usb:v04F3p0C10*
|
||||
usb:v04F3p0C11*
|
||||
usb:v04F3p0C12*
|
||||
usb:v04F3p0C13*
|
||||
usb:v04F3p0C14*
|
||||
usb:v04F3p0C15*
|
||||
usb:v04F3p0C16*
|
||||
usb:v04F3p0C17*
|
||||
usb:v04F3p0C18*
|
||||
usb:v04F3p0C19*
|
||||
usb:v04F3p0C1A*
|
||||
usb:v04F3p0C1B*
|
||||
usb:v04F3p0C1C*
|
||||
usb:v04F3p0C1D*
|
||||
usb:v04F3p0C1E*
|
||||
usb:v04F3p0C1F*
|
||||
usb:v04F3p0C20*
|
||||
usb:v04F3p0C21*
|
||||
usb:v04F3p0C22*
|
||||
usb:v04F3p0C23*
|
||||
usb:v04F3p0C24*
|
||||
usb:v04F3p0C25*
|
||||
usb:v04F3p0C26*
|
||||
usb:v04F3p0C27*
|
||||
usb:v04F3p0C28*
|
||||
usb:v04F3p0C29*
|
||||
usb:v04F3p0C2A*
|
||||
usb:v04F3p0C2B*
|
||||
usb:v04F3p0C2C*
|
||||
usb:v04F3p0C2D*
|
||||
usb:v04F3p0C2E*
|
||||
usb:v04F3p0C2F*
|
||||
usb:v04F3p0C30*
|
||||
usb:v04F3p0C31*
|
||||
usb:v04F3p0C32*
|
||||
usb:v04F3p0C33*
|
||||
usb:v04F3p0C3D*
|
||||
usb:v04F3p0C42*
|
||||
usb:v04F3p0C4D*
|
||||
usb:v04F3p0C4F*
|
||||
usb:v04F3p0C63*
|
||||
usb:v04F3p0C6E*
|
||||
usb:v04F3p0C58*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver elanmoc
|
||||
usb:v04F3p0C7E*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver etes603
|
||||
usb:v1C7Ap0603*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver goodixmoc
|
||||
usb:v27C6p5840*
|
||||
usb:v27C6p609C*
|
||||
usb:v27C6p60A2*
|
||||
usb:v27C6p639C*
|
||||
usb:v27C6p63AC*
|
||||
usb:v27C6p63BC*
|
||||
usb:v27C6p6496*
|
||||
usb:v27C6p6584*
|
||||
usb:v27C6p658C*
|
||||
usb:v27C6p6592*
|
||||
usb:v27C6p6594*
|
||||
usb:v27C6p659C*
|
||||
usb:v27C6p6A94*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver nb1010
|
||||
usb:v298Dp1010*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver synaptics
|
||||
usb:v06CBp00BD*
|
||||
usb:v06CBp00DF*
|
||||
usb:v06CBp00F9*
|
||||
usb:v06CBp00FC*
|
||||
usb:v06CBp00C2*
|
||||
usb:v06CBp00C9*
|
||||
usb:v06CBp0100*
|
||||
usb:v06CBp00F0*
|
||||
usb:v06CBp0103*
|
||||
usb:v06CBp0123*
|
||||
usb:v06CBp0126*
|
||||
usb:v06CBp0129*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver upeksonly
|
||||
usb:v147Ep2016*
|
||||
usb:v147Ep1000*
|
||||
usb:v147Ep1001*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver upektc
|
||||
usb:v0483p2015*
|
||||
usb:v147Ep3001*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver upektc_img
|
||||
usb:v147Ep2020*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver uru4000
|
||||
usb:v045Ep00BC*
|
||||
usb:v045Ep00BD*
|
||||
usb:v045Ep00CA*
|
||||
usb:v05BAp0007*
|
||||
usb:v05BAp0008*
|
||||
usb:v05BAp000A*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vcom5s
|
||||
usb:v061Ap0110*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs0050
|
||||
usb:v138Ap0050*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs101
|
||||
usb:v138Ap0001*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs301
|
||||
usb:v138Ap0005*
|
||||
usb:v138Ap0008*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs5011
|
||||
usb:v138Ap0010*
|
||||
usb:v138Ap0011*
|
||||
usb:v138Ap0015*
|
||||
usb:v138Ap0017*
|
||||
usb:v138Ap0018*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Supported by libfprint driver vfs7552
|
||||
usb:v138Ap0091*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
||||
|
||||
# Known unsupported devices
|
||||
usb:v04F3p036B*
|
||||
usb:v04F3p0C00*
|
||||
usb:v04F3p0C4B*
|
||||
usb:v04F3p0C4C*
|
||||
usb:v04F3p0C57*
|
||||
usb:v04F3p0C5E*
|
||||
usb:v04F3p2706*
|
||||
usb:v06CBp0081*
|
||||
usb:v06CBp0088*
|
||||
usb:v06CBp008A*
|
||||
usb:v06CBp009A*
|
||||
usb:v06CBp009B*
|
||||
usb:v06CBp00A2*
|
||||
usb:v06CBp00B7*
|
||||
usb:v06CBp00BB*
|
||||
usb:v06CBp00BE*
|
||||
usb:v06CBp00C4*
|
||||
usb:v06CBp00CB*
|
||||
usb:v06CBp00D8*
|
||||
usb:v06CBp00DA*
|
||||
usb:v06CBp00E7*
|
||||
usb:v06CBp00E9*
|
||||
usb:v0A5Cp5801*
|
||||
usb:v0A5Cp5805*
|
||||
usb:v0A5Cp5834*
|
||||
usb:v0A5Cp5840*
|
||||
usb:v0A5Cp5841*
|
||||
usb:v0A5Cp5842*
|
||||
usb:v0A5Cp5843*
|
||||
usb:v0A5Cp5844*
|
||||
usb:v0A5Cp5845*
|
||||
usb:v10A5p0007*
|
||||
usb:v1188p9545*
|
||||
usb:v138Ap0007*
|
||||
usb:v138Ap003A*
|
||||
usb:v138Ap003C*
|
||||
usb:v138Ap003D*
|
||||
usb:v138Ap003F*
|
||||
usb:v138Ap0090*
|
||||
usb:v138Ap0092*
|
||||
usb:v138Ap0094*
|
||||
usb:v138Ap0097*
|
||||
usb:v138Ap009D*
|
||||
usb:v138Ap00AB*
|
||||
usb:v147Ep1002*
|
||||
usb:v1491p0088*
|
||||
usb:v16D1p1027*
|
||||
usb:v1C7Ap0300*
|
||||
usb:v1C7Ap0575*
|
||||
usb:v27C6p5042*
|
||||
usb:v27C6p5110*
|
||||
usb:v27C6p5117*
|
||||
usb:v27C6p5201*
|
||||
usb:v27C6p521D*
|
||||
usb:v27C6p5301*
|
||||
usb:v27C6p530C*
|
||||
usb:v27C6p532D*
|
||||
usb:v27C6p533C*
|
||||
usb:v27C6p5381*
|
||||
usb:v27C6p5385*
|
||||
usb:v27C6p538C*
|
||||
usb:v27C6p538D*
|
||||
usb:v27C6p5395*
|
||||
usb:v27C6p5584*
|
||||
usb:v27C6p55A2*
|
||||
usb:v27C6p55A4*
|
||||
usb:v27C6p55B4*
|
||||
usb:v27C6p5740*
|
||||
usb:v2808p9338*
|
||||
usb:v298Dp2033*
|
||||
usb:v3538p0930*
|
||||
ID_AUTOSUSPEND=1
|
||||
ID_PERSIST=0
|
11
data/meson.build
Normal file
|
@ -0,0 +1,11 @@
|
|||
if udev_hwdb_dir != ''
|
||||
# This file has to be updated using
|
||||
# ninja -C <builddir> libfprint/sync-udev-hwdb
|
||||
# Note that the unsupported device list needs to be manually synced from
|
||||
# the wiki. See comment in libfprint/fprint-list-uev-hwdb.c
|
||||
|
||||
install_data('autosuspend.hwdb',
|
||||
rename: '60-autosuspend-@0@.hwdb'.format(versioned_libname),
|
||||
install_dir: udev_hwdb_dir,
|
||||
)
|
||||
endif
|
567
demo/gtk-libfprint-test.c
Normal file
|
@ -0,0 +1,567 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
struct _LibfprintDemo
|
||||
{
|
||||
GtkApplication parent;
|
||||
};
|
||||
G_DECLARE_FINAL_TYPE (LibfprintDemo, libfprint_demo, FP, DEMO, GtkApplication)
|
||||
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;
|
||||
|
||||
struct _LibfprintDemoWindow
|
||||
{
|
||||
GtkApplicationWindow parent_instance;
|
||||
|
||||
GtkWidget *header_bar;
|
||||
GtkWidget *mode_stack;
|
||||
GtkWidget *capture_button;
|
||||
GtkWidget *cancel_button;
|
||||
GtkWidget *capture_image;
|
||||
GtkWidget *spinner;
|
||||
GtkWidget *instructions;
|
||||
|
||||
GCancellable *cancellable;
|
||||
|
||||
gboolean opened;
|
||||
FpDevice *dev;
|
||||
|
||||
FpImage *img;
|
||||
ImageDisplayFlags img_flags;
|
||||
};
|
||||
|
||||
G_DECLARE_FINAL_TYPE (LibfprintDemoWindow, libfprint_demo_window, FP, DEMO_WINDOW, GtkApplicationWindow)
|
||||
G_DEFINE_TYPE (LibfprintDemoWindow, libfprint_demo_window, GTK_TYPE_APPLICATION_WINDOW)
|
||||
|
||||
typedef enum {
|
||||
EMPTY_MODE,
|
||||
NOIMAGING_MODE,
|
||||
CAPTURE_MODE,
|
||||
SPINNER_MODE,
|
||||
ERROR_MODE,
|
||||
RETRY_MODE
|
||||
} LibfprintDemoMode;
|
||||
|
||||
static void libfprint_demo_set_mode (LibfprintDemoWindow *win,
|
||||
LibfprintDemoMode mode);
|
||||
|
||||
static unsigned char *
|
||||
img_to_rgbdata (const guint8 *imgdata,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
int size = width * height;
|
||||
guint8 *rgbdata = g_malloc (size * 3);
|
||||
size_t i;
|
||||
size_t rgb_offset = 0;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
guint8 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,
|
||||
GPtrArray *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 < minutiae->len; i++)
|
||||
{
|
||||
struct fp_minutia *min = g_ptr_array_index (minutiae, 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 (FpImage *img,
|
||||
ImageDisplayFlags flags)
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
const guint8 *data;
|
||||
unsigned char *rgbdata;
|
||||
|
||||
width = fp_image_get_width (img);
|
||||
height = fp_image_get_height (img);
|
||||
|
||||
if (flags & IMAGE_DISPLAY_BINARY)
|
||||
data = fp_image_get_binarized (img, NULL);
|
||||
else
|
||||
data = fp_image_get_data (img, NULL);
|
||||
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
rgbdata = img_to_rgbdata (data, width, height);
|
||||
|
||||
if (flags & IMAGE_DISPLAY_MINUTIAE)
|
||||
{
|
||||
GPtrArray *minutiae;
|
||||
|
||||
minutiae = fp_image_get_minutiae (img);
|
||||
plot_minutiae (rgbdata, width, height, minutiae);
|
||||
}
|
||||
|
||||
return gdk_pixbuf_new_from_data (rgbdata, GDK_COLORSPACE_RGB,
|
||||
FALSE, 8, width, height,
|
||||
width * 3, (GdkPixbufDestroyNotify) g_free,
|
||||
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)
|
||||
{
|
||||
FpScanType scan_type;
|
||||
const char *message;
|
||||
|
||||
scan_type = fp_device_get_scan_type (win->dev);
|
||||
|
||||
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 (FpDevice *dev,
|
||||
GAsyncResult *res,
|
||||
void *user_data)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
LibfprintDemoWindow *win = user_data;
|
||||
FpImage *image = NULL;
|
||||
|
||||
g_clear_object (&win->cancellable);
|
||||
|
||||
image = fp_device_capture_finish (dev, res, &error);
|
||||
if (!image)
|
||||
{
|
||||
g_warning ("Error capturing data: %s", error->message);
|
||||
if (error->domain == FP_DEVICE_RETRY ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
libfprint_demo_set_mode (win, RETRY_MODE);
|
||||
else if (g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_SUPPORTED))
|
||||
libfprint_demo_set_mode (win, NOIMAGING_MODE);
|
||||
else
|
||||
libfprint_demo_set_mode (win, ERROR_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
g_clear_object (&win->img);
|
||||
win->img = image;
|
||||
update_image (win);
|
||||
|
||||
libfprint_demo_set_mode (win, CAPTURE_MODE);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_start_capture (LibfprintDemoWindow *win)
|
||||
{
|
||||
libfprint_demo_set_capture_label (win);
|
||||
|
||||
fp_device_capture (win->dev, TRUE, win->cancellable, (GAsyncReadyCallback) dev_capture_start_cb, win);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_open_cb (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
LibfprintDemoWindow *win = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!fp_device_open_finish (dev, res, &error))
|
||||
{
|
||||
g_warning ("Failed to open device: %s", error->message);
|
||||
libfprint_demo_set_mode (win, ERROR_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_start_capture (win);
|
||||
}
|
||||
|
||||
static void
|
||||
activate_capture (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data)
|
||||
{
|
||||
LibfprintDemoWindow *win = user_data;
|
||||
|
||||
libfprint_demo_set_mode (win, SPINNER_MODE);
|
||||
g_clear_pointer (&win->img, g_object_unref);
|
||||
|
||||
g_clear_object (&win->cancellable);
|
||||
win->cancellable = g_cancellable_new ();
|
||||
|
||||
if (win->opened)
|
||||
{
|
||||
dev_start_capture (win);
|
||||
return;
|
||||
}
|
||||
|
||||
libfprint_demo_set_spinner_label (win, "Opening fingerprint reader");
|
||||
|
||||
win->opened = TRUE;
|
||||
fp_device_open (win->dev, win->cancellable, (GAsyncReadyCallback) dev_open_cb, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
cancel_capture (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data)
|
||||
{
|
||||
LibfprintDemoWindow *win = user_data;
|
||||
|
||||
g_debug ("cancelling %p", win->cancellable);
|
||||
|
||||
if (win->cancellable)
|
||||
g_cancellable_cancel (win->cancellable);
|
||||
}
|
||||
|
||||
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 },
|
||||
{ "cancel", cancel_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)
|
||||
{
|
||||
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_widget_set_sensitive (win->cancel_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_widget_set_sensitive (win->cancel_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);
|
||||
gtk_widget_set_sensitive (win->cancel_button, FALSE);
|
||||
|
||||
title = g_strdup_printf ("%s Test", fp_device_get_name (win->dev));
|
||||
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_widget_set_sensitive (win->cancel_button, TRUE);
|
||||
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_widget_set_sensitive (win->cancel_button, FALSE);
|
||||
gtk_spinner_stop (GTK_SPINNER (win->spinner));
|
||||
break;
|
||||
|
||||
case RETRY_MODE:
|
||||
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "retry-mode");
|
||||
gtk_widget_set_sensitive (win->capture_button, TRUE);
|
||||
gtk_widget_set_sensitive (win->cancel_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)
|
||||
{
|
||||
FpContext *ctx;
|
||||
GPtrArray *devices;
|
||||
|
||||
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);
|
||||
|
||||
ctx = fp_context_new ();
|
||||
|
||||
devices = fp_context_get_devices (ctx);
|
||||
if (!devices)
|
||||
{
|
||||
libfprint_demo_set_mode (window, ERROR_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Empty list? */
|
||||
if (devices->len == 0)
|
||||
{
|
||||
libfprint_demo_set_mode (window, EMPTY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fp_device_has_feature (g_ptr_array_index (devices, 0), FP_DEVICE_FEATURE_CAPTURE))
|
||||
{
|
||||
libfprint_demo_set_mode (window, NOIMAGING_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
window->dev = g_object_ref (g_ptr_array_index (devices, 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, cancel_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);
|
||||
}
|
6
demo/gtk-libfprint-test.gresource.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/libfprint_demo">
|
||||
<file>gtk-libfprint-test.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
427
demo/gtk-libfprint-test.ui
Normal file
|
@ -0,0 +1,427 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.0 -->
|
||||
<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>
|
||||
<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"><b><span size="large">Please press finger on reader</span></b></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>
|
||||
<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="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="retry-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"><b><span size="large">Device reported a recoverable error. Please retry!</span></b></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>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">retry-mode</property>
|
||||
<property name="title">retry-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"><b><span size="large">Fingerprint reader does not support capturing images</span></b></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>
|
||||
<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"><b><span size="large">An error occurred trying to access the fingerprint reader</span></b></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">4</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"><b><span size="large">No fingerprint readers found</span></b></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>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<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="cancel_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.cancel</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="cancel_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="text-button"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</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>
|
||||
<property name="position">2</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>
|
30
demo/meson.build
Normal file
|
@ -0,0 +1,30 @@
|
|||
gtk_test_resources = gnome.compile_resources('gtk-test-resources',
|
||||
'gtk-libfprint-test.gresource.xml',
|
||||
source_dir : '.',
|
||||
c_name : 'gtk_test')
|
||||
|
||||
prefix = get_option('prefix')
|
||||
bindir = join_paths(prefix, get_option('bindir'))
|
||||
datadir = join_paths(prefix, get_option('datadir'))
|
||||
|
||||
executable('gtk-libfprint-test',
|
||||
[ 'gtk-libfprint-test.c', gtk_test_resources ],
|
||||
dependencies: [
|
||||
gtk_dep,
|
||||
libfprint_dep,
|
||||
],
|
||||
c_args: '-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'))
|
28
demo/org.freedesktop.libfprint.Demo.appdata.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>org.freedesktop.libfprint.Demo.desktop</id>
|
||||
<name>Fingerprint Reader Demo</name>
|
||||
<summary>Test fingerprint readers</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>LGPL-2.1+</project_license>
|
||||
<description>
|
||||
<p>
|
||||
Fingerprint Reader Demo is a test application for the libfprint
|
||||
fingerprint reader library. Its purpose is to test drivers, new and old,
|
||||
in a sandbox, to make sure that the drivers and associated functions
|
||||
work correctly.
|
||||
</p>
|
||||
<p>
|
||||
Fingerprint Reader Demo does not modify the system, or replace integration
|
||||
in desktop environments.
|
||||
</p>
|
||||
</description>
|
||||
<url type="homepage">https://fprint.freedesktop.org</url>
|
||||
<screenshots>
|
||||
<screenshot type="default" width="1024" height="576">https://git.gnome.org/browse/totem/plain/data/appdata/ss-main.png</screenshot>
|
||||
<screenshot width="1024" height="576">https://git.gnome.org/browse/totem/plain/data/appdata/ss-music-playlist.png</screenshot>
|
||||
</screenshots>
|
||||
<updatecontact>hadess@hadess.net</updatecontact>
|
||||
<!-- Incorrect, but appstream-util won't validate without it -->
|
||||
<translation type="gettext">libfprint</translation>
|
||||
</component>
|
10
demo/org.freedesktop.libfprint.Demo.desktop
Normal file
|
@ -0,0 +1,10 @@
|
|||
[Desktop Entry]
|
||||
Name=Fingerprint Reader Demo
|
||||
Comment=Test fingerprint readers
|
||||
Keywords=finger;print;fingerprint;fprint;demo;driver;reader;
|
||||
Exec=gtk-libfprint-test
|
||||
Icon=org.freedesktop.libfprint.Demo
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=GTK;GNOME;Development;System;
|
||||
StartupNotify=true
|
75
demo/org.freedesktop.libfprint.Demo.json
Normal file
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"app-id": "org.freedesktop.libfprint.Demo",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "3.36",
|
||||
"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/libgusb/COPYING"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "libgusb",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [ "-Dtests=false", "-Dvapi=false", "-Ddocs=false", "-Dintrospection=false" ],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/hughsie/libgusb/archive/0.3.0.tar.gz",
|
||||
"sha256": "b36310f8405d5fd68f6caf4a829f7ab4c627b38fd3d02a139d411fce0f3a49f1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "gudev",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [ "-Dtests=disabled", "-Dintrospection=disabled" ],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://download.gnome.org/sources/libgudev/236/libgudev-236.tar.xz",
|
||||
"sha256": "e50369d06d594bae615eb7aeb787de304ebaad07a26d1043cef8e9c7ab7c9524"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "libfprint",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [ "-Dudev_hwdb=disabled", "-Dudev_rules=disabled", "-Dx11-examples=false", "-Dgtk-examples=true", "-Ddrivers=all" ],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.freedesktop.org/libfprint/libfprint.git",
|
||||
"branch": "wip/benzea/v2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
BIN
demo/org.freedesktop.libfprint.Demo.png
Normal file
After Width: | Height: | Size: 24 KiB |
|
@ -1,10 +0,0 @@
|
|||
EXTRA_DIST = doxygen.cfg
|
||||
|
||||
docs: doxygen.cfg
|
||||
doxygen $^
|
||||
|
||||
docs-upload: docs
|
||||
ln -s html api
|
||||
ncftpput -f ~/.ncftp/reactivated -m -R httpdocs/fprint api/
|
||||
rm -f api
|
||||
|
113
doc/advanced-topics.xml
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?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">driver</ulink> and <ulink url="#device-id">device ID</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">
|
||||
<title>Driver</title>
|
||||
|
||||
<para>
|
||||
Each driver is assigned a unique string identifier by the project maintainer.
|
||||
</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-id">
|
||||
<title>Device ID</title>
|
||||
|
||||
<para>
|
||||
Internally, the behind a device assigns a string 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 device ID may even
|
||||
be the same string in all cases. It is guaranteed to have a non-zero length
|
||||
and be a valid file name. It defaults to "0".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The only reason you may be interested in retrieving the device ID for a
|
||||
device is for the purpose of checking if some print data is compatible
|
||||
with a device. libfprint uses the device ID as one way of checking that the
|
||||
print you are verifying is compatible with the device in question - the
|
||||
device ID 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>
|
1294
doc/doxygen.cfg
30
doc/getting-started.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
|
||||
]>
|
||||
<chapter id="getting-started" xmlns:xi="http://www.w3.org/2003/XInclude">
|
||||
<title>Getting Started</title>
|
||||
|
||||
<para>
|
||||
libfprint includes several simple functional examples under the <computeroutput>examples/</computeroutput>
|
||||
directory in the libfprint source distribution. Those are good starting
|
||||
points.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Usually the first thing you want to do is determine which fingerprint
|
||||
devices are present. This is done using the <ulink url="fp-context.html">FpContext</ulink> object.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once you have found a device you would like to operate, you should open it.
|
||||
Refer to <ulink url="fp-context.html">device operations</ulink>. This section also details enrollment,
|
||||
image capture, and verification.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
That should be enough to get you started, but do remember there are
|
||||
documentation pages on other aspects of libfprint's API (see the modules
|
||||
page).
|
||||
</para>
|
||||
</chapter>
|
106
doc/intro.xml
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
|
||||
]>
|
||||
<chapter id="intro" xmlns:xi="http://www.w3.org/2003/XInclude">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>
|
||||
libfprint is an open source library to provide access to fingerprint
|
||||
scanning devices. For more info, see the
|
||||
<ulink url="https://fprint.freedesktop.org/">libfprint project homepage</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This documentation is aimed at application developers who wish to integrate
|
||||
fingerprint-related functionality into their software. libfprint has been
|
||||
designed so that you only have to do this once – by integrating your
|
||||
software with libfprint, you'll be supporting all the fingerprint readers
|
||||
that we have got our hands on. As such, the API is rather general (and
|
||||
therefore hopefully easy to comprehend!), and does its best to hide the
|
||||
technical details that required to operate the hardware.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This documentation is not aimed at developers wishing to develop and
|
||||
contribute fingerprint device drivers to libfprint.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Feedback on this API and its associated documentation is appreciated. Was
|
||||
anything unclear? Does anything seem unreasonably complicated? Is anything
|
||||
missing? Let us know on the
|
||||
<ulink url="https://lists.freedesktop.org/mailman/listinfo/fprint">mailing list</ulink>.
|
||||
</para>
|
||||
|
||||
<refsect2 id="enrollment">
|
||||
<title>Enrollment</title>
|
||||
|
||||
<para>
|
||||
Before you dive into the API, it's worth introducing a couple of concepts.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The process of enrolling a finger is where you effectively scan your
|
||||
finger for the purposes of teaching the system what your finger looks like.
|
||||
This means that you scan your fingerprint, then the system processes it and
|
||||
stores some data about your fingerprint to refer to later.
|
||||
</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2 id="verification">
|
||||
<title>Verification</title>
|
||||
|
||||
<para>
|
||||
Verification is what most people think of when they think about fingerprint
|
||||
scanning. The process of verification is effectively performing a fresh
|
||||
fingerprint scan, and then comparing that scan to a finger that was
|
||||
previously enrolled.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As an example scenario, verification can be used to implement what people
|
||||
would picture as fingerprint login (i.e. fingerprint replaces password).
|
||||
For example:
|
||||
</para>
|
||||
<itemizedlist mark='dot'>
|
||||
<listitem>
|
||||
I enroll my fingerprint through some software that trusts I am who I say
|
||||
I am. This is a prerequisite before I can perform fingerprint-based
|
||||
login for my account.
|
||||
</listitem>
|
||||
<listitem>
|
||||
Some time later, I want to login to my computer. I enter my username,
|
||||
but instead of prompting me for a password, it asks me to scan my finger.
|
||||
I scan my finger.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The system compares the finger I just scanned to the one that was
|
||||
enrolled earlier. If the system decides that the fingerprints match,
|
||||
I am successfully logged in. Otherwise, the system informs me that I am
|
||||
not authorised to login as that user.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</refsect2>
|
||||
|
||||
<refsect2 id="identification">
|
||||
<title>Identification</title>
|
||||
|
||||
<para>
|
||||
Identification is the process of comparing a freshly scanned fingerprint
|
||||
to a <emphasis>collection</emphasis> of previously enrolled fingerprints. For example,
|
||||
imagine there are 100 people in an organisation, and they all have enrolled
|
||||
their fingerprints. One user walks up to a fingerprint scanner and scans
|
||||
their finger. With <emphasis>no other knowledge</emphasis> of who that user might be,
|
||||
the system examines their fingerprint, looks in the database, and determines
|
||||
that the user is user number #61.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In other words, verification might be seen as a one-to-one fingerprint
|
||||
comparison where you know the identity of the user that you wish to
|
||||
authenticate, whereas identification is a one-to-many comparison where you
|
||||
do not know the identity of the user that you wish to authenticate.
|
||||
</para>
|
||||
</refsect2>
|
||||
</chapter>
|
288
doc/libfprint-2-sections.txt
Normal file
|
@ -0,0 +1,288 @@
|
|||
<SECTION>
|
||||
<FILE>drivers_api</FILE>
|
||||
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fp-context</FILE>
|
||||
<TITLE>FpContext</TITLE>
|
||||
FP_TYPE_CONTEXT
|
||||
FpContextClass
|
||||
fp_context_new
|
||||
fp_context_enumerate
|
||||
fp_context_get_devices
|
||||
FpContext
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fp-device</FILE>
|
||||
FP_TYPE_DEVICE
|
||||
FP_DEVICE_RETRY
|
||||
FP_DEVICE_ERROR
|
||||
FpDeviceType
|
||||
FpDeviceFeature
|
||||
FpScanType
|
||||
FpDeviceRetry
|
||||
FpDeviceError
|
||||
FpFingerStatusFlags
|
||||
fp_device_retry_quark
|
||||
fp_device_error_quark
|
||||
FpEnrollProgress
|
||||
FpMatchCb
|
||||
fp_device_get_driver
|
||||
fp_device_get_device_id
|
||||
fp_device_get_name
|
||||
fp_device_get_scan_type
|
||||
fp_device_get_nr_enroll_stages
|
||||
fp_device_get_finger_status
|
||||
fp_device_get_features
|
||||
fp_device_has_feature
|
||||
fp_device_has_storage
|
||||
fp_device_supports_identify
|
||||
fp_device_supports_capture
|
||||
fp_device_is_open
|
||||
fp_device_open
|
||||
fp_device_close
|
||||
fp_device_enroll
|
||||
fp_device_verify
|
||||
fp_device_identify
|
||||
fp_device_capture
|
||||
fp_device_delete_print
|
||||
fp_device_list_prints
|
||||
fp_device_clear_storage
|
||||
fp_device_suspend
|
||||
fp_device_resume
|
||||
fp_device_open_finish
|
||||
fp_device_close_finish
|
||||
fp_device_enroll_finish
|
||||
fp_device_verify_finish
|
||||
fp_device_identify_finish
|
||||
fp_device_capture_finish
|
||||
fp_device_delete_print_finish
|
||||
fp_device_list_prints_finish
|
||||
fp_device_clear_storage_finish
|
||||
fp_device_suspend_finish
|
||||
fp_device_resume_finish
|
||||
fp_device_open_sync
|
||||
fp_device_close_sync
|
||||
fp_device_enroll_sync
|
||||
fp_device_verify_sync
|
||||
fp_device_identify_sync
|
||||
fp_device_capture_sync
|
||||
fp_device_delete_print_sync
|
||||
fp_device_list_prints_sync
|
||||
fp_device_clear_storage_sync
|
||||
fp_device_suspend_sync
|
||||
fp_device_resume_sync
|
||||
FpDevice
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fp-image</FILE>
|
||||
FP_TYPE_IMAGE
|
||||
FpMinutia
|
||||
fp_image_new
|
||||
fp_image_get_width
|
||||
fp_image_get_height
|
||||
fp_image_get_ppmm
|
||||
fp_image_get_minutiae
|
||||
fp_image_detect_minutiae
|
||||
fp_image_detect_minutiae_finish
|
||||
fp_image_get_data
|
||||
fp_image_get_binarized
|
||||
fp_minutia_get_coords
|
||||
FpImage
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fp-image-device</FILE>
|
||||
FP_TYPE_IMAGE_DEVICE
|
||||
FpImageDevice
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fp-print</FILE>
|
||||
FP_TYPE_PRINT
|
||||
FpFinger
|
||||
FpPrint
|
||||
fp_print_new
|
||||
fp_print_get_driver
|
||||
fp_print_get_device_id
|
||||
fp_print_get_device_stored
|
||||
fp_print_get_image
|
||||
fp_print_get_finger
|
||||
fp_print_get_username
|
||||
fp_print_get_description
|
||||
fp_print_get_enroll_date
|
||||
fp_print_set_finger
|
||||
fp_print_set_username
|
||||
fp_print_set_description
|
||||
fp_print_set_enroll_date
|
||||
fp_print_compatible
|
||||
fp_print_equal
|
||||
fp_print_serialize
|
||||
fp_print_deserialize
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-assembling</FILE>
|
||||
fpi_frame
|
||||
fpi_frame_asmbl_ctx
|
||||
fpi_do_movement_estimation
|
||||
fpi_assemble_frames
|
||||
fpi_line_asmbl_ctx
|
||||
fpi_assemble_lines
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-context</FILE>
|
||||
fpi_get_driver_types
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-device</FILE>
|
||||
FpDeviceClass
|
||||
FpTimeoutFunc
|
||||
FpiDeviceAction
|
||||
FpIdEntry
|
||||
FpiDeviceUdevSubtypeFlags
|
||||
fpi_device_get_usb_device
|
||||
fpi_device_get_udev_data
|
||||
fpi_device_get_virtual_env
|
||||
fpi_device_get_current_action
|
||||
fpi_device_retry_new
|
||||
fpi_device_error_new
|
||||
fpi_device_retry_new_msg
|
||||
fpi_device_error_new_msg
|
||||
fpi_device_get_driver_data
|
||||
fpi_device_get_enroll_data
|
||||
fpi_device_get_capture_data
|
||||
fpi_device_get_verify_data
|
||||
fpi_device_get_identify_data
|
||||
fpi_device_get_delete_data
|
||||
fpi_device_get_cancellable
|
||||
fpi_device_action_is_cancelled
|
||||
fpi_device_add_timeout
|
||||
fpi_device_set_nr_enroll_stages
|
||||
fpi_device_set_scan_type
|
||||
fpi_device_update_features
|
||||
fpi_device_critical_enter
|
||||
fpi_device_critical_leave
|
||||
fpi_device_remove
|
||||
fpi_device_report_finger_status
|
||||
fpi_device_report_finger_status_changes
|
||||
fpi_device_action_error
|
||||
fpi_device_probe_complete
|
||||
fpi_device_open_complete
|
||||
fpi_device_close_complete
|
||||
fpi_device_enroll_complete
|
||||
fpi_device_verify_complete
|
||||
fpi_device_identify_complete
|
||||
fpi_device_capture_complete
|
||||
fpi_device_delete_complete
|
||||
fpi_device_list_complete
|
||||
fpi_device_suspend_complete
|
||||
fpi_device_resume_complete
|
||||
fpi_device_enroll_progress
|
||||
fpi_device_verify_report
|
||||
fpi_device_identify_report
|
||||
fpi_device_class_auto_initialize_features
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-image</FILE>
|
||||
FpiImageFlags
|
||||
FpImage
|
||||
fpi_std_sq_dev
|
||||
fpi_mean_sq_diff_norm
|
||||
fpi_image_resize
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-image-device</FILE>
|
||||
<TITLE>Internal FpImageDevice</TITLE>
|
||||
FpiImageDeviceState
|
||||
FpImageDeviceClass
|
||||
fpi_image_device_session_error
|
||||
fpi_image_device_open_complete
|
||||
fpi_image_device_close_complete
|
||||
fpi_image_device_activate_complete
|
||||
fpi_image_device_deactivate_complete
|
||||
fpi_image_device_report_finger_status
|
||||
fpi_image_device_image_captured
|
||||
fpi_image_device_retry_scan
|
||||
fpi_image_device_set_bz3_threshold
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-log</FILE>
|
||||
fp_dbg
|
||||
fp_info
|
||||
fp_warn
|
||||
fp_err
|
||||
BUG_ON
|
||||
BUG
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-print</FILE>
|
||||
FpiPrintType
|
||||
FpiMatchResult
|
||||
fpi_print_add_print
|
||||
fpi_print_set_type
|
||||
fpi_print_set_device_stored
|
||||
fpi_print_add_from_image
|
||||
fpi_print_bz3_match
|
||||
fpi_print_generate_user_id
|
||||
fpi_print_fill_from_user_id
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-ssm</FILE>
|
||||
FpiSsmCompletedCallback
|
||||
FpiSsmHandlerCallback
|
||||
fpi_ssm_new
|
||||
fpi_ssm_new_full
|
||||
fpi_ssm_free
|
||||
fpi_ssm_start
|
||||
fpi_ssm_start_subsm
|
||||
fpi_ssm_next_state
|
||||
fpi_ssm_next_state_delayed
|
||||
fpi_ssm_jump_to_state
|
||||
fpi_ssm_jump_to_state_delayed
|
||||
fpi_ssm_cancel_delayed_state_change
|
||||
fpi_ssm_mark_completed
|
||||
fpi_ssm_mark_failed
|
||||
fpi_ssm_set_data
|
||||
fpi_ssm_get_data
|
||||
fpi_ssm_get_device
|
||||
fpi_ssm_get_error
|
||||
fpi_ssm_dup_error
|
||||
fpi_ssm_get_cur_state
|
||||
fpi_ssm_usb_transfer_cb
|
||||
FpiSsm
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-usb-transfer</FILE>
|
||||
FPI_USB_ENDPOINT_IN
|
||||
FPI_USB_ENDPOINT_OUT
|
||||
FpiUsbTransferCallback
|
||||
FpiTransferType
|
||||
FpiUsbTransfer
|
||||
fpi_usb_transfer_new
|
||||
fpi_usb_transfer_ref
|
||||
fpi_usb_transfer_unref
|
||||
fpi_usb_transfer_set_short_error
|
||||
fpi_usb_transfer_fill_bulk
|
||||
fpi_usb_transfer_fill_bulk_full
|
||||
fpi_usb_transfer_fill_control
|
||||
fpi_usb_transfer_fill_interrupt
|
||||
fpi_usb_transfer_fill_interrupt_full
|
||||
fpi_usb_transfer_submit
|
||||
fpi_usb_transfer_submit_sync
|
||||
<SUBSECTION Standard>
|
||||
FPI_TYPE_USB_TRANSFER
|
||||
fpi_usb_transfer_get_type
|
||||
</SECTION>
|
||||
|
8
doc/libfprint-2.types
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <fprint.h>
|
||||
#include <fp-image-device.h>
|
||||
|
||||
fp_context_get_type
|
||||
fp_device_get_type
|
||||
fp_image_device_get_type
|
||||
fp_image_get_type
|
||||
fp_print_get_type
|
75
doc/libfprint-docs.xml
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?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/fp-context.xml"/>
|
||||
<xi:include href="xml/fp-device.xml"/>
|
||||
<xi:include href="xml/fp-image-device.xml"/>
|
||||
<xi:include href="xml/fp-print.xml"/>
|
||||
<xi:include href="xml/fp-image.xml"/>
|
||||
</part>
|
||||
|
||||
<part>
|
||||
<title>Writing Drivers</title>
|
||||
<chapter id="driver-dev">
|
||||
<title>Device methods for drivers</title>
|
||||
<xi:include href="xml/fpi-device.xml"/>
|
||||
<xi:include href="xml/fpi-image-device.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="driver-helpers">
|
||||
<title>USB and State Machine helpers</title>
|
||||
<xi:include href="xml/fpi-usb-transfer.xml"/>
|
||||
<xi:include href="xml/fpi-ssm.xml"/>
|
||||
<xi:include href="xml/fpi-log.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="driver-img">
|
||||
<title>Image manipulation</title>
|
||||
<xi:include href="xml/fpi-image.xml"/>
|
||||
<xi:include href="xml/fpi-assembling.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="driver-print">
|
||||
<title>Print handling</title>
|
||||
<xi:include href="xml/fpi-print.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="driver-misc">
|
||||
<title>Listing drivers</title>
|
||||
<xi:include href="xml/fpi-context.xml"/>
|
||||
</chapter>
|
||||
</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>
|
116
doc/libfprint-sections.txt-new-manual
Normal file
|
@ -0,0 +1,116 @@
|
|||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>context</FILE>
|
||||
<TITLE>Device discovery and hotplugging</TITLE>
|
||||
FP_TYPE_CONTEXT
|
||||
FpContext
|
||||
fp_context_new
|
||||
fp_context_enumerate
|
||||
fp_context_get_devices
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>device</FILE>
|
||||
<TITLE>Device</TITLE>
|
||||
FP_TYPE_DEVICE
|
||||
FpDevice
|
||||
FpDeviceType
|
||||
FpScanType
|
||||
FpDeviceRetry
|
||||
FpDeviceError
|
||||
FP_DEVICE_ERROR
|
||||
FP_DEVICE_RETRY
|
||||
fp_device_get_driver
|
||||
fp_device_get_device_id
|
||||
fp_device_get_name
|
||||
fp_device_get_scan_type
|
||||
|
||||
fp_device_open
|
||||
fp_device_open_finish
|
||||
fp_device_open_sync
|
||||
|
||||
fp_device_close
|
||||
fp_device_close_finish
|
||||
fp_device_close_sync
|
||||
|
||||
fp_device_enroll
|
||||
fp_device_enroll_finish
|
||||
fp_device_enroll_sync
|
||||
|
||||
fp_device_identify
|
||||
fp_device_identify_finish
|
||||
fp_device_identify_sync
|
||||
|
||||
fp_device_capture
|
||||
fp_device_capture_finish
|
||||
fp_device_capture_sync
|
||||
|
||||
fp_device_verify
|
||||
fp_device_verify_finish
|
||||
fp_device_verify_sync
|
||||
|
||||
_fp_device_get_cancellable
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>print</FILE>
|
||||
<TITLE>Fingerprint handling</TITLE>
|
||||
FpPrint
|
||||
fp_print_new
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>image</FILE>
|
||||
<TITLE>Image handling</TITLE>
|
||||
FP_TYPE_IMAGE
|
||||
FpImage
|
||||
fp_image_new
|
||||
fp_image_detect_minutiae
|
||||
fp_image_detect_minutiae_finish
|
||||
fp_image_device_new
|
||||
fp_image_get_binarized
|
||||
fp_image_get_data
|
||||
fp_image_get_height
|
||||
fp_image_get_minutiae
|
||||
fp_image_get_ppmm
|
||||
fp_image_get_width
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<FILE>internal-image-device</FILE>
|
||||
<INCLUDE>drivers_api.h</INCLUDE>
|
||||
<TITLE>Base class for image devices</TITLE>
|
||||
FpImageDevice
|
||||
FpImageDeviceClass
|
||||
FpiImageDeviceState
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>internal-usb-transfers</FILE>
|
||||
<INCLUDE>drivers_api.h</INCLUDE>
|
||||
<TITLE>USB Transfers</TITLE>
|
||||
FpUsbTransfer
|
||||
fp_usb_transfer_fill_bulk
|
||||
fp_usb_transfer_fill_bulk_full
|
||||
fp_usb_transfer_fill_control
|
||||
fp_usb_transfer_fill_interrupt
|
||||
fp_usb_transfer_fill_interrupt_full
|
||||
fp_usb_transfer_get_type
|
||||
fp_usb_transfer_new
|
||||
fp_usb_transfer_ref
|
||||
fp_usb_transfer_set_short_error
|
||||
fp_usb_transfer_submit
|
||||
fp_usb_transfer_submit_sync
|
||||
fp_usb_transfer_unref
|
||||
FpUsbTransferCallback
|
||||
FP_USB_ENDPOINT_IN
|
||||
FP_USB_ENDPOINT_OUT
|
||||
</SECTION>
|
41
doc/meson.build
Normal file
|
@ -0,0 +1,41 @@
|
|||
subdir('xml')
|
||||
|
||||
private_headers = [
|
||||
'config.h',
|
||||
'nbis-helpers.h',
|
||||
'fprint.h',
|
||||
|
||||
# Subdirectories to ignore
|
||||
'drivers',
|
||||
'nbis',
|
||||
]
|
||||
|
||||
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(versioned_libname,
|
||||
main_xml: 'libfprint-docs.xml',
|
||||
src_dir: join_paths(meson.source_root(), 'libfprint'),
|
||||
include_directories: include_directories('../libfprint'),
|
||||
dependencies: libfprint_dep,
|
||||
content_files: content_files,
|
||||
expand_content_files: expand_content_files,
|
||||
ignore_headers: private_headers,
|
||||
gobject_typesfile: 'libfprint-2.types',
|
||||
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)
|
8
doc/xml/gtkdocentities.ent.in
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!ENTITY package "@PACKAGE@">
|
||||
<!ENTITY package_bugreport "@PACKAGE_BUGREPORT@">
|
||||
<!ENTITY package_name "@PACKAGE_NAME@">
|
||||
<!ENTITY package_string "@PACKAGE_STRING@">
|
||||
<!ENTITY package_tarname "@PACKAGE_TARNAME@">
|
||||
<!ENTITY package_url "@PACKAGE_URL@">
|
||||
<!ENTITY package_version "@PACKAGE_VERSION@">
|
||||
<!ENTITY package_api_version "@PACKAGE_API_VERSION@">
|
12
doc/xml/meson.build
Normal file
|
@ -0,0 +1,12 @@
|
|||
ent_conf = configuration_data()
|
||||
ent_conf.set('PACKAGE', versioned_libname)
|
||||
ent_conf.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/libfprint/libfprint/issues')
|
||||
ent_conf.set('PACKAGE_NAME', versioned_libname)
|
||||
ent_conf.set('PACKAGE_STRING', versioned_libname)
|
||||
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)
|
|
@ -1,26 +0,0 @@
|
|||
AM_CFLAGS = -I$(top_srcdir)
|
||||
noinst_PROGRAMS = verify_live enroll verify img_capture cpp-test
|
||||
|
||||
verify_live_SOURCES = verify_live.c
|
||||
verify_live_LDADD = ../libfprint/libfprint.la
|
||||
|
||||
enroll_SOURCES = enroll.c
|
||||
enroll_LDADD = ../libfprint/libfprint.la
|
||||
|
||||
verify_SOURCES = verify.c
|
||||
verify_LDADD = ../libfprint/libfprint.la
|
||||
|
||||
img_capture_SOURCES = img_capture.c
|
||||
img_capture_LDADD = ../libfprint/libfprint.la
|
||||
|
||||
cpp_test_SOURCES = cpp-test.cpp
|
||||
cpp_test_LDADD = ../libfprint/libfprint.la
|
||||
|
||||
if BUILD_X11_EXAMPLES
|
||||
noinst_PROGRAMS += img_capture_continuous
|
||||
|
||||
img_capture_continuous_CFLAGS = $(X_CFLAGS) $(XV_CFLAGS)
|
||||
img_capture_continuous_SOURCES = img_capture_continuous.c
|
||||
img_capture_continuous_LDADD = ../libfprint/libfprint.la $(X_LIBS) $(X_PRE_LIBS) $(XV_LIBS) -lX11 $(X_EXTRA_LIBS);
|
||||
endif
|
||||
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
fp_init ();
|
||||
return 0;
|
||||
FpContext *ctx;
|
||||
|
||||
ctx = fp_context_new ();
|
||||
g_object_unref (ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* Example fingerprint enrollment program
|
||||
* Enrolls your right index finger and saves the print to disk
|
||||
* Enrolls your chosen finger and saves the print to disk
|
||||
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -18,139 +19,207 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "example-enroll"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
#include <glib-unix.h>
|
||||
|
||||
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
||||
#include "storage.h"
|
||||
#include "utilities.h"
|
||||
|
||||
typedef struct _EnrollData
|
||||
{
|
||||
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;
|
||||
}
|
||||
GMainLoop *loop;
|
||||
GCancellable *cancellable;
|
||||
unsigned int sigint_handler;
|
||||
FpFinger finger;
|
||||
int ret_value;
|
||||
} EnrollData;
|
||||
|
||||
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;
|
||||
|
||||
sleep(1);
|
||||
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)
|
||||
static void
|
||||
enroll_data_free (EnrollData *enroll_data)
|
||||
{
|
||||
int r = 1;
|
||||
struct fp_dscv_dev *ddev;
|
||||
struct fp_dscv_dev **discovered_devs;
|
||||
struct fp_dev *dev;
|
||||
struct fp_print_data *data;
|
||||
g_clear_handle_id (&enroll_data->sigint_handler, g_source_remove);
|
||||
g_clear_object (&enroll_data->cancellable);
|
||||
g_main_loop_unref (enroll_data->loop);
|
||||
g_free (enroll_data);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (EnrollData, enroll_data_free)
|
||||
|
||||
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();
|
||||
static void
|
||||
on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
EnrollData *enroll_data = user_data;
|
||||
|
||||
r = fp_init();
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to initialize libfprint\n");
|
||||
exit(1);
|
||||
}
|
||||
fp_set_debug(3);
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
fprintf(stderr, "Could not discover devices\n");
|
||||
goto out;
|
||||
}
|
||||
fp_device_close_finish (dev, res, &error);
|
||||
|
||||
ddev = discover_device(discovered_devs);
|
||||
if (!ddev) {
|
||||
fprintf(stderr, "No devices detected.\n");
|
||||
goto out;
|
||||
}
|
||||
if (error)
|
||||
g_warning ("Failed closing device %s", error->message);
|
||||
|
||||
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 = fp_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;
|
||||
g_main_loop_quit (enroll_data->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
on_enroll_completed (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
EnrollData *enroll_data = user_data;
|
||||
|
||||
g_autoptr(FpPrint) print = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
print = fp_device_enroll_finish (dev, res, &error);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
enroll_data->ret_value = EXIT_SUCCESS;
|
||||
|
||||
if (fp_device_has_feature (dev, FP_DEVICE_FEATURE_STORAGE))
|
||||
g_debug ("Device has storage, saving a print reference locally");
|
||||
else
|
||||
g_debug ("Device has not storage, saving print only locally");
|
||||
|
||||
/* Even if the device has storage, it may not be able to save all the
|
||||
* metadata that the print contains, so we can always save a local copy
|
||||
* containing the handle to the device print */
|
||||
int r = print_data_save (print, enroll_data->finger);
|
||||
if (r < 0)
|
||||
{
|
||||
g_warning ("Data save failed, code %d", r);
|
||||
enroll_data->ret_value = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Enroll failed with error %s", error->message);
|
||||
}
|
||||
|
||||
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed,
|
||||
enroll_data);
|
||||
}
|
||||
|
||||
static void
|
||||
on_enroll_progress (FpDevice *device,
|
||||
gint completed_stages,
|
||||
FpPrint *print,
|
||||
gpointer user_data,
|
||||
GError *error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
g_warning ("Enroll stage %d of %d failed with error %s",
|
||||
completed_stages,
|
||||
fp_device_get_nr_enroll_stages (device),
|
||||
error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (print && fp_print_get_image (print) &&
|
||||
print_image_save (print, "enrolled.pgm"))
|
||||
printf ("Wrote scanned image to enrolled.pgm\n");
|
||||
|
||||
printf ("Enroll stage %d of %d passed. Yay!\n", completed_stages,
|
||||
fp_device_get_nr_enroll_stages (device));
|
||||
}
|
||||
|
||||
static void
|
||||
on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
EnrollData *enroll_data = user_data;
|
||||
FpPrint *print_template;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!fp_device_open_finish (dev, res, &error))
|
||||
{
|
||||
g_warning ("Failed to open device: %s", error->message);
|
||||
g_main_loop_quit (enroll_data->loop);
|
||||
return;
|
||||
}
|
||||
|
||||
printf ("Opened device. It's now time to enroll your finger.\n\n");
|
||||
printf ("You will need to successfully scan your %s finger %d times to "
|
||||
"complete the process.\n\n", finger_to_string (enroll_data->finger),
|
||||
fp_device_get_nr_enroll_stages (dev));
|
||||
printf ("Scan your finger now.\n");
|
||||
|
||||
print_template = print_create_template (dev, enroll_data->finger);
|
||||
fp_device_enroll (dev, print_template, enroll_data->cancellable,
|
||||
on_enroll_progress, NULL, NULL,
|
||||
(GAsyncReadyCallback) on_enroll_completed,
|
||||
enroll_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sigint_cb (void *user_data)
|
||||
{
|
||||
EnrollData *enroll_data = user_data;
|
||||
|
||||
g_cancellable_cancel (enroll_data->cancellable);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
g_autoptr(FpContext) ctx = NULL;
|
||||
g_autoptr(EnrollData) enroll_data = NULL;
|
||||
GPtrArray *devices;
|
||||
FpDevice *dev;
|
||||
FpFinger finger;
|
||||
|
||||
g_print ("This program will enroll the selected finger, unconditionally "
|
||||
"overwriting any print for the same finger that was enrolled "
|
||||
"previously. If you want to continue, press enter, otherwise hit "
|
||||
"Ctrl+C\n");
|
||||
getchar ();
|
||||
|
||||
g_print ("Choose the finger to enroll:\n");
|
||||
finger = finger_chooser ();
|
||||
|
||||
if (finger == FP_FINGER_UNKNOWN)
|
||||
{
|
||||
g_warning ("Unknown finger selected");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
setenv ("G_MESSAGES_DEBUG", "all", 0);
|
||||
|
||||
ctx = fp_context_new ();
|
||||
|
||||
devices = fp_context_get_devices (ctx);
|
||||
if (!devices)
|
||||
{
|
||||
g_warning ("Impossible to get devices");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
dev = discover_device (devices);
|
||||
if (!dev)
|
||||
{
|
||||
g_warning ("No devices detected.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
enroll_data = g_new0 (EnrollData, 1);
|
||||
enroll_data->finger = finger;
|
||||
enroll_data->ret_value = EXIT_FAILURE;
|
||||
enroll_data->loop = g_main_loop_new (NULL, FALSE);
|
||||
enroll_data->cancellable = g_cancellable_new ();
|
||||
enroll_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH,
|
||||
SIGINT,
|
||||
sigint_cb,
|
||||
enroll_data,
|
||||
NULL);
|
||||
|
||||
fp_device_open (dev, enroll_data->cancellable,
|
||||
(GAsyncReadyCallback) on_device_opened,
|
||||
enroll_data);
|
||||
|
||||
g_main_loop_run (enroll_data->loop);
|
||||
|
||||
return enroll_data->ret_value;
|
||||
}
|
||||
|
|
319
examples/identify.c
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* Example fingerprint verification program, which verifies the
|
||||
* finger which has been previously enrolled to disk.
|
||||
* Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "example-identify"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <libfprint/fprint.h>
|
||||
#include <glib-unix.h>
|
||||
|
||||
#include "storage.h"
|
||||
#include "utilities.h"
|
||||
|
||||
typedef struct _IdentifyData
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GCancellable *cancellable;
|
||||
unsigned int sigint_handler;
|
||||
int ret_value;
|
||||
} IdentifyData;
|
||||
|
||||
static void
|
||||
identify_data_free (IdentifyData *identify_data)
|
||||
{
|
||||
g_clear_handle_id (&identify_data->sigint_handler, g_source_remove);
|
||||
g_clear_object (&identify_data->cancellable);
|
||||
g_main_loop_unref (identify_data->loop);
|
||||
g_free (identify_data);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (IdentifyData, identify_data_free)
|
||||
|
||||
static void
|
||||
on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
IdentifyData *identify_data = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
fp_device_close_finish (dev, res, &error);
|
||||
|
||||
if (error)
|
||||
g_warning ("Failed closing device %s", error->message);
|
||||
|
||||
g_main_loop_quit (identify_data->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
identify_quit (FpDevice *dev,
|
||||
IdentifyData *identify_data)
|
||||
{
|
||||
if (!fp_device_is_open (dev))
|
||||
{
|
||||
g_main_loop_quit (identify_data->loop);
|
||||
return;
|
||||
}
|
||||
|
||||
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, identify_data);
|
||||
}
|
||||
|
||||
static void start_identification (FpDevice *dev,
|
||||
IdentifyData *identify_data);
|
||||
|
||||
static void
|
||||
on_identify_completed (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
IdentifyData *identify_data = user_data;
|
||||
|
||||
g_autoptr(FpPrint) print = NULL;
|
||||
g_autoptr(FpPrint) match = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
char buffer[20];
|
||||
|
||||
if (!fp_device_identify_finish (dev, res, &match, &print, &error))
|
||||
{
|
||||
g_warning ("Failed to identify print: %s", error->message);
|
||||
identify_data->ret_value = EXIT_FAILURE;
|
||||
|
||||
if (error->domain != FP_DEVICE_RETRY)
|
||||
{
|
||||
identify_quit (dev, identify_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g_print ("Identify again? [Y/n]? ");
|
||||
if (fgets (buffer, sizeof (buffer), stdin) &&
|
||||
(buffer[0] == 'Y' || buffer[0] == 'y' || buffer[0] == '\n'))
|
||||
{
|
||||
start_identification (dev, identify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
identify_quit (dev, identify_data);
|
||||
}
|
||||
|
||||
static FpPrint *
|
||||
get_stored_print (FpDevice *dev, FpPrint *print)
|
||||
{
|
||||
g_autoptr(GPtrArray) gallery = gallery_data_load (dev);
|
||||
guint index;
|
||||
|
||||
if (g_ptr_array_find_with_equal_func (gallery, print,
|
||||
(GEqualFunc) fp_print_equal,
|
||||
&index))
|
||||
return g_object_ref (g_ptr_array_index (gallery, index));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
on_identify_cb (FpDevice *dev, FpPrint *match, FpPrint *print,
|
||||
gpointer user_data, GError *error)
|
||||
{
|
||||
IdentifyData *identify_data = user_data;
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_warning ("Identify report: No finger matched, retry error reported: %s",
|
||||
error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (print && fp_print_get_image (print) &&
|
||||
print_image_save (print, "identify.pgm"))
|
||||
g_print ("Print image saved as identify.pgm\n");
|
||||
|
||||
if (match)
|
||||
{
|
||||
g_autoptr(FpPrint) matched_print = g_object_ref (match);
|
||||
char date_str[128] = {};
|
||||
|
||||
identify_data->ret_value = EXIT_SUCCESS;
|
||||
|
||||
if (fp_print_get_device_stored (match))
|
||||
{
|
||||
FpPrint *stored_print = get_stored_print (dev, match);
|
||||
|
||||
if (stored_print)
|
||||
matched_print = g_steal_pointer (&stored_print);
|
||||
}
|
||||
|
||||
if (fp_print_get_enroll_date (matched_print))
|
||||
g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0",
|
||||
fp_print_get_enroll_date (matched_print));
|
||||
else
|
||||
strcpy (date_str, "<unknown>");
|
||||
|
||||
g_debug ("Identify report: device %s matched finger %s successfully "
|
||||
"with print '%s', enrolled on date %s by user %s",
|
||||
fp_device_get_name (dev),
|
||||
finger_to_string (fp_print_get_finger (matched_print)),
|
||||
fp_print_get_description (matched_print), date_str,
|
||||
fp_print_get_username (matched_print));
|
||||
|
||||
g_print ("IDENTIFIED!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_debug ("Identification report: No finger matched");
|
||||
g_print ("NOT IDENTIFIED!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
IdentifyData *identify_data = user_data;
|
||||
|
||||
g_autoptr(GPtrArray) gallery = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
gallery = fp_device_list_prints_finish (dev, res, &error);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
if (!gallery->len)
|
||||
{
|
||||
g_warning ("No prints saved on device");
|
||||
identify_quit (dev, identify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("Identifying with %u prints in gallery", gallery->len);
|
||||
fp_device_identify (dev, gallery, identify_data->cancellable,
|
||||
on_identify_cb, identify_data, NULL,
|
||||
(GAsyncReadyCallback) on_identify_completed,
|
||||
identify_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Loading prints failed with error %s", error->message);
|
||||
identify_quit (dev, identify_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
start_identification (FpDevice *dev, IdentifyData *identify_data)
|
||||
{
|
||||
if (fp_device_has_feature (dev, FP_DEVICE_FEATURE_STORAGE))
|
||||
{
|
||||
g_print ("Creating finger template, using device storage...\n");
|
||||
fp_device_list_prints (dev, NULL,
|
||||
(GAsyncReadyCallback) on_list_completed,
|
||||
identify_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autoptr(GPtrArray) gallery = gallery_data_load (dev);
|
||||
|
||||
if (!gallery)
|
||||
{
|
||||
identify_quit (dev, identify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_print ("Gallery loaded. Time to identify!\n");
|
||||
fp_device_identify (dev, gallery, identify_data->cancellable,
|
||||
on_identify_cb, identify_data, NULL,
|
||||
(GAsyncReadyCallback) on_identify_completed,
|
||||
identify_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
IdentifyData *identify_data = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!fp_device_open_finish (dev, res, &error))
|
||||
{
|
||||
g_warning ("Failed to open device: %s", error->message);
|
||||
identify_quit (dev, identify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_print ("Opened device. ");
|
||||
|
||||
start_identification (dev, identify_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sigint_cb (void *user_data)
|
||||
{
|
||||
IdentifyData *identify_data = user_data;
|
||||
|
||||
g_cancellable_cancel (identify_data->cancellable);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
g_autoptr(FpContext) ctx = NULL;
|
||||
g_autoptr(IdentifyData) identify_data = NULL;
|
||||
GPtrArray *devices;
|
||||
FpDevice *dev;
|
||||
|
||||
setenv ("G_MESSAGES_DEBUG", "all", 0);
|
||||
setenv ("LIBUSB_DEBUG", "3", 0);
|
||||
|
||||
ctx = fp_context_new ();
|
||||
|
||||
devices = fp_context_get_devices (ctx);
|
||||
if (!devices)
|
||||
{
|
||||
g_warning ("Impossible to get devices");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
dev = discover_device (devices);
|
||||
if (!dev)
|
||||
{
|
||||
g_warning ("No devices detected.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!fp_device_has_feature (dev, FP_DEVICE_FEATURE_IDENTIFY))
|
||||
{
|
||||
g_warning ("Device %s does not support identification.",
|
||||
fp_device_get_name (dev));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
identify_data = g_new0 (IdentifyData, 1);
|
||||
identify_data->ret_value = EXIT_FAILURE;
|
||||
identify_data->loop = g_main_loop_new (NULL, FALSE);
|
||||
identify_data->cancellable = g_cancellable_new ();
|
||||
identify_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH,
|
||||
SIGINT,
|
||||
sigint_cb,
|
||||
identify_data,
|
||||
NULL);
|
||||
fp_device_open (dev, identify_data->cancellable,
|
||||
(GAsyncReadyCallback) on_device_opened,
|
||||
identify_data);
|
||||
|
||||
g_main_loop_run (identify_data->loop);
|
||||
|
||||
return identify_data->ret_value;
|
||||
}
|
192
examples/img-capture.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Example fingerprint verification program, which verifies the
|
||||
* finger which has been previously enrolled to disk.
|
||||
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
* Copyright (C) 2020 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "example-capture"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <libfprint/fprint.h>
|
||||
#include <glib-unix.h>
|
||||
|
||||
#include "storage.h"
|
||||
#include "utilities.h"
|
||||
|
||||
typedef struct CaptureData
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GCancellable *cancellable;
|
||||
unsigned int sigint_handler;
|
||||
int ret_value;
|
||||
const char *filename;
|
||||
} CaptureData;
|
||||
|
||||
static void
|
||||
capture_data_free (CaptureData *capture_data)
|
||||
{
|
||||
g_clear_handle_id (&capture_data->sigint_handler, g_source_remove);
|
||||
g_clear_object (&capture_data->cancellable);
|
||||
g_main_loop_unref (capture_data->loop);
|
||||
g_free (capture_data);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (CaptureData, capture_data_free)
|
||||
|
||||
static void
|
||||
on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
CaptureData *capture_data = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
fp_device_close_finish (dev, res, &error);
|
||||
|
||||
if (error)
|
||||
g_warning ("Failed closing device %s", error->message);
|
||||
|
||||
g_main_loop_quit (capture_data->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
capture_quit (FpDevice *dev,
|
||||
CaptureData *capture_data)
|
||||
{
|
||||
if (!fp_device_is_open (dev))
|
||||
{
|
||||
g_main_loop_quit (capture_data->loop);
|
||||
return;
|
||||
}
|
||||
|
||||
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, capture_data);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_capture_cb (FpDevice *dev,
|
||||
GAsyncResult *res,
|
||||
void *user_data)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
CaptureData *capture_data = user_data;
|
||||
FpImage *image = NULL;
|
||||
|
||||
g_clear_object (&capture_data->cancellable);
|
||||
|
||||
image = fp_device_capture_finish (dev, res, &error);
|
||||
if (!image)
|
||||
{
|
||||
g_warning ("Error capturing data: %s", error->message);
|
||||
capture_quit (dev, capture_data);
|
||||
return;
|
||||
}
|
||||
|
||||
save_image_to_pgm (image, capture_data->filename);
|
||||
|
||||
capture_quit (dev, capture_data);
|
||||
}
|
||||
|
||||
static void
|
||||
start_capture (FpDevice *dev, CaptureData *capture_data)
|
||||
{
|
||||
fp_device_capture (dev, TRUE, capture_data->cancellable, (GAsyncReadyCallback) dev_capture_cb, capture_data);
|
||||
}
|
||||
|
||||
static void
|
||||
on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
CaptureData *capture_data = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!fp_device_open_finish (dev, res, &error))
|
||||
{
|
||||
g_warning ("Failed to open device: %s", error->message);
|
||||
capture_quit (dev, capture_data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_print ("Opened device. ");
|
||||
|
||||
start_capture (dev, capture_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sigint_cb (void *user_data)
|
||||
{
|
||||
CaptureData *capture_data = user_data;
|
||||
|
||||
g_cancellable_cancel (capture_data->cancellable);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, const char *argv[])
|
||||
{
|
||||
g_autoptr(FpContext) ctx = NULL;
|
||||
g_autoptr(CaptureData) capture_data = NULL;
|
||||
GPtrArray *devices;
|
||||
FpDevice *dev;
|
||||
|
||||
setenv ("G_MESSAGES_DEBUG", "all", 0);
|
||||
setenv ("LIBUSB_DEBUG", "3", 0);
|
||||
|
||||
ctx = fp_context_new ();
|
||||
|
||||
devices = fp_context_get_devices (ctx);
|
||||
if (!devices)
|
||||
{
|
||||
g_warning ("Impossible to get devices");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
dev = discover_device (devices);
|
||||
if (!dev)
|
||||
{
|
||||
g_warning ("No devices detected.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!fp_device_has_feature (dev, FP_DEVICE_FEATURE_CAPTURE))
|
||||
{
|
||||
g_warning ("Device %s doesn't support capture",
|
||||
fp_device_get_name (dev));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
capture_data = g_new0 (CaptureData, 1);
|
||||
capture_data->ret_value = EXIT_FAILURE;
|
||||
capture_data->loop = g_main_loop_new (NULL, FALSE);
|
||||
capture_data->cancellable = g_cancellable_new ();
|
||||
capture_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH,
|
||||
SIGINT,
|
||||
sigint_cb,
|
||||
capture_data,
|
||||
NULL);
|
||||
if (argc == 2)
|
||||
capture_data->filename = argv[1];
|
||||
else
|
||||
capture_data->filename = "finger.pgm";
|
||||
fp_device_open (dev, capture_data->cancellable,
|
||||
(GAsyncReadyCallback) on_device_opened,
|
||||
capture_data);
|
||||
|
||||
g_main_loop_run (capture_data->loop);
|
||||
|
||||
return capture_data->ret_value;
|
||||
}
|
|
@ -1,105 +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;
|
||||
|
||||
r = fp_init();
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to initialize libfprint\n");
|
||||
exit(1);
|
||||
}
|
||||
fp_set_debug(3);
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,259 +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;
|
||||
int 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()
|
||||
{
|
||||
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;
|
||||
|
||||
r = fp_init();
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to initialize libfprint\n");
|
||||
exit(1);
|
||||
}
|
||||
fp_set_debug(3);
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
283
examples/manage-prints.c
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Example fingerprint device prints listing and deletion
|
||||
* Enrolls your right index finger and saves the print to disk
|
||||
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "example-mange-prints"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
#include "utilities.h"
|
||||
|
||||
typedef struct _ListData
|
||||
{
|
||||
GMainLoop *loop;
|
||||
int ret_value;
|
||||
GList *to_delete;
|
||||
gboolean any_failed;
|
||||
} ListData;
|
||||
|
||||
static void
|
||||
list_data_free (ListData *list_data)
|
||||
{
|
||||
g_list_free_full (list_data->to_delete, g_object_unref);
|
||||
g_main_loop_unref (list_data->loop);
|
||||
g_free (list_data);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ListData, list_data_free)
|
||||
|
||||
static void
|
||||
on_device_closed (FpDevice *dev,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
ListData *list_data = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
fp_device_close_finish (dev, res, &error);
|
||||
|
||||
if (error)
|
||||
g_warning ("Failed closing device %s", error->message);
|
||||
|
||||
g_main_loop_quit (list_data->loop);
|
||||
}
|
||||
|
||||
typedef struct _DeleteData
|
||||
{
|
||||
ListData *list_data;
|
||||
FpPrint *print;
|
||||
} DeleteData;
|
||||
|
||||
static void
|
||||
delete_data_free (DeleteData *delete_data)
|
||||
{
|
||||
g_object_unref (delete_data->print);
|
||||
g_free (delete_data);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (DeleteData, delete_data_free);
|
||||
|
||||
static void on_print_deleted (FpDevice *dev,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data);
|
||||
|
||||
static void
|
||||
delete_next_print (FpDevice *dev,
|
||||
ListData *list_data)
|
||||
{
|
||||
FpPrint *print;
|
||||
|
||||
g_assert_nonnull (list_data->to_delete);
|
||||
print = list_data->to_delete->data;
|
||||
|
||||
g_debug ("Deleting print %s", fp_print_get_description (print));
|
||||
fp_device_delete_print (dev, print, NULL,
|
||||
(GAsyncReadyCallback) on_print_deleted, list_data);
|
||||
}
|
||||
|
||||
static void
|
||||
on_print_deleted (FpDevice *dev,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
ListData *list_data = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(FpPrint) print = NULL;
|
||||
GList *deleted_link;
|
||||
|
||||
fp_device_delete_print_finish (dev, res, &error);
|
||||
|
||||
deleted_link = list_data->to_delete;
|
||||
print = g_steal_pointer (&deleted_link->data);
|
||||
list_data->to_delete = g_list_delete_link (list_data->to_delete, deleted_link);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_warning ("Failed to remove print %s: %s",
|
||||
fp_print_get_description (print), error->message);
|
||||
list_data->any_failed = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_debug ("Deleted print %s from device", fp_print_get_description (print));
|
||||
}
|
||||
|
||||
if (list_data->to_delete != NULL)
|
||||
{
|
||||
delete_next_print (dev, list_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!list_data->any_failed)
|
||||
list_data->ret_value = EXIT_SUCCESS;
|
||||
|
||||
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed,
|
||||
list_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_list_completed (FpDevice *dev,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
ListData *list_data = user_data;
|
||||
|
||||
g_autoptr(GPtrArray) prints = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
prints = fp_device_list_prints_finish (dev, res, &error);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
guint i;
|
||||
char buf[128];
|
||||
|
||||
g_print ("Device contains %u prints\n", prints->len);
|
||||
|
||||
for (i = 0; i < prints->len; ++i)
|
||||
{
|
||||
FpPrint * print = prints->pdata[i];
|
||||
const GDate *date = fp_print_get_enroll_date (print);
|
||||
|
||||
g_print ("[%d] Print of %s finger for username %s", i + 1,
|
||||
finger_to_string (fp_print_get_finger (print)),
|
||||
fp_print_get_username (print));
|
||||
|
||||
if (date)
|
||||
{
|
||||
g_date_strftime (buf, G_N_ELEMENTS (buf), "%Y-%m-%d\0", date);
|
||||
g_print (", enrolled on %s", buf);
|
||||
}
|
||||
|
||||
g_print (". Description: %s\n", fp_print_get_description (print));
|
||||
}
|
||||
|
||||
if (prints->len)
|
||||
{
|
||||
gint64 idx = 0;
|
||||
|
||||
g_print ("Want to delete saved print? [<number>/A/n]\n> ");
|
||||
if (fgets (buf, 3, stdin))
|
||||
idx = g_ascii_strtoll (buf, NULL, 10);
|
||||
|
||||
if (idx > 0 && idx <= prints->len)
|
||||
{
|
||||
FpPrint *print = prints->pdata[idx - 1];
|
||||
list_data->to_delete = g_list_prepend (list_data->to_delete,
|
||||
g_object_ref (print));
|
||||
}
|
||||
else if (buf[0] == 'A')
|
||||
{
|
||||
for (i = 0; i < prints->len; ++i)
|
||||
{
|
||||
FpPrint *print = prints->pdata[i];
|
||||
list_data->to_delete = g_list_prepend (list_data->to_delete,
|
||||
g_object_ref (print));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (buf[0] == 'n' || buf[0] == 'N')
|
||||
list_data->ret_value = EXIT_SUCCESS;
|
||||
else
|
||||
g_warning ("Invalid finger selected");
|
||||
}
|
||||
}
|
||||
|
||||
if (list_data->to_delete)
|
||||
delete_next_print (dev, list_data);
|
||||
else
|
||||
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed,
|
||||
list_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Getting prints failed with error %s", error->message);
|
||||
g_main_loop_quit (list_data->loop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_device_opened (FpDevice *dev,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
ListData *list_data = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!fp_device_open_finish (dev, res, &error))
|
||||
{
|
||||
g_warning ("Failed to open device: %s", error->message);
|
||||
g_main_loop_quit (list_data->loop);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fp_device_has_feature (dev, FP_DEVICE_FEATURE_STORAGE))
|
||||
{
|
||||
g_warning ("Device %s doesn't support storage", fp_device_get_name (dev));
|
||||
g_main_loop_quit (list_data->loop);
|
||||
return;
|
||||
}
|
||||
|
||||
fp_device_list_prints (dev, NULL,
|
||||
(GAsyncReadyCallback) on_list_completed, list_data);
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
g_autoptr(FpContext) ctx = NULL;
|
||||
g_autoptr(ListData) list_data = NULL;
|
||||
GPtrArray *devices;
|
||||
FpDevice *dev;
|
||||
|
||||
g_print ("This program will report the prints saved in device\n");
|
||||
|
||||
setenv ("G_MESSAGES_DEBUG", "all", 0);
|
||||
|
||||
ctx = fp_context_new ();
|
||||
|
||||
devices = fp_context_get_devices (ctx);
|
||||
if (!devices)
|
||||
{
|
||||
g_warning ("Impossible to get devices");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
dev = discover_device (devices);
|
||||
if (!dev)
|
||||
{
|
||||
g_warning ("No devices detected.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
list_data = g_new0 (ListData, 1);
|
||||
list_data->ret_value = EXIT_FAILURE;
|
||||
list_data->loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
fp_device_open (dev, NULL, (GAsyncReadyCallback) on_device_opened, list_data);
|
||||
|
||||
g_main_loop_run (list_data->loop);
|
||||
|
||||
return list_data->ret_value;
|
||||
}
|
23
examples/meson.build
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
examples = [
|
||||
'enroll',
|
||||
'identify',
|
||||
'img-capture',
|
||||
'manage-prints',
|
||||
'verify',
|
||||
]
|
||||
|
||||
foreach example: examples
|
||||
executable(example,
|
||||
[ example + '.c', 'storage.c', 'utilities.c' ],
|
||||
dependencies: [
|
||||
libfprint_dep,
|
||||
glib_dep,
|
||||
],
|
||||
)
|
||||
endforeach
|
||||
|
||||
executable('cpp-test',
|
||||
'cpp-test.cpp',
|
||||
dependencies: libfprint_dep,
|
||||
)
|
3
examples/prints/README
Normal file
|
@ -0,0 +1,3 @@
|
|||
These are example images from NIST and are in the public domain.
|
||||
|
||||
The PNG files have been generated by using the greyscale data as a mask.
|
BIN
examples/prints/arch.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
examples/prints/arch.png
Normal file
After Width: | Height: | Size: 120 KiB |
BIN
examples/prints/loop-right.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
examples/prints/loop-right.png
Normal file
After Width: | Height: | Size: 120 KiB |
BIN
examples/prints/tented_arch.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
examples/prints/tented_arch.png
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
examples/prints/whorl.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
examples/prints/whorl.png
Normal file
After Width: | Height: | Size: 102 KiB |
111
examples/sendvirtimg.py
Executable file
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# This script can be used together with the virtual_imgdev to simulate an
|
||||
# image based fingerprint reader.
|
||||
#
|
||||
# To use, set the FP_VIRTUAL_IMAGE environment variable for both the
|
||||
# libfprint using program (e.g. fprintd) and this script.
|
||||
#
|
||||
# Usually this would work by adding it into the systemd unit file. The
|
||||
# best way of doing so is to create
|
||||
# /etc/systemd/system/fprintd.service.d/fprintd-test.conf
|
||||
#
|
||||
# [Service]
|
||||
# RuntimeDirectory=fprint
|
||||
# Environment=FP_VIRTUAL_IMAGE=/run/fprint/virtimg_sock
|
||||
# Environment=G_MESSAGES_DEBUG=all
|
||||
# ReadWritePaths=$RUNTIME_DIR
|
||||
#
|
||||
# After that run:
|
||||
#
|
||||
# systemctl daemon-reload
|
||||
# systemctl restart fprintd.service
|
||||
#
|
||||
# You may also need to disable selinux.
|
||||
#
|
||||
# Then run this script with e.g.
|
||||
# FP_VIRTUAL_IMAGE=/run/fprint/virtimg_sock ./sendvirtimg.py prints/whorl.png
|
||||
|
||||
|
||||
|
||||
import cairo
|
||||
import sys
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
sys.stderr.write('You need to pass a PNG with an alpha channel!\n')
|
||||
sys.exit(1)
|
||||
|
||||
# Just copied from the C file, we could also use the introspection data for
|
||||
# this. Also, most of them do *not* make any sense.
|
||||
commands = {
|
||||
'retry' : struct.pack('ii', -1, 0),
|
||||
'retry-too-short' : struct.pack('ii', -1, 1),
|
||||
'retry-center-finger' : struct.pack('ii', -1, 2),
|
||||
'retry-remove-finger' : struct.pack('ii', -1, 3),
|
||||
|
||||
'error' : struct.pack('ii', -2, 0),
|
||||
'error-not-supported' : struct.pack('ii', -2, 1),
|
||||
'error-not-open' : struct.pack('ii', -2, 2),
|
||||
'error-already-open' : struct.pack('ii', -2, 3),
|
||||
'error-busy' : struct.pack('ii', -2, 4),
|
||||
'error-proto' : struct.pack('ii', -2, 5),
|
||||
'error-data-invalid' : struct.pack('ii', -2, 6),
|
||||
'error-data-not-found' : struct.pack('ii', -2, 7),
|
||||
'error-data-full' : struct.pack('ii', -2, 8),
|
||||
}
|
||||
|
||||
|
||||
if sys.argv[1] in commands:
|
||||
command = commands[sys.argv[1]]
|
||||
else:
|
||||
png = cairo.ImageSurface.create_from_png(sys.argv[1])
|
||||
|
||||
# Cairo wants 4 byte aligned rows, so just add a few pixel if necessary
|
||||
w = png.get_width()
|
||||
h = png.get_height()
|
||||
w = (w + 3) // 4 * 4
|
||||
h = (h + 3) // 4 * 4
|
||||
img = cairo.ImageSurface(cairo.Format.A8, w, h)
|
||||
cr = cairo.Context(img)
|
||||
|
||||
cr.set_source_rgba(1, 1, 1, 1)
|
||||
cr.paint()
|
||||
|
||||
cr.set_source_rgba(0, 0, 0, 0)
|
||||
cr.set_operator(cairo.OPERATOR_SOURCE)
|
||||
|
||||
cr.set_source_surface(png)
|
||||
cr.paint()
|
||||
|
||||
mem = img.get_data()
|
||||
mem = mem.tobytes()
|
||||
assert len(mem) == img.get_width() * img.get_height()
|
||||
|
||||
command = struct.pack('ii', img.get_width(), img.get_height())
|
||||
command += mem
|
||||
|
||||
|
||||
|
||||
def write_dbg_img():
|
||||
dbg_img_rgb = cairo.ImageSurface(cairo.Format.RGB24, img.get_width(), img.get_height())
|
||||
dbg_cr = cairo.Context(dbg_img_rgb)
|
||||
dbg_cr.set_source_rgb(0, 0, 0)
|
||||
dbg_cr.paint()
|
||||
dbg_cr.set_source_rgb(1, 1, 1)
|
||||
dbg_cr.mask_surface(img, 0, 0)
|
||||
|
||||
dbg_img_rgb.write_to_png('/tmp/test.png')
|
||||
|
||||
#write_dbg_img()
|
||||
|
||||
# Send image through socket
|
||||
sockaddr = os.environ['FP_VIRTUAL_IMAGE']
|
||||
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.connect(sockaddr)
|
||||
|
||||
sock.sendall(command)
|
||||
|
280
examples/storage.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Trivial storage driver for example programs
|
||||
*
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
* Copyright (C) 2019-2020 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "example-storage"
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
#include <libfprint/fpi-compat.h>
|
||||
#include "storage.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define STORAGE_FILE "test-storage.variant"
|
||||
|
||||
static char *
|
||||
get_print_data_descriptor (FpPrint *print, FpDevice *dev, FpFinger finger)
|
||||
{
|
||||
const char *driver;
|
||||
const char *dev_id;
|
||||
|
||||
if (print)
|
||||
{
|
||||
driver = fp_print_get_driver (print);
|
||||
dev_id = fp_print_get_device_id (print);
|
||||
}
|
||||
else
|
||||
{
|
||||
driver = fp_device_get_driver (dev);
|
||||
dev_id = fp_device_get_device_id (dev);
|
||||
}
|
||||
|
||||
return g_strdup_printf ("%s/%s/%x",
|
||||
driver,
|
||||
dev_id,
|
||||
finger);
|
||||
}
|
||||
|
||||
static GVariantDict *
|
||||
load_data (void)
|
||||
{
|
||||
GVariantDict *res;
|
||||
GVariant *var;
|
||||
gchar *contents = NULL;
|
||||
gsize 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,
|
||||
g_free,
|
||||
contents);
|
||||
|
||||
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 (FpPrint *print, FpFinger finger)
|
||||
{
|
||||
g_autofree gchar *descr = get_print_data_descriptor (print, NULL, finger);
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(GVariantDict) dict = NULL;
|
||||
g_autofree guchar *data = NULL;
|
||||
GVariant *val;
|
||||
gsize size;
|
||||
int res;
|
||||
|
||||
dict = load_data ();
|
||||
|
||||
fp_print_serialize (print, &data, &size, &error);
|
||||
if (error)
|
||||
{
|
||||
g_warning ("Error serializing data: %s", error->message);
|
||||
return -1;
|
||||
}
|
||||
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));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
FpPrint *
|
||||
print_data_load (FpDevice *dev, FpFinger finger)
|
||||
{
|
||||
g_autofree gchar *descr = get_print_data_descriptor (NULL, dev, finger);
|
||||
|
||||
g_autoptr(GVariant) val = NULL;
|
||||
g_autoptr(GVariantDict) dict = NULL;
|
||||
const guchar *stored_data = NULL;
|
||||
gsize stored_len;
|
||||
|
||||
dict = load_data ();
|
||||
val = g_variant_dict_lookup_value (dict, descr, G_VARIANT_TYPE ("ay"));
|
||||
|
||||
if (val)
|
||||
{
|
||||
FpPrint *print;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
stored_data = (const guchar *) g_variant_get_fixed_array (val, &stored_len, 1);
|
||||
print = fp_print_deserialize (stored_data, stored_len, &error);
|
||||
|
||||
if (error)
|
||||
g_warning ("Error deserializing data: %s", error->message);
|
||||
|
||||
return print;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
gallery_data_load (FpDevice *dev)
|
||||
{
|
||||
g_autoptr(GVariantDict) dict = NULL;
|
||||
g_autoptr(GVariant) dict_variant = NULL;
|
||||
g_autofree char *dev_prefix = NULL;
|
||||
GPtrArray *gallery;
|
||||
const char *driver;
|
||||
const char *dev_id;
|
||||
GVariantIter iter;
|
||||
GVariant *value;
|
||||
gchar *key;
|
||||
|
||||
gallery = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
dict = load_data ();
|
||||
dict_variant = g_variant_dict_end (dict);
|
||||
driver = fp_device_get_driver (dev);
|
||||
dev_id = fp_device_get_device_id (dev);
|
||||
dev_prefix = g_strdup_printf ("%s/%s/", driver, dev_id);
|
||||
|
||||
g_variant_iter_init (&iter, dict_variant);
|
||||
while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
|
||||
{
|
||||
FpPrint *print;
|
||||
const guchar *stored_data;
|
||||
g_autoptr(GError) error = NULL;
|
||||
gsize stored_len;
|
||||
|
||||
if (!g_str_has_prefix (key, dev_prefix))
|
||||
continue;
|
||||
|
||||
stored_data = (const guchar *) g_variant_get_fixed_array (value, &stored_len, 1);
|
||||
print = fp_print_deserialize (stored_data, stored_len, &error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_warning ("Error deserializing data: %s", error->message);
|
||||
continue;
|
||||
}
|
||||
|
||||
g_ptr_array_add (gallery, print);
|
||||
}
|
||||
|
||||
return gallery;
|
||||
}
|
||||
|
||||
FpPrint *
|
||||
print_create_template (FpDevice *dev, FpFinger finger)
|
||||
{
|
||||
g_autoptr(GDateTime) datetime = NULL;
|
||||
g_autoptr(GDate) date = NULL;
|
||||
FpPrint *template = NULL;
|
||||
gint year, month, day;
|
||||
|
||||
template = fp_print_new (dev);
|
||||
fp_print_set_finger (template, finger);
|
||||
fp_print_set_username (template, g_get_user_name ());
|
||||
datetime = g_date_time_new_now_local ();
|
||||
g_date_time_get_ymd (datetime, &year, &month, &day);
|
||||
date = g_date_new_dmy (day, month, year);
|
||||
fp_print_set_enroll_date (template, date);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
save_image_to_pgm (FpImage *img, const char *path)
|
||||
{
|
||||
FILE *fd = fopen (path, "w");
|
||||
size_t write_size;
|
||||
const guchar *data = fp_image_get_data (img, &write_size);
|
||||
int r;
|
||||
|
||||
if (!fd)
|
||||
{
|
||||
g_warning ("could not open '%s' for writing: %d", path, errno);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
r = fprintf (fd, "P5 %d %d 255\n",
|
||||
fp_image_get_width (img), fp_image_get_height (img));
|
||||
if (r < 0)
|
||||
{
|
||||
fclose (fd);
|
||||
g_critical ("pgm header write failed, error %d", r);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
r = fwrite (data, 1, write_size, fd);
|
||||
if (r < write_size)
|
||||
{
|
||||
fclose (fd);
|
||||
g_critical ("short write (%d)", r);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
fclose (fd);
|
||||
g_debug ("written to '%s'", path);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
print_image_save (FpPrint *print, const char *path)
|
||||
{
|
||||
FpImage *img = NULL;
|
||||
|
||||
g_return_val_if_fail (FP_IS_PRINT (print), FALSE);
|
||||
g_return_val_if_fail (path != NULL, FALSE);
|
||||
|
||||
img = fp_print_get_image (print);
|
||||
|
||||
if (img)
|
||||
return save_image_to_pgm (img, path);
|
||||
|
||||
return FALSE;
|
||||
}
|
33
examples/storage.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int print_data_save (FpPrint *print,
|
||||
FpFinger finger);
|
||||
FpPrint * print_data_load (FpDevice *dev,
|
||||
FpFinger finger);
|
||||
GPtrArray * gallery_data_load (FpDevice *dev);
|
||||
FpPrint * print_create_template (FpDevice *dev,
|
||||
FpFinger finger);
|
||||
gboolean print_image_save (FpPrint *print,
|
||||
const char *path);
|
||||
gboolean save_image_to_pgm (FpImage *img,
|
||||
const char *path);
|
128
examples/utilities.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Utilities for example programs
|
||||
*
|
||||
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "example-utilities"
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utilities.h"
|
||||
|
||||
FpDevice *
|
||||
discover_device (GPtrArray * devices)
|
||||
{
|
||||
FpDevice *dev;
|
||||
int i;
|
||||
|
||||
if (!devices->len)
|
||||
return NULL;
|
||||
|
||||
if (devices->len == 1)
|
||||
{
|
||||
i = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_print ("Multiple devices found, choose one\n");
|
||||
|
||||
for (i = 0; i < devices->len; ++i)
|
||||
{
|
||||
dev = g_ptr_array_index (devices, i);
|
||||
g_print ("[%d] %s (%s) - driver %s\n", i,
|
||||
fp_device_get_device_id (dev), fp_device_get_name (dev),
|
||||
fp_device_get_driver (dev));
|
||||
}
|
||||
|
||||
g_print ("> ");
|
||||
if (!scanf ("%d%*c", &i))
|
||||
return NULL;
|
||||
|
||||
if (i < 0 || i >= devices->len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = g_ptr_array_index (devices, i);
|
||||
g_print ("Selected device %s (%s) claimed by %s driver\n",
|
||||
fp_device_get_device_id (dev), fp_device_get_name (dev),
|
||||
fp_device_get_driver (dev));
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
const char *
|
||||
finger_to_string (FpFinger finger)
|
||||
{
|
||||
switch (finger)
|
||||
{
|
||||
case FP_FINGER_LEFT_THUMB:
|
||||
return "left thumb";
|
||||
|
||||
case FP_FINGER_LEFT_INDEX:
|
||||
return "left index";
|
||||
|
||||
case FP_FINGER_LEFT_MIDDLE:
|
||||
return "left middle";
|
||||
|
||||
case FP_FINGER_LEFT_RING:
|
||||
return "left ring";
|
||||
|
||||
case FP_FINGER_LEFT_LITTLE:
|
||||
return "left little";
|
||||
|
||||
case FP_FINGER_RIGHT_THUMB:
|
||||
return "right thumb";
|
||||
|
||||
case FP_FINGER_RIGHT_INDEX:
|
||||
return "right index";
|
||||
|
||||
case FP_FINGER_RIGHT_MIDDLE:
|
||||
return "right middle";
|
||||
|
||||
case FP_FINGER_RIGHT_RING:
|
||||
return "right ring";
|
||||
|
||||
case FP_FINGER_RIGHT_LITTLE:
|
||||
return "right little";
|
||||
|
||||
case FP_FINGER_UNKNOWN:
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
FpFinger
|
||||
finger_chooser (void)
|
||||
{
|
||||
int i = FP_FINGER_UNKNOWN;
|
||||
|
||||
for (i = FP_FINGER_FIRST; i <= FP_FINGER_LAST; ++i)
|
||||
g_print (" [%d] %s\n", (i - FP_FINGER_FIRST), finger_to_string (i));
|
||||
|
||||
g_print ("> ");
|
||||
if (!scanf ("%d%*c", &i))
|
||||
return FP_FINGER_UNKNOWN;
|
||||
|
||||
i += FP_FINGER_FIRST;
|
||||
|
||||
if (i < FP_FINGER_FIRST || i > FP_FINGER_LAST)
|
||||
return FP_FINGER_UNKNOWN;
|
||||
|
||||
return i;
|
||||
}
|
25
examples/utilities.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Trivial storage driver for example programs
|
||||
*
|
||||
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
FpDevice * discover_device (GPtrArray *devices);
|
||||
FpFinger finger_chooser (void);
|
||||
const char * finger_to_string (FpFinger finger);
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* Example fingerprint verification program, which verifies the right index
|
||||
* Example fingerprint verification program, which verifies the
|
||||
* finger which has been previously enrolled to disk.
|
||||
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -18,128 +19,342 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "example-verify"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
#include <glib-unix.h>
|
||||
|
||||
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
||||
#include "storage.h"
|
||||
#include "utilities.h"
|
||||
|
||||
typedef struct _VerifyData
|
||||
{
|
||||
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;
|
||||
GMainLoop *loop;
|
||||
GCancellable *cancellable;
|
||||
unsigned int sigint_handler;
|
||||
FpFinger finger;
|
||||
int ret_value;
|
||||
} VerifyData;
|
||||
|
||||
static void
|
||||
verify_data_free (VerifyData *verify_data)
|
||||
{
|
||||
g_clear_handle_id (&verify_data->sigint_handler, g_source_remove);
|
||||
g_clear_object (&verify_data->cancellable);
|
||||
g_main_loop_unref (verify_data->loop);
|
||||
g_free (verify_data);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (VerifyData, verify_data_free)
|
||||
|
||||
static void
|
||||
on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
VerifyData *verify_data = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
fp_device_close_finish (dev, res, &error);
|
||||
|
||||
if (error)
|
||||
g_warning ("Failed closing device %s", error->message);
|
||||
|
||||
g_main_loop_quit (verify_data->loop);
|
||||
}
|
||||
|
||||
int verify(struct fp_dev *dev, struct fp_print_data *data)
|
||||
static void
|
||||
verify_quit (FpDevice *dev,
|
||||
VerifyData *verify_data)
|
||||
{
|
||||
int r;
|
||||
if (!fp_device_is_open (dev))
|
||||
{
|
||||
g_main_loop_quit (verify_data->loop);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed, verify_data);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
static void start_verification (FpDevice *dev,
|
||||
VerifyData *verify_data);
|
||||
|
||||
static void
|
||||
on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
int r = 1;
|
||||
struct fp_dscv_dev *ddev;
|
||||
struct fp_dscv_dev **discovered_devs;
|
||||
struct fp_dev *dev;
|
||||
struct fp_print_data *data;
|
||||
VerifyData *verify_data = user_data;
|
||||
|
||||
r = fp_init();
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to initialize libfprint\n");
|
||||
exit(1);
|
||||
}
|
||||
fp_set_debug(3);
|
||||
g_autoptr(FpPrint) print = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
char buffer[20];
|
||||
gboolean match;
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
fprintf(stderr, "Could not discover devices\n");
|
||||
goto out;
|
||||
}
|
||||
if (!fp_device_verify_finish (dev, res, &match, &print, &error))
|
||||
{
|
||||
g_warning ("Failed to verify print: %s", error->message);
|
||||
verify_data->ret_value = EXIT_FAILURE;
|
||||
|
||||
ddev = discover_device(discovered_devs);
|
||||
if (!ddev) {
|
||||
fprintf(stderr, "No devices detected.\n");
|
||||
goto out;
|
||||
}
|
||||
if (error->domain != FP_DEVICE_RETRY)
|
||||
{
|
||||
verify_quit (dev, verify_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dev = fp_dev_open(ddev);
|
||||
fp_dscv_devs_free(discovered_devs);
|
||||
if (!dev) {
|
||||
fprintf(stderr, "Could not open device.\n");
|
||||
goto out;
|
||||
}
|
||||
g_print ("Verify again? [Y/n]? ");
|
||||
if (fgets (buffer, sizeof (buffer), stdin) &&
|
||||
(buffer[0] == 'Y' || buffer[0] == 'y' || buffer[0] == '\n'))
|
||||
{
|
||||
start_verification (dev, verify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Opened device. Loading previously enrolled right index finger "
|
||||
"data...\n");
|
||||
|
||||
r = fp_print_data_load(dev, RIGHT_INDEX, &data);
|
||||
if (r != 0) {
|
||||
fprintf(stderr, "Failed to load fingerprint, error %d\n", r);
|
||||
fprintf(stderr, "Did you remember to enroll your right index finger "
|
||||
"first?\n");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
printf("Print loaded. 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;
|
||||
verify_quit (dev, verify_data);
|
||||
}
|
||||
|
||||
static void
|
||||
on_match_cb (FpDevice *dev, FpPrint *match, FpPrint *print,
|
||||
gpointer user_data, GError *error)
|
||||
{
|
||||
VerifyData *verify_data = user_data;
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_warning ("Match report: Finger not matched, retry error reported: %s",
|
||||
error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (print && fp_print_get_image (print) &&
|
||||
print_image_save (print, "verify.pgm"))
|
||||
g_print ("Print image saved as verify.pgm\n");
|
||||
|
||||
if (match)
|
||||
{
|
||||
char date_str[128];
|
||||
|
||||
verify_data->ret_value = EXIT_SUCCESS;
|
||||
|
||||
g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0",
|
||||
fp_print_get_enroll_date (match));
|
||||
g_debug ("Match report: device %s matched finger %s successifully "
|
||||
"with print %s, enrolled on date %s by user %s",
|
||||
fp_device_get_name (dev),
|
||||
finger_to_string (fp_print_get_finger (match)),
|
||||
fp_print_get_description (match), date_str,
|
||||
fp_print_get_username (match));
|
||||
|
||||
g_print ("MATCH!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_debug ("Match report: Finger not matched");
|
||||
g_print ("NO MATCH!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static FpPrint *
|
||||
get_stored_print (FpDevice *dev, VerifyData *verify_data)
|
||||
{
|
||||
FpPrint *verify_print;
|
||||
|
||||
g_print ("Loading previously enrolled %s finger data...\n",
|
||||
finger_to_string (verify_data->finger));
|
||||
|
||||
verify_print = print_data_load (dev, verify_data->finger);
|
||||
|
||||
if (!verify_print)
|
||||
{
|
||||
g_warning ("Failed to load fingerprint data");
|
||||
g_warning ("Did you remember to enroll your %s finger first?",
|
||||
finger_to_string (verify_data->finger));
|
||||
}
|
||||
|
||||
return verify_print;
|
||||
}
|
||||
|
||||
static void
|
||||
on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
VerifyData *verify_data = user_data;
|
||||
|
||||
g_autoptr(GPtrArray) prints = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
prints = fp_device_list_prints_finish (dev, res, &error);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
FpPrint *verify_print = NULL;
|
||||
g_autoptr(FpPrint) stored_print = NULL;
|
||||
guint i;
|
||||
|
||||
if (!prints->len)
|
||||
{
|
||||
g_warning ("No prints saved on device");
|
||||
verify_quit (dev, verify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
stored_print = get_stored_print (dev, verify_data);
|
||||
|
||||
for (i = 0; i < prints->len; ++i)
|
||||
{
|
||||
FpPrint *print = prints->pdata[i];
|
||||
|
||||
if (stored_print && fp_print_equal (stored_print, print))
|
||||
/* If the private print data matches, let's use the stored print
|
||||
* as it contains more metadata to show */
|
||||
print = stored_print;
|
||||
|
||||
if (fp_print_get_finger (print) == verify_data->finger &&
|
||||
g_strcmp0 (fp_print_get_username (print), g_get_user_name ()) == 0)
|
||||
{
|
||||
const GDate *verify_print_date = NULL;
|
||||
const GDate *print_date = fp_print_get_enroll_date (print);
|
||||
|
||||
if (verify_print)
|
||||
verify_print_date = fp_print_get_enroll_date (verify_print);
|
||||
|
||||
if (!verify_print || !print_date || !verify_print_date ||
|
||||
g_date_compare (print_date, verify_print_date) >= 0)
|
||||
verify_print = print;
|
||||
}
|
||||
}
|
||||
|
||||
if (!verify_print)
|
||||
{
|
||||
verify_quit (dev, verify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("Comparing print with %s",
|
||||
fp_print_get_description (verify_print));
|
||||
|
||||
g_print ("Print loaded. Time to verify!\n");
|
||||
fp_device_verify (dev, verify_print, verify_data->cancellable,
|
||||
on_match_cb, verify_data, NULL,
|
||||
(GAsyncReadyCallback) on_verify_completed,
|
||||
verify_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Loading prints failed with error %s", error->message);
|
||||
verify_quit (dev, verify_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
start_verification (FpDevice *dev, VerifyData *verify_data)
|
||||
{
|
||||
if (verify_data->finger == FP_FINGER_UNKNOWN)
|
||||
{
|
||||
g_print ("Choose the finger to verify:\n");
|
||||
verify_data->finger = finger_chooser ();
|
||||
}
|
||||
|
||||
if (verify_data->finger == FP_FINGER_UNKNOWN)
|
||||
{
|
||||
g_warning ("Unknown finger selected");
|
||||
verify_data->ret_value = EXIT_FAILURE;
|
||||
verify_quit (dev, verify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fp_device_has_feature (dev, FP_DEVICE_FEATURE_STORAGE))
|
||||
{
|
||||
g_print ("Creating finger template, using device storage...\n");
|
||||
fp_device_list_prints (dev, NULL,
|
||||
(GAsyncReadyCallback) on_list_completed,
|
||||
verify_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autoptr(FpPrint) verify_print = get_stored_print (dev, verify_data);
|
||||
|
||||
if (!verify_print)
|
||||
{
|
||||
verify_quit (dev, verify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_print ("Print loaded. Time to verify!\n");
|
||||
fp_device_verify (dev, verify_print, verify_data->cancellable,
|
||||
on_match_cb, verify_data, NULL,
|
||||
(GAsyncReadyCallback) on_verify_completed,
|
||||
verify_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
|
||||
{
|
||||
VerifyData *verify_data = user_data;
|
||||
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!fp_device_open_finish (dev, res, &error))
|
||||
{
|
||||
g_warning ("Failed to open device: %s", error->message);
|
||||
verify_quit (dev, verify_data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_print ("Opened device. ");
|
||||
|
||||
start_verification (dev, verify_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sigint_cb (void *user_data)
|
||||
{
|
||||
VerifyData *verify_data = user_data;
|
||||
|
||||
g_cancellable_cancel (verify_data->cancellable);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
g_autoptr(FpContext) ctx = NULL;
|
||||
g_autoptr(VerifyData) verify_data = NULL;
|
||||
GPtrArray *devices;
|
||||
FpDevice *dev;
|
||||
|
||||
setenv ("G_MESSAGES_DEBUG", "all", 0);
|
||||
setenv ("LIBUSB_DEBUG", "3", 0);
|
||||
|
||||
ctx = fp_context_new ();
|
||||
|
||||
devices = fp_context_get_devices (ctx);
|
||||
if (!devices)
|
||||
{
|
||||
g_warning ("Impossible to get devices");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
dev = discover_device (devices);
|
||||
if (!dev)
|
||||
{
|
||||
g_warning ("No devices detected.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
verify_data = g_new0 (VerifyData, 1);
|
||||
verify_data->ret_value = EXIT_FAILURE;
|
||||
verify_data->loop = g_main_loop_new (NULL, FALSE);
|
||||
verify_data->cancellable = g_cancellable_new ();
|
||||
verify_data->sigint_handler = g_unix_signal_add_full (G_PRIORITY_HIGH,
|
||||
SIGINT,
|
||||
sigint_cb,
|
||||
verify_data,
|
||||
NULL);
|
||||
fp_device_open (dev, verify_data->cancellable,
|
||||
(GAsyncReadyCallback) on_device_opened,
|
||||
verify_data);
|
||||
|
||||
g_main_loop_run (verify_data->loop);
|
||||
|
||||
return verify_data->ret_value;
|
||||
}
|
||||
|
|
|
@ -1,187 +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 {
|
||||
sleep(1);
|
||||
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;
|
||||
|
||||
r = fp_init();
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Failed to initialize libfprint\n");
|
||||
exit(1);
|
||||
}
|
||||
fp_set_debug(3);
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: libfprint
|
||||
Description: Generic C API for fingerprint reader access
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lfprint
|
||||
Cflags: -I${includedir}/libfprint
|
||||
|
|
@ -1,227 +0,0 @@
|
|||
lib_LTLIBRARIES = libfprint.la
|
||||
noinst_PROGRAMS = fprint-list-udev-rules
|
||||
MOSTLYCLEANFILES = $(udev_rules_DATA)
|
||||
|
||||
UPEKE2_SRC = drivers/upeke2.c
|
||||
UPEKTS_SRC = drivers/upekts.c
|
||||
UPEKTC_SRC = drivers/upektc.c drivers/upektc.h
|
||||
UPEKSONLY_SRC = drivers/upeksonly.c drivers/upeksonly.h
|
||||
URU4000_SRC = drivers/uru4000.c
|
||||
AES1610_SRC = drivers/aes1610.c
|
||||
AES1660_SRC = drivers/aes1660.c drivers/aes1660.h
|
||||
AES2501_SRC = drivers/aes2501.c drivers/aes2501.h
|
||||
AES2550_SRC = drivers/aes2550.c drivers/aes2550.h
|
||||
AES2660_SRC = drivers/aes2660.c drivers/aes2660.h
|
||||
AES3500_SRC = drivers/aes3500.c
|
||||
AES4000_SRC = drivers/aes4000.c
|
||||
FDU2000_SRC = drivers/fdu2000.c
|
||||
VCOM5S_SRC = drivers/vcom5s.c
|
||||
VFS101_SRC = drivers/vfs101.c
|
||||
VFS301_SRC = drivers/vfs301.c drivers/vfs301_proto.c drivers/vfs301_proto.h drivers/vfs301_proto_fragments.h
|
||||
VFS5011_SRC = drivers/vfs5011.c drivers/vfs5011_proto.h
|
||||
UPEKTC_IMG_SRC = drivers/upektc_img.c drivers/upektc_img.h
|
||||
ETES603_SRC = drivers/etes603.c
|
||||
VFS0050_SRC = drivers/vfs0050.c drivers/vfs0050.h
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(UPEKE2_SRC) \
|
||||
$(UPEKTS_SRC) \
|
||||
$(UPEKTC_SRC) \
|
||||
$(UPEKSONLY_SRC) \
|
||||
$(URU4000_SRC) \
|
||||
$(AES1610_SRC) \
|
||||
$(AES1660_SRC) \
|
||||
$(AES2501_SRC) \
|
||||
$(AES2550_SRC) \
|
||||
$(AES2660_SRC) \
|
||||
$(AES3500_SRC) \
|
||||
$(AES4000_SRC) \
|
||||
$(FDU2000_SRC) \
|
||||
$(VCOM5S_SRC) \
|
||||
$(VFS101_SRC) \
|
||||
$(VFS301_SRC) \
|
||||
$(VFS5011_SRC) \
|
||||
$(UPEKTC_IMG_SRC) \
|
||||
$(ETES603_SRC) \
|
||||
$(VFS0050_SRC) \
|
||||
drivers/aesx660.c \
|
||||
drivers/aesx660.h \
|
||||
drivers/aes3k.c \
|
||||
drivers/aes3k.h \
|
||||
drivers/driver_ids.h \
|
||||
aeslib.c aeslib.h \
|
||||
assembling.c \
|
||||
assembling.h \
|
||||
pixman.c \
|
||||
60-fprint-autosuspend.rules
|
||||
|
||||
DRIVER_SRC =
|
||||
OTHER_SRC =
|
||||
|
||||
NBIS_SRC = \
|
||||
nbis/include/bozorth.h \
|
||||
nbis/include/bz_array.h \
|
||||
nbis/include/defs.h \
|
||||
nbis/include/lfs.h \
|
||||
nbis/include/log.h \
|
||||
nbis/include/morph.h \
|
||||
nbis/include/sunrast.h \
|
||||
nbis/bozorth3/bozorth3.c \
|
||||
nbis/bozorth3/bz_alloc.c \
|
||||
nbis/bozorth3/bz_drvrs.c \
|
||||
nbis/bozorth3/bz_gbls.c \
|
||||
nbis/bozorth3/bz_io.c \
|
||||
nbis/bozorth3/bz_sort.c \
|
||||
nbis/mindtct/binar.c \
|
||||
nbis/mindtct/block.c \
|
||||
nbis/mindtct/contour.c \
|
||||
nbis/mindtct/detect.c \
|
||||
nbis/mindtct/dft.c \
|
||||
nbis/mindtct/free.c \
|
||||
nbis/mindtct/globals.c \
|
||||
nbis/mindtct/imgutil.c \
|
||||
nbis/mindtct/init.c \
|
||||
nbis/mindtct/line.c \
|
||||
nbis/mindtct/log.c \
|
||||
nbis/mindtct/loop.c \
|
||||
nbis/mindtct/maps.c \
|
||||
nbis/mindtct/matchpat.c \
|
||||
nbis/mindtct/minutia.c \
|
||||
nbis/mindtct/morph.c \
|
||||
nbis/mindtct/quality.c \
|
||||
nbis/mindtct/remove.c \
|
||||
nbis/mindtct/ridges.c \
|
||||
nbis/mindtct/shape.c \
|
||||
nbis/mindtct/sort.c \
|
||||
nbis/mindtct/util.c
|
||||
|
||||
libfprint_la_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
|
||||
libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
|
||||
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(CRYPTO_LIBS)
|
||||
|
||||
fprint_list_udev_rules_SOURCES = fprint-list-udev-rules.c
|
||||
fprint_list_udev_rules_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
|
||||
fprint_list_udev_rules_LDADD = $(builddir)/libfprint.la $(GLIB_LIBS)
|
||||
|
||||
udev_rules_DATA = 60-fprint-autosuspend.rules
|
||||
|
||||
if ENABLE_UDEV_RULES
|
||||
$(udev_rules_DATA): fprint-list-udev-rules
|
||||
$(builddir)/fprint-list-udev-rules > $@
|
||||
endif
|
||||
|
||||
if ENABLE_UPEKE2
|
||||
DRIVER_SRC += $(UPEKE2_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_UPEKTS
|
||||
DRIVER_SRC += $(UPEKTS_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_UPEKSONLY
|
||||
DRIVER_SRC += $(UPEKSONLY_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_UPEKTC
|
||||
DRIVER_SRC += $(UPEKTC_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_URU4000
|
||||
DRIVER_SRC += $(URU4000_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_VCOM5S
|
||||
DRIVER_SRC += $(VCOM5S_SRC)
|
||||
endif
|
||||
|
||||
#if ENABLE_FDU2000
|
||||
#DRIVER_SRC += $(FDU2000_SRC)
|
||||
#endif
|
||||
|
||||
if ENABLE_AES1610
|
||||
DRIVER_SRC += $(AES1610_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_AES1660
|
||||
DRIVER_SRC += $(AES1660_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_AES2501
|
||||
DRIVER_SRC += $(AES2501_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_AES2550
|
||||
DRIVER_SRC += $(AES2550_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_AES2660
|
||||
DRIVER_SRC += $(AES2660_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_AES3500
|
||||
DRIVER_SRC += $(AES3500_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_AES4000
|
||||
DRIVER_SRC += $(AES4000_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_VFS101
|
||||
DRIVER_SRC += $(VFS101_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_VFS301
|
||||
DRIVER_SRC += $(VFS301_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_VFS5011
|
||||
DRIVER_SRC += $(VFS5011_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_UPEKTC_IMG
|
||||
DRIVER_SRC += $(UPEKTC_IMG_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_ETES603
|
||||
DRIVER_SRC += $(ETES603_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_VFS0050
|
||||
DRIVER_SRC += $(VFS0050_SRC)
|
||||
endif
|
||||
|
||||
if REQUIRE_PIXMAN
|
||||
OTHER_SRC += pixman.c
|
||||
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
|
||||
libfprint_la_LIBADD += $(IMAGING_LIBS)
|
||||
endif
|
||||
|
||||
if REQUIRE_AESLIB
|
||||
OTHER_SRC += aeslib.c aeslib.h
|
||||
endif
|
||||
|
||||
if REQUIRE_AESX660
|
||||
OTHER_SRC += drivers/aesx660.c drivers/aesx660.h
|
||||
endif
|
||||
|
||||
if REQUIRE_AES3K
|
||||
OTHER_SRC += drivers/aes3k.c drivers/aes3k.h
|
||||
endif
|
||||
|
||||
libfprint_la_SOURCES = \
|
||||
fp_internal.h \
|
||||
async.c \
|
||||
core.c \
|
||||
data.c \
|
||||
drv.c \
|
||||
img.c \
|
||||
imgdev.c \
|
||||
poll.c \
|
||||
sync.c \
|
||||
assembling.c \
|
||||
assembling.h \
|
||||
$(DRIVER_SRC) \
|
||||
$(OTHER_SRC) \
|
||||
$(NBIS_SRC)
|
||||
|
||||
pkginclude_HEADERS = fprint.h
|
|
@ -1,174 +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 <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libusb.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
#include "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);
|
||||
else if (transfer->length != transfer->actual_length)
|
||||
wdata->callback(wdata->imgdev, -EPROTO, wdata->user_data);
|
||||
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 = libusb_alloc_transfer(0);
|
||||
int r;
|
||||
|
||||
if (!transfer) {
|
||||
g_free(data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = offset; i < offset + num; i++) {
|
||||
const struct aes_regwrite *regwrite = &wdata->regs[i];
|
||||
data[data_offset++] = regwrite->reg;
|
||||
data[data_offset++] = regwrite->value;
|
||||
}
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, wdata->imgdev->udev, EP_OUT, data,
|
||||
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);
|
||||
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);
|
||||
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 = g_malloc(sizeof(*wdata));
|
||||
fp_dbg("write %d regs", num_regs);
|
||||
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;
|
||||
}
|
|
@ -1,423 +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 <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libusb.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
#include "assembling.h"
|
||||
|
||||
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);
|
||||
prev_stripe->delta_y = -prev_stripe->delta_y;
|
||||
prev_stripe->delta_x = -prev_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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t stripes_len)
|
||||
{
|
||||
GSList *stripe;
|
||||
struct fp_img *img;
|
||||
int height = 0;
|
||||
int i, y, x;
|
||||
gboolean reverse = FALSE;
|
||||
struct fpi_frame *fpi_frame;
|
||||
|
||||
BUG_ON(stripes_len == 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 < stripes_len);
|
||||
|
||||
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;
|
||||
|
||||
y += fpi_frame->delta_y;
|
||||
x += fpi_frame->delta_x;
|
||||
|
||||
aes_blit_stripe(ctx, img, fpi_frame, x, y);
|
||||
|
||||
stripe = g_slist_next(stripe);
|
||||
i++;
|
||||
} while (i < stripes_len);
|
||||
|
||||
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;
|
||||
g_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);
|
||||
}
|
||||
}
|
||||
|
||||
static int min(int a, int b) {return (a < b) ? a : b; }
|
||||
|
||||
/* Rescale image to account for variable swiping speed */
|
||||
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *lines, size_t lines_len)
|
||||
{
|
||||
/* 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((lines_len / 2) * sizeof(int));
|
||||
unsigned char *output = g_malloc0(ctx->line_width * ctx->max_height);
|
||||
struct fp_img *img;
|
||||
|
||||
fp_dbg("%llu", g_get_real_time());
|
||||
|
||||
row1 = lines;
|
||||
for (i = 0; (i < lines_len - 1) && row1; i += 2) {
|
||||
int bestmatch = i;
|
||||
int bestdiff = 0;
|
||||
int j, firstrow, lastrow;
|
||||
|
||||
firstrow = i + 1;
|
||||
lastrow = min(i + ctx->max_search_offset, lines_len - 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, (lines_len / 2) - 1, ctx->median_filter_size);
|
||||
|
||||
fp_dbg("offsets_filtered: %llu", g_get_real_time());
|
||||
for (i = 0; i <= (lines_len / 2) - 1; i++)
|
||||
fp_dbg("%d", offsets[i]);
|
||||
row1 = lines;
|
||||
for (i = 0; i < lines_len - 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;
|
||||
g_memmove(img->data, output, ctx->line_width * line_ind);
|
||||
g_free(offsets);
|
||||
g_free(output);
|
||||
return img;
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Image assembling routines
|
||||
* Shared functions between libfprint Authentec drivers
|
||||
* 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 __ASSEMBLING_H__
|
||||
#define __ASSEMBLING_H__
|
||||
|
||||
#include <fp_internal.h>
|
||||
|
||||
struct fpi_frame {
|
||||
int delta_x;
|
||||
int delta_y;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
struct fpi_frame_asmbl_ctx {
|
||||
unsigned frame_width;
|
||||
unsigned frame_height;
|
||||
unsigned image_width;
|
||||
unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *frame,
|
||||
unsigned x,
|
||||
unsigned y);
|
||||
};
|
||||
|
||||
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t stripes_len);
|
||||
|
||||
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t stripes_len);
|
||||
|
||||
struct fpi_line_asmbl_ctx {
|
||||
unsigned line_width;
|
||||
unsigned max_height;
|
||||
unsigned resolution;
|
||||
unsigned median_filter_size;
|
||||
unsigned 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 x);
|
||||
};
|
||||
|
||||
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *lines, size_t lines_len);
|
||||
|
||||
#endif
|
|
@ -1,512 +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 <config.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb cb,
|
||||
void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = ddev->drv;
|
||||
struct fp_dev *dev;
|
||||
libusb_device_handle *udevh;
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
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 = cb;
|
||||
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)
|
||||
{
|
||||
fp_dbg("");
|
||||
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
|
||||
dev->state = DEV_STATE_DEINITIALIZED;
|
||||
libusb_close(dev->udev);
|
||||
if (dev->close_cb)
|
||||
dev->close_cb(dev, dev->close_cb_data);
|
||||
g_free(dev);
|
||||
}
|
||||
|
||||
API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
|
||||
fp_dev_close_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
|
||||
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;
|
||||
|
||||
if (!drv->close) {
|
||||
fpi_drvcb_close_complete(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_enroll_start(struct fp_dev *dev,
|
||||
fp_enroll_stage_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int r;
|
||||
|
||||
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)
|
||||
{
|
||||
fp_dbg("");
|
||||
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);
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_enroll_stop(struct fp_dev *dev,
|
||||
fp_enroll_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
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;
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
|
||||
struct fp_print_data *data, fp_verify_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
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)
|
||||
{
|
||||
fp_dbg("");
|
||||
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)
|
||||
{
|
||||
fp_dbg("");
|
||||
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);
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
|
||||
fp_verify_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
gboolean iterating = (dev->state == DEV_STATE_VERIFYING);
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
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;
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_identify_start(struct fp_dev *dev,
|
||||
struct fp_print_data **gallery, fp_identify_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
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");
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
|
||||
fp_identify_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
gboolean iterating = (dev->state == DEV_STATE_IDENTIFYING);
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
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)
|
||||
{
|
||||
fp_dbg("");
|
||||
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);
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional,
|
||||
fp_capture_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
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 verification, error %d", r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when capture has started */
|
||||
void fpi_drvcb_capture_started(struct fp_dev *dev, int status)
|
||||
{
|
||||
fp_dbg("");
|
||||
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)
|
||||
{
|
||||
fp_dbg("");
|
||||
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);
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
|
||||
fp_capture_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
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 verification");
|
||||
dev->capture_stop_cb = NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
969
libfprint/core.c
|
@ -1,969 +0,0 @@
|
|||
/*
|
||||
* Core functions for libfprint
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
|
||||
static int log_level = 0;
|
||||
static int log_level_fixed = 0;
|
||||
|
||||
libusb_context *fpi_usb_ctx = NULL;
|
||||
GSList *opened_devices = NULL;
|
||||
|
||||
/**
|
||||
* \mainpage libfprint API Reference
|
||||
* libfprint is an open source library to provide access to fingerprint
|
||||
* scanning devices. For more info, see the
|
||||
* <a href="http://www.reactivated.net/fprint/Libfprint">libfprint project
|
||||
* homepage</a>.
|
||||
*
|
||||
* This documentation is aimed at application developers who wish to integrate
|
||||
* fingerprint-related functionality into their software. libfprint has been
|
||||
* designed so that you only have to do this once - by integrating your
|
||||
* software with libfprint, you'll be supporting all the fingerprint readers
|
||||
* that we have got our hands on. As such, the API is rather general (and
|
||||
* therefore hopefully easy to comprehend!), and does its best to hide the
|
||||
* technical details that required to operate the hardware.
|
||||
*
|
||||
* This documentation is not aimed at developers wishing to develop and
|
||||
* contribute fingerprint device drivers to libfprint.
|
||||
*
|
||||
* Feedback on this API and its associated documentation is appreciated. Was
|
||||
* anything unclear? Does anything seem unreasonably complicated? Is anything
|
||||
* missing? Let us know on the
|
||||
* <a href="http://www.reactivated.net/fprint/Mailing_list">mailing list</a>.
|
||||
*
|
||||
* \section enrollment Enrollment
|
||||
*
|
||||
* Before you dive into the API, it's worth introducing a couple of concepts.
|
||||
*
|
||||
* The process of enrolling a finger is where you effectively scan your
|
||||
* finger for the purposes of teaching the system what your finger looks like.
|
||||
* This means that you scan your fingerprint, then the system processes it and
|
||||
* stores some data about your fingerprint to refer to later.
|
||||
*
|
||||
* \section verification Verification
|
||||
*
|
||||
* Verification is what most people think of when they think about fingerprint
|
||||
* scanning. The process of verification is effectively performing a fresh
|
||||
* fingerprint scan, and then comparing that scan to a finger that was
|
||||
* previously enrolled.
|
||||
*
|
||||
* As an example scenario, verification can be used to implement what people
|
||||
* would picture as fingerprint login (i.e. fingerprint replaces password).
|
||||
* For example:
|
||||
* - I enroll my fingerprint through some software that trusts I am who I say
|
||||
* I am. This is a prerequisite before I can perform fingerprint-based
|
||||
* login for my account.
|
||||
* - Some time later, I want to login to my computer. I enter my username,
|
||||
* but instead of prompting me for a password, it asks me to scan my finger.
|
||||
* I scan my finger.
|
||||
* - The system compares the finger I just scanned to the one that was
|
||||
* enrolled earlier. If the system decides that the fingerprints match,
|
||||
* I am successfully logged in. Otherwise, the system informs me that I am
|
||||
* not authorised to login as that user.
|
||||
*
|
||||
* \section identification Identification
|
||||
*
|
||||
* Identification is the process of comparing a freshly scanned fingerprint
|
||||
* to a <em>collection</em> of previously enrolled fingerprints. For example,
|
||||
* imagine there are 100 people in an organisation, and they all have enrolled
|
||||
* their fingerprints. One user walks up to a fingerprint scanner and scans
|
||||
* their finger. With <em>no other knowledge</em> of who that user might be,
|
||||
* the system examines their fingerprint, looks in the database, and determines
|
||||
* that the user is user number #61.
|
||||
*
|
||||
* In other words, verification might be seen as a one-to-one fingerprint
|
||||
* comparison where you know the identity of the user that you wish to
|
||||
* authenticate, whereas identification is a one-to-many comparison where you
|
||||
* do not know the identity of the user that you wish to authenticate.
|
||||
*
|
||||
* \section compat_general Device and print compatibility
|
||||
* Moving off generic conceptual ideas and onto libfprint-specific
|
||||
* implementation details, here are some introductory notes regarding how
|
||||
* libfprint copes with compatibility of fingerprints.
|
||||
*
|
||||
* libfprint deals with a whole variety of different fingerprint readers and
|
||||
* the design includes considerations of compatibility and interoperability
|
||||
* between multiple devices. Your application should also be prepared to
|
||||
* work with more than one type of fingerprint reader and should consider that
|
||||
* enrolled fingerprint X may not be compatible with the device the user has
|
||||
* plugged in today.
|
||||
*
|
||||
* libfprint implements the principle that fingerprints from different devices
|
||||
* are not necessarily compatible. For example, different devices may see
|
||||
* significantly different areas of fingerprint surface, and comparing images
|
||||
* between the devices would be unreliable. Also, devices can stretch and
|
||||
* distort images in different ways.
|
||||
*
|
||||
* libfprint also implements the principle that in some cases, fingerprints
|
||||
* <em>are</em> compatible between different devices. If you go and buy two
|
||||
* identical fingerprint readers, it seems logical that you should be able
|
||||
* to enroll on one and verify on another without problems.
|
||||
*
|
||||
* libfprint takes a fairly simplistic approach to these issues. Internally,
|
||||
* fingerprint hardware is driven by individual drivers. libfprint enforces
|
||||
* that a fingerprint that came from a device backed by driver X is never
|
||||
* compared to a fingerprint that came from a device backed by driver Y.
|
||||
*
|
||||
* Additionally, libfprint is designed for the situation where a single driver
|
||||
* may support a range of devices which differ in imaging or scanning
|
||||
* properties. For example, a driver may support two ranges of devices which
|
||||
* even though are programmed over the same interface, one device sees
|
||||
* substantially less of the finger flesh, therefore images from the two
|
||||
* device types should be incompatible despite being from the same driver. To
|
||||
* implement this, each driver assigns a <em>device type</em> to each device
|
||||
* that it detects based on its imaging characteristics. libfprint ensures that
|
||||
* two prints being compared have the same device type.
|
||||
*
|
||||
* In summary, libfprint represents fingerprints in several internal structures
|
||||
* and each representation will offer you a way of determining the
|
||||
* \ref driver_id "driver ID" and \ref devtype "devtype" of the print in
|
||||
* question. Prints are only compatible if the driver ID <b>and</b> devtypes
|
||||
* match. libfprint does offer you some "is this print compatible?" helper
|
||||
* functions, so you don't have to worry about these details too much.
|
||||
*
|
||||
* \section sync Synchronity/asynchronity
|
||||
*
|
||||
* Currently, all data acquisition operations are synchronous and can
|
||||
* potentially block for extended periods of time. For example, the enroll
|
||||
* function will block for an unpredictable amount of time until the user
|
||||
* scans their finger.
|
||||
*
|
||||
* Alternative asynchronous/non-blocking functionality will be offered in
|
||||
* future but has not been implemented yet.
|
||||
*
|
||||
* \section getting_started Getting started
|
||||
*
|
||||
* libfprint includes several simple functional examples under the examples/
|
||||
* directory in the libfprint source distribution. Those are good starting
|
||||
* points.
|
||||
*
|
||||
* Usually the first thing you want to do is determine which fingerprint
|
||||
* devices are present. This is done through \ref dscv_dev "device discovery".
|
||||
*
|
||||
* Once you have found a device you would like to operate, you should open it.
|
||||
* Refer to \ref dev "device operations". This section also details enrollment,
|
||||
* image capture, and verification.
|
||||
*
|
||||
*
|
||||
* That should be enough to get you started, but do remember there are
|
||||
* documentation pages on other aspects of libfprint's API (see the modules
|
||||
* page).
|
||||
*/
|
||||
|
||||
/** @defgroup core Core library operations */
|
||||
|
||||
/**
|
||||
* @defgroup dev Device operations
|
||||
* In order to interact with fingerprint scanners, your software will
|
||||
* interface primarily with libfprint's representation of devices, detailed
|
||||
* on this page.
|
||||
*
|
||||
* \section enrolling Enrolling
|
||||
* Enrolling is represented within libfprint as a multi-stage process. This
|
||||
* slightly complicates things for application developers, but is required
|
||||
* for a smooth process.
|
||||
*
|
||||
* Some devices require the user to scan their finger multiple times in
|
||||
* order to complete the enrollment process. libfprint must return control
|
||||
* to your application inbetween each scan in order for your application to
|
||||
* instruct the user to swipe their finger again. Each scan is referred to
|
||||
* as a stage, so a device that requires 3 scans for enrollment corresponds
|
||||
* to you running 3 enrollment stages using libfprint.
|
||||
*
|
||||
* The fp_dev_get_nr_enroll_stages() function can be used to find out how
|
||||
* many enroll stages are needed.
|
||||
*
|
||||
* In order to complete an enroll stage, you call an enroll function such
|
||||
* as fp_enroll_finger(). The return of this function does not necessarily
|
||||
* indicate that a stage has completed though, as the user may not have
|
||||
* produced a good enough scan. Each stage may have to be retried several
|
||||
* times.
|
||||
*
|
||||
* The exact semantics of the enroll functions are described in the
|
||||
* fp_enroll_finger() documentation. You should pay careful attention to the
|
||||
* details.
|
||||
*
|
||||
* \section imaging Imaging
|
||||
* libfprint provides you with some ways to retrieve images of scanned
|
||||
* fingers, such as the fp_dev_img_capture() function, or some enroll/verify
|
||||
* function variants which provide images. You may wish to do something with
|
||||
* such images in your application.
|
||||
*
|
||||
* However, you must be aware that not all hardware supported by libfprint
|
||||
* operates like this. Most hardware does operate simply by sending
|
||||
* fingerprint images to the host computer for further processing, but some
|
||||
* devices do all fingerprint processing in hardware and do not present images
|
||||
* to the host computer.
|
||||
*
|
||||
* You can use fp_dev_supports_imaging() to see if image capture is possible
|
||||
* on a particular device. Your application must be able to cope with the
|
||||
* fact that libfprint does support regular operations (e.g. enrolling and
|
||||
* verification) on some devices which do not provide images.
|
||||
*
|
||||
* \section devtype Devtypes
|
||||
* Internally, the \ref drv "driver" behind a device assigns a 32-bit
|
||||
* <em>devtype</em> identifier to the device. This cannot be used as a unique
|
||||
* ID for a specific device as many devices under the same range may share
|
||||
* the same devtype. The devtype may even be 0 in all cases.
|
||||
*
|
||||
* The only reason you may be interested in retrieving the devtype for a
|
||||
* device is for the purpose of checking if some print data is compatible
|
||||
* with a device. libfprint uses the devtype as one way of checking that the
|
||||
* print you are verifying is compatible with the device in question - the
|
||||
* devtypes must be equal. This effectively allows drivers to support more
|
||||
* than one type of device where the data from each one is not compatible with
|
||||
* the other. Note that libfprint does provide you with helper functions to
|
||||
* determine whether a print is compatible with a device, so under most
|
||||
* circumstances, you don't have to worry about devtypes at all.
|
||||
*/
|
||||
|
||||
/** @defgroup dscv_dev Device discovery
|
||||
* These functions allow you to scan the system for supported fingerprint
|
||||
* scanning hardware. This is your starting point when integrating libfprint
|
||||
* into your software.
|
||||
*
|
||||
* When you've identified a discovered device that you would like to control,
|
||||
* you can open it with fp_dev_open(). Note that discovered devices may no
|
||||
* longer be available at the time when you want to open them, for example
|
||||
* the user may have unplugged the device.
|
||||
*/
|
||||
|
||||
/** @defgroup drv Driver operations
|
||||
* Internally, libfprint is abstracted into various drivers to communicate
|
||||
* with the different types of supported fingerprint readers. libfprint works
|
||||
* hard so that you don't have to care about these internal abstractions,
|
||||
* however there are some situations where you may be interested in a little
|
||||
* behind-the-scenes driver info.
|
||||
*
|
||||
* You can obtain the driver for a device using fp_dev_get_driver(), which
|
||||
* you can pass to the functions documented on this page.
|
||||
*
|
||||
* \section driver_id Driver IDs
|
||||
* Each driver is assigned a unique ID by the project maintainer. These
|
||||
* assignments are
|
||||
* <a href="http://www.reactivated.net/fprint/Driver_ID_assignments">
|
||||
* documented on the wiki</a> and will never change.
|
||||
*
|
||||
* The only reason you may be interested in retrieving the driver ID for a
|
||||
* driver is for the purpose of checking if some print data is compatible
|
||||
* with a device. libfprint uses the driver ID as one way of checking that
|
||||
* the print you are trying to verify is compatible with the device in
|
||||
* question - it ensures that enrollment data from one driver is never fed to
|
||||
* another. Note that libfprint does provide you with helper functions to
|
||||
* determine whether a print is compatible with a device, so under most
|
||||
* circumstances, you don't have to worry about driver IDs at all.
|
||||
*/
|
||||
|
||||
static GSList *registered_drivers = NULL;
|
||||
|
||||
void fpi_log(enum fpi_log_level level, const char *component,
|
||||
const char *function, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
FILE *stream = stdout;
|
||||
const char *prefix;
|
||||
|
||||
#ifndef ENABLE_DEBUG_LOGGING
|
||||
if (!log_level)
|
||||
return;
|
||||
if (level == FPRINT_LOG_LEVEL_WARNING && log_level < 2)
|
||||
return;
|
||||
if (level == FPRINT_LOG_LEVEL_INFO && log_level < 3)
|
||||
return;
|
||||
#endif
|
||||
|
||||
switch (level) {
|
||||
case FPRINT_LOG_LEVEL_INFO:
|
||||
prefix = "info";
|
||||
break;
|
||||
case FPRINT_LOG_LEVEL_WARNING:
|
||||
stream = stderr;
|
||||
prefix = "warning";
|
||||
break;
|
||||
case FPRINT_LOG_LEVEL_ERROR:
|
||||
stream = stderr;
|
||||
prefix = "error";
|
||||
break;
|
||||
case FPRINT_LOG_LEVEL_DEBUG:
|
||||
stream = stderr;
|
||||
prefix = "debug";
|
||||
break;
|
||||
default:
|
||||
stream = stderr;
|
||||
prefix = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stream, "%s:%s [%s] ", component ? component : "fp", prefix,
|
||||
function);
|
||||
|
||||
va_start (args, format);
|
||||
vfprintf(stream, format, args);
|
||||
va_end (args);
|
||||
|
||||
fprintf(stream, "\n");
|
||||
}
|
||||
|
||||
static void register_driver(struct fp_driver *drv)
|
||||
{
|
||||
if (drv->id == 0) {
|
||||
fp_err("not registering driver %s: driver ID is 0", drv->name);
|
||||
return;
|
||||
}
|
||||
registered_drivers = g_slist_prepend(registered_drivers, (gpointer) drv);
|
||||
fp_dbg("registered driver %s", drv->name);
|
||||
}
|
||||
|
||||
static struct fp_driver * const primitive_drivers[] = {
|
||||
#ifdef ENABLE_UPEKTS
|
||||
&upekts_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_UPEKE2
|
||||
&upeke2_driver,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct fp_img_driver * const img_drivers[] = {
|
||||
#ifdef ENABLE_AES3500
|
||||
&aes3500_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_AES4000
|
||||
&aes4000_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_AES2501
|
||||
&aes2501_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_AES2550
|
||||
&aes2550_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_URU4000
|
||||
&uru4000_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_VCOM5S
|
||||
&vcom5s_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_UPEKSONLY
|
||||
&upeksonly_driver,
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AES1610
|
||||
&aes1610_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_AES1660
|
||||
&aes1660_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_AES2660
|
||||
&aes2660_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_VFS101
|
||||
&vfs101_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_VFS301
|
||||
&vfs301_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_VFS5011
|
||||
&vfs5011_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_UPEKTC
|
||||
&upektc_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_UPEKTC_IMG
|
||||
&upektc_img_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_ETES603
|
||||
&etes603_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_VFS0050
|
||||
&vfs0050_driver,
|
||||
#endif
|
||||
/*#ifdef ENABLE_FDU2000
|
||||
&fdu2000_driver,
|
||||
#endif
|
||||
*/
|
||||
};
|
||||
|
||||
static void register_drivers(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++)
|
||||
register_driver(primitive_drivers[i]);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(img_drivers); i++) {
|
||||
struct fp_img_driver *imgdriver = img_drivers[i];
|
||||
fpi_img_driver_setup(imgdriver);
|
||||
register_driver(&imgdriver->driver);
|
||||
}
|
||||
}
|
||||
|
||||
API_EXPORTED struct fp_driver **fprint_get_drivers (void)
|
||||
{
|
||||
GPtrArray *array;
|
||||
unsigned int i;
|
||||
|
||||
array = g_ptr_array_new ();
|
||||
for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++)
|
||||
g_ptr_array_add (array, primitive_drivers[i]);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(img_drivers); i++)
|
||||
g_ptr_array_add (array, &(img_drivers[i]->driver));
|
||||
|
||||
/* Add a null item terminating the array */
|
||||
g_ptr_array_add (array, NULL);
|
||||
|
||||
return (struct fp_driver **) g_ptr_array_free (array, FALSE);
|
||||
}
|
||||
|
||||
static struct fp_driver *find_supporting_driver(libusb_device *udev,
|
||||
const struct usb_id **usb_id, uint32_t *devtype)
|
||||
{
|
||||
int ret;
|
||||
GSList *elem = registered_drivers;
|
||||
struct libusb_device_descriptor dsc;
|
||||
|
||||
const struct usb_id *best_usb_id;
|
||||
struct fp_driver *best_drv;
|
||||
uint32_t best_devtype;
|
||||
int drv_score = 0;
|
||||
|
||||
ret = libusb_get_device_descriptor(udev, &dsc);
|
||||
if (ret < 0) {
|
||||
fp_err("Failed to get device descriptor");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
best_drv = NULL;
|
||||
best_devtype = 0;
|
||||
|
||||
do {
|
||||
struct fp_driver *drv = elem->data;
|
||||
uint32_t type = 0;
|
||||
const struct usb_id *id;
|
||||
|
||||
for (id = drv->id_table; id->vendor; id++) {
|
||||
if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) {
|
||||
if (drv->discover) {
|
||||
int r = drv->discover(&dsc, &type);
|
||||
if (r < 0)
|
||||
fp_err("%s discover failed, code %d", drv->name, r);
|
||||
if (r <= 0)
|
||||
continue;
|
||||
/* Has a discover function, and matched our device */
|
||||
drv_score = 100;
|
||||
} else {
|
||||
/* Already got a driver as good */
|
||||
if (drv_score >= 50)
|
||||
continue;
|
||||
drv_score = 50;
|
||||
}
|
||||
fp_dbg("driver %s supports USB device %04x:%04x",
|
||||
drv->name, id->vendor, id->product);
|
||||
best_usb_id = id;
|
||||
best_drv = drv;
|
||||
best_devtype = type;
|
||||
|
||||
/* We found the best possible driver */
|
||||
if (drv_score == 100)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while ((elem = g_slist_next(elem)));
|
||||
|
||||
if (best_drv != NULL) {
|
||||
fp_dbg("selected driver %s supports USB device %04x:%04x",
|
||||
best_drv->name, dsc.idVendor, dsc.idProduct);
|
||||
*devtype = best_devtype;
|
||||
*usb_id = best_usb_id;
|
||||
}
|
||||
|
||||
return best_drv;
|
||||
}
|
||||
|
||||
static struct fp_dscv_dev *discover_dev(libusb_device *udev)
|
||||
{
|
||||
const struct usb_id *usb_id;
|
||||
struct fp_driver *drv;
|
||||
struct fp_dscv_dev *ddev;
|
||||
uint32_t devtype;
|
||||
|
||||
drv = find_supporting_driver(udev, &usb_id, &devtype);
|
||||
|
||||
if (!drv)
|
||||
return NULL;
|
||||
|
||||
ddev = g_malloc0(sizeof(*ddev));
|
||||
ddev->drv = drv;
|
||||
ddev->udev = udev;
|
||||
ddev->driver_data = usb_id->driver_data;
|
||||
ddev->devtype = devtype;
|
||||
return ddev;
|
||||
}
|
||||
|
||||
/** \ingroup dscv_dev
|
||||
* Scans the system and returns a list of discovered devices. This is your
|
||||
* entry point into finding a fingerprint reader to operate.
|
||||
* \returns a NULL-terminated list of discovered devices. Must be freed with
|
||||
* fp_dscv_devs_free() after use.
|
||||
*/
|
||||
API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void)
|
||||
{
|
||||
GSList *tmplist = NULL;
|
||||
struct fp_dscv_dev **list;
|
||||
libusb_device *udev;
|
||||
libusb_device **devs;
|
||||
int dscv_count = 0;
|
||||
int r;
|
||||
int i = 0;
|
||||
|
||||
if (registered_drivers == NULL)
|
||||
return NULL;
|
||||
|
||||
r = libusb_get_device_list(fpi_usb_ctx, &devs);
|
||||
if (r < 0) {
|
||||
fp_err("couldn't enumerate USB devices, error %d", r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check each device against each driver, temporarily storing successfully
|
||||
* discovered devices in a GSList.
|
||||
*
|
||||
* Quite inefficient but excusable as we'll only be dealing with small
|
||||
* sets of drivers against small sets of USB devices */
|
||||
while ((udev = devs[i++]) != NULL) {
|
||||
struct fp_dscv_dev *ddev = discover_dev(udev);
|
||||
if (!ddev)
|
||||
continue;
|
||||
tmplist = g_slist_prepend(tmplist, (gpointer) ddev);
|
||||
dscv_count++;
|
||||
}
|
||||
|
||||
/* Convert our temporary GSList into a standard NULL-terminated pointer
|
||||
* array. */
|
||||
list = g_malloc(sizeof(*list) * (dscv_count + 1));
|
||||
if (dscv_count > 0) {
|
||||
GSList *elem = tmplist;
|
||||
i = 0;
|
||||
do {
|
||||
list[i++] = elem->data;
|
||||
} while ((elem = g_slist_next(elem)));
|
||||
}
|
||||
list[dscv_count] = NULL; /* NULL-terminate */
|
||||
|
||||
g_slist_free(tmplist);
|
||||
return list;
|
||||
}
|
||||
|
||||
/** \ingroup dscv_dev
|
||||
* Free a list of discovered devices. This function destroys the list and all
|
||||
* discovered devices that it included, so make sure you have opened your
|
||||
* discovered device <b>before</b> freeing the list.
|
||||
* \param devs the list of discovered devices. If NULL, function simply
|
||||
* returns.
|
||||
*/
|
||||
API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs)
|
||||
{
|
||||
int i;
|
||||
if (!devs)
|
||||
return;
|
||||
|
||||
for (i = 0; devs[i]; i++)
|
||||
g_free(devs[i]);
|
||||
g_free(devs);
|
||||
}
|
||||
|
||||
/** \ingroup dscv_dev
|
||||
* Gets the \ref drv "driver" for a discovered device.
|
||||
* \param dev the discovered device
|
||||
* \returns the driver backing the device
|
||||
*/
|
||||
API_EXPORTED struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev)
|
||||
{
|
||||
return dev->drv;
|
||||
}
|
||||
|
||||
/** \ingroup dscv_dev
|
||||
* Gets the \ref devtype "devtype" for a discovered device.
|
||||
* \param dev the discovered device
|
||||
* \returns the devtype of the device
|
||||
*/
|
||||
API_EXPORTED uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev)
|
||||
{
|
||||
return dev->devtype;
|
||||
}
|
||||
|
||||
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv)
|
||||
{
|
||||
switch (drv->type) {
|
||||
case DRIVER_PRIMITIVE:
|
||||
return PRINT_DATA_RAW;
|
||||
case DRIVER_IMAGING:
|
||||
return PRINT_DATA_NBIS_MINUTIAE;
|
||||
default:
|
||||
fp_err("unrecognised drv type %d", drv->type);
|
||||
return PRINT_DATA_RAW;
|
||||
}
|
||||
}
|
||||
|
||||
/** \ingroup dscv_dev
|
||||
* Determines if a specific \ref print_data "stored print" appears to be
|
||||
* compatible with a discovered device.
|
||||
* \param dev the discovered device
|
||||
* \param data the print for compatibility checking
|
||||
* \returns 1 if the print is compatible with the device, 0 otherwise
|
||||
*/
|
||||
API_EXPORTED int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev,
|
||||
struct fp_print_data *data)
|
||||
{
|
||||
return fpi_print_data_compatible(dev->drv->id, dev->devtype,
|
||||
fpi_driver_get_data_type(dev->drv), data->driver_id, data->devtype,
|
||||
data->type);
|
||||
}
|
||||
|
||||
/** \ingroup dscv_dev
|
||||
* Determines if a specific \ref dscv_print "discovered print" appears to be
|
||||
* compatible with a discovered device.
|
||||
* \param dev the discovered device
|
||||
* \param data the discovered print for compatibility checking
|
||||
* \returns 1 if the print is compatible with the device, 0 otherwise
|
||||
*/
|
||||
API_EXPORTED int fp_dscv_dev_supports_dscv_print(struct fp_dscv_dev *dev,
|
||||
struct fp_dscv_print *data)
|
||||
{
|
||||
return fpi_print_data_compatible(dev->drv->id, dev->devtype, 0,
|
||||
data->driver_id, data->devtype, 0);
|
||||
}
|
||||
|
||||
/** \ingroup dscv_dev
|
||||
* Searches a list of discovered devices for a device that appears to be
|
||||
* compatible with a \ref print_data "stored print".
|
||||
* \param devs a list of discovered devices
|
||||
* \param data the print under inspection
|
||||
* \returns the first discovered device that appears to support the print, or
|
||||
* NULL if no apparently compatible devices could be found
|
||||
*/
|
||||
API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_print_data(struct fp_dscv_dev **devs,
|
||||
struct fp_print_data *data)
|
||||
{
|
||||
struct fp_dscv_dev *ddev;
|
||||
int i;
|
||||
|
||||
for (i = 0; (ddev = devs[i]); i++)
|
||||
if (fp_dscv_dev_supports_print_data(ddev, data))
|
||||
return ddev;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** \ingroup dscv_dev
|
||||
* Searches a list of discovered devices for a device that appears to be
|
||||
* compatible with a \ref dscv_print "discovered print".
|
||||
* \param devs a list of discovered devices
|
||||
* \param print the print under inspection
|
||||
* \returns the first discovered device that appears to support the print, or
|
||||
* NULL if no apparently compatible devices could be found
|
||||
*/
|
||||
API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_dscv_print(struct fp_dscv_dev **devs,
|
||||
struct fp_dscv_print *print)
|
||||
{
|
||||
struct fp_dscv_dev *ddev;
|
||||
int i;
|
||||
|
||||
for (i = 0; (ddev = devs[i]); i++)
|
||||
if (fp_dscv_dev_supports_dscv_print(ddev, print))
|
||||
return ddev;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Get the \ref drv "driver" for a fingerprint device.
|
||||
* \param dev the device
|
||||
* \returns the driver controlling the device
|
||||
*/
|
||||
API_EXPORTED struct fp_driver *fp_dev_get_driver(struct fp_dev *dev)
|
||||
{
|
||||
return dev->drv;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Gets the number of \ref enrolling "enroll stages" required to enroll a
|
||||
* fingerprint with the device.
|
||||
* \param dev the device
|
||||
* \returns the number of enroll stages
|
||||
*/
|
||||
API_EXPORTED int fp_dev_get_nr_enroll_stages(struct fp_dev *dev)
|
||||
{
|
||||
return dev->nr_enroll_stages;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Gets the \ref devtype "devtype" for a device.
|
||||
* \param dev the device
|
||||
* \returns the devtype
|
||||
*/
|
||||
API_EXPORTED uint32_t fp_dev_get_devtype(struct fp_dev *dev)
|
||||
{
|
||||
return dev->devtype;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Determines if a stored print is compatible with a certain device.
|
||||
* \param dev the device
|
||||
* \param data the stored print
|
||||
* \returns 1 if the print is compatible with the device, 0 if not
|
||||
*/
|
||||
API_EXPORTED int fp_dev_supports_print_data(struct fp_dev *dev,
|
||||
struct fp_print_data *data)
|
||||
{
|
||||
return fpi_print_data_compatible(dev->drv->id, dev->devtype,
|
||||
fpi_driver_get_data_type(dev->drv), data->driver_id, data->devtype,
|
||||
data->type);
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Determines if a \ref dscv_print "discovered print" appears to be compatible
|
||||
* with a certain device.
|
||||
* \param dev the device
|
||||
* \param data the discovered print
|
||||
* \returns 1 if the print is compatible with the device, 0 if not
|
||||
*/
|
||||
API_EXPORTED int fp_dev_supports_dscv_print(struct fp_dev *dev,
|
||||
struct fp_dscv_print *data)
|
||||
{
|
||||
return fpi_print_data_compatible(dev->drv->id, dev->devtype,
|
||||
0, data->driver_id, data->devtype, 0);
|
||||
}
|
||||
|
||||
/** \ingroup drv
|
||||
* Retrieves the name of the driver. For example: "upekts"
|
||||
* \param drv the driver
|
||||
* \returns the driver name. Must not be modified or freed.
|
||||
*/
|
||||
API_EXPORTED const char *fp_driver_get_name(struct fp_driver *drv)
|
||||
{
|
||||
return drv->name;
|
||||
}
|
||||
|
||||
/** \ingroup drv
|
||||
* Retrieves a descriptive name of the driver. For example: "UPEK TouchStrip"
|
||||
* \param drv the driver
|
||||
* \returns the descriptive name. Must not be modified or freed.
|
||||
*/
|
||||
API_EXPORTED const char *fp_driver_get_full_name(struct fp_driver *drv)
|
||||
{
|
||||
return drv->full_name;
|
||||
}
|
||||
|
||||
/** \ingroup drv
|
||||
* Retrieves the driver ID code for a driver.
|
||||
* \param drv the driver
|
||||
* \returns the driver ID
|
||||
*/
|
||||
API_EXPORTED uint16_t fp_driver_get_driver_id(struct fp_driver *drv)
|
||||
{
|
||||
return drv->id;
|
||||
}
|
||||
|
||||
/** \ingroup drv
|
||||
* Retrieves the scan type for the devices associated with the driver.
|
||||
* \param drv the driver
|
||||
* \returns the scan type
|
||||
*/
|
||||
API_EXPORTED enum fp_scan_type fp_driver_get_scan_type(struct fp_driver *drv)
|
||||
{
|
||||
return drv->scan_type;
|
||||
}
|
||||
|
||||
static struct fp_img_dev *dev_to_img_dev(struct fp_dev *dev)
|
||||
{
|
||||
if (dev->drv->type != DRIVER_IMAGING)
|
||||
return NULL;
|
||||
return dev->priv;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Determines if a device has imaging capabilities. If a device has imaging
|
||||
* capabilities you are able to perform imaging operations such as retrieving
|
||||
* scan images using fp_dev_img_capture(). However, not all devices are
|
||||
* imaging devices - some do all processing in hardware. This function will
|
||||
* indicate which class a device in question falls into.
|
||||
* \param dev the fingerprint device
|
||||
* \returns 1 if the device is an imaging device, 0 if the device does not
|
||||
* provide images to the host computer
|
||||
*/
|
||||
API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev)
|
||||
{
|
||||
return dev->drv->capture_start != NULL;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Determines if a device is capable of \ref identification "identification"
|
||||
* through fp_identify_finger() and similar. Not all devices support this
|
||||
* functionality.
|
||||
* \param dev the fingerprint device
|
||||
* \returns 1 if the device is capable of identification, 0 otherwise.
|
||||
*/
|
||||
API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
|
||||
{
|
||||
return dev->drv->identify_start != NULL;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Gets the expected width of images that will be captured from the device.
|
||||
* This function will return -1 for devices that are not
|
||||
* \ref imaging "imaging devices". If the width of images from this device
|
||||
* can vary, 0 will be returned.
|
||||
* \param dev the device
|
||||
* \returns the expected image width, or 0 for variable, or -1 for non-imaging
|
||||
* devices.
|
||||
*/
|
||||
API_EXPORTED int fp_dev_get_img_width(struct fp_dev *dev)
|
||||
{
|
||||
struct fp_img_dev *imgdev = dev_to_img_dev(dev);
|
||||
if (!imgdev) {
|
||||
fp_dbg("get image width for non-imaging device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fpi_imgdev_get_img_width(imgdev);
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Gets the expected height of images that will be captured from the device.
|
||||
* This function will return -1 for devices that are not
|
||||
* \ref imaging "imaging devices". If the height of images from this device
|
||||
* can vary, 0 will be returned.
|
||||
* \param dev the device
|
||||
* \returns the expected image height, or 0 for variable, or -1 for non-imaging
|
||||
* devices.
|
||||
*/
|
||||
API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev)
|
||||
{
|
||||
struct fp_img_dev *imgdev = dev_to_img_dev(dev);
|
||||
if (!imgdev) {
|
||||
fp_dbg("get image height for non-imaging device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fpi_imgdev_get_img_height(imgdev);
|
||||
}
|
||||
|
||||
/** \ingroup core
|
||||
* Set message verbosity.
|
||||
* - Level 0: no messages ever printed by the library (default)
|
||||
* - Level 1: error messages are printed to stderr
|
||||
* - Level 2: warning and error messages are printed to stderr
|
||||
* - Level 3: informational messages are printed to stdout, warning and error
|
||||
* messages are printed to stderr
|
||||
*
|
||||
* The default level is 0, which means no messages are ever printed. If you
|
||||
* choose to increase the message verbosity level, ensure that your
|
||||
* application does not close the stdout/stderr file descriptors.
|
||||
*
|
||||
* You are advised to set level 3. libfprint is conservative with its message
|
||||
* logging and most of the time, will only log messages that explain error
|
||||
* conditions and other oddities. This will help you debug your software.
|
||||
*
|
||||
* If the LIBFPRINT_DEBUG environment variable was set when libfprint was
|
||||
* initialized, this function does nothing: the message verbosity is fixed
|
||||
* to the value in the environment variable.
|
||||
*
|
||||
* If libfprint was compiled without any message logging, this function does
|
||||
* nothing: you'll never get any messages.
|
||||
*
|
||||
* If libfprint was compiled with verbose debug message logging, this function
|
||||
* does nothing: you'll always get messages from all levels.
|
||||
*
|
||||
* \param ctx the context to operate on, or NULL for the default context
|
||||
* \param level debug level to set
|
||||
*/
|
||||
API_EXPORTED void fp_set_debug(int level)
|
||||
{
|
||||
if (log_level_fixed)
|
||||
return;
|
||||
|
||||
log_level = level;
|
||||
libusb_set_debug(fpi_usb_ctx, level);
|
||||
}
|
||||
|
||||
/** \ingroup core
|
||||
* Initialise libfprint. This function must be called before you attempt to
|
||||
* use the library in any way.
|
||||
* \return 0 on success, non-zero on error.
|
||||
*/
|
||||
API_EXPORTED int fp_init(void)
|
||||
{
|
||||
char *dbg = getenv("LIBFPRINT_DEBUG");
|
||||
int r;
|
||||
fp_dbg("");
|
||||
|
||||
r = libusb_init(&fpi_usb_ctx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (dbg) {
|
||||
log_level = atoi(dbg);
|
||||
if (log_level) {
|
||||
log_level_fixed = 1;
|
||||
libusb_set_debug(fpi_usb_ctx, log_level);
|
||||
}
|
||||
}
|
||||
|
||||
register_drivers();
|
||||
fpi_poll_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** \ingroup core
|
||||
* Deinitialise libfprint. This function should be called during your program
|
||||
* exit sequence. You must not use any libfprint functions after calling this
|
||||
* function, unless you call fp_init() again.
|
||||
*/
|
||||
API_EXPORTED void fp_exit(void)
|
||||
{
|
||||
fp_dbg("");
|
||||
|
||||
if (opened_devices) {
|
||||
GSList *copy = g_slist_copy(opened_devices);
|
||||
GSList *elem = copy;
|
||||
fp_dbg("naughty app left devices open on exit!");
|
||||
|
||||
do
|
||||
fp_dev_close((struct fp_dev *) elem->data);
|
||||
while ((elem = g_slist_next(elem)));
|
||||
|
||||
g_slist_free(copy);
|
||||
g_slist_free(opened_devices);
|
||||
opened_devices = NULL;
|
||||
}
|
||||
|
||||
fpi_data_exit();
|
||||
fpi_poll_exit();
|
||||
g_slist_free(registered_drivers);
|
||||
registered_drivers = NULL;
|
||||
libusb_exit(fpi_usb_ctx);
|
||||
}
|
||||
|
783
libfprint/data.c
|
@ -1,783 +0,0 @@
|
|||
/*
|
||||
* Fingerprint data handling and storage
|
||||
* 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 <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
|
||||
#define DIR_PERMS 0700
|
||||
|
||||
/** @defgroup print_data Stored prints
|
||||
* Stored prints are represented by a structure named <tt>fp_print_data</tt>.
|
||||
* Stored prints are originally obtained from an enrollment function such as
|
||||
* fp_enroll_finger().
|
||||
*
|
||||
* This page documents the various operations you can do with a stored print.
|
||||
* Note that by default, "stored prints" are not actually stored anywhere
|
||||
* except in RAM. For the simple scenarios, libfprint provides a simple API
|
||||
* for you to save and load the stored prints referring to a single user in
|
||||
* their home directory. For more advanced users, libfprint provides APIs for
|
||||
* you to convert print data to a byte string, and to reconstruct stored prints
|
||||
* from such data at a later point. You are welcome to store these byte strings
|
||||
* in any fashion that suits you.
|
||||
*/
|
||||
|
||||
static char *base_store = NULL;
|
||||
|
||||
static void storage_setup(void)
|
||||
{
|
||||
const char *homedir;
|
||||
|
||||
homedir = g_getenv("HOME");
|
||||
if (!homedir)
|
||||
homedir = g_get_home_dir();
|
||||
if (!homedir)
|
||||
return;
|
||||
|
||||
base_store = g_build_filename(homedir, ".fprint/prints", NULL);
|
||||
g_mkdir_with_parents(base_store, DIR_PERMS);
|
||||
/* FIXME handle failure */
|
||||
}
|
||||
|
||||
void fpi_data_exit(void)
|
||||
{
|
||||
g_free(base_store);
|
||||
}
|
||||
|
||||
#define FP_FINGER_IS_VALID(finger) \
|
||||
((finger) >= LEFT_THUMB && (finger) <= RIGHT_LITTLE)
|
||||
|
||||
/* for debug messages only */
|
||||
#ifdef ENABLE_DEBUG_LOGGING
|
||||
static const char *finger_num_to_str(enum fp_finger finger)
|
||||
{
|
||||
const char *names[] = {
|
||||
[LEFT_THUMB] = "left thumb",
|
||||
[LEFT_INDEX] = "left index",
|
||||
[LEFT_MIDDLE] = "left middle",
|
||||
[LEFT_RING] = "left ring",
|
||||
[LEFT_LITTLE] = "left little",
|
||||
[RIGHT_THUMB] = "right thumb",
|
||||
[RIGHT_INDEX] = "right index",
|
||||
[RIGHT_MIDDLE] = "right middle",
|
||||
[RIGHT_RING] = "right ring",
|
||||
[RIGHT_LITTLE] = "right little",
|
||||
};
|
||||
if (!FP_FINGER_IS_VALID(finger))
|
||||
return "UNKNOWN";
|
||||
return names[finger];
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct fp_print_data *print_data_new(uint16_t driver_id,
|
||||
uint32_t devtype, enum fp_print_data_type type)
|
||||
{
|
||||
struct fp_print_data *data = g_malloc0(sizeof(*data));
|
||||
fp_dbg("driver=%02x devtype=%04x", driver_id, devtype);
|
||||
data->driver_id = driver_id;
|
||||
data->devtype = devtype;
|
||||
data->type = type;
|
||||
return data;
|
||||
}
|
||||
|
||||
void fpi_print_data_item_free(struct fp_print_data_item *item)
|
||||
{
|
||||
g_free(item);
|
||||
}
|
||||
|
||||
struct fp_print_data_item *fpi_print_data_item_new(size_t length)
|
||||
{
|
||||
struct fp_print_data_item *item = g_malloc(sizeof(*item) + length);
|
||||
item->length = length;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev)
|
||||
{
|
||||
return print_data_new(dev->drv->id, dev->devtype,
|
||||
fpi_driver_get_data_type(dev->drv));
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Convert a stored print into a unified representation inside a data buffer.
|
||||
* You can then store this data buffer in any way that suits you, and load
|
||||
* it back at some later time using fp_print_data_from_data().
|
||||
* \param data the stored print
|
||||
* \param ret output location for the data buffer. Must be freed with free()
|
||||
* after use.
|
||||
* \returns the size of the freshly allocated buffer, or 0 on error.
|
||||
*/
|
||||
API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
|
||||
unsigned char **ret)
|
||||
{
|
||||
struct fpi_print_data_fp2 *out_data;
|
||||
struct fpi_print_data_item_fp2 *out_item;
|
||||
struct fp_print_data_item *item;
|
||||
size_t buflen = 0;
|
||||
GSList *list_item;
|
||||
unsigned char *buf;
|
||||
|
||||
fp_dbg("");
|
||||
|
||||
list_item = data->prints;
|
||||
while (list_item) {
|
||||
item = list_item->data;
|
||||
buflen += sizeof(*out_item);
|
||||
buflen += item->length;
|
||||
list_item = g_slist_next(list_item);
|
||||
}
|
||||
|
||||
buflen += sizeof(*out_data);
|
||||
out_data = g_malloc(buflen);
|
||||
|
||||
*ret = (unsigned char *) out_data;
|
||||
buf = out_data->data;
|
||||
out_data->prefix[0] = 'F';
|
||||
out_data->prefix[1] = 'P';
|
||||
out_data->prefix[2] = '2';
|
||||
out_data->driver_id = GUINT16_TO_LE(data->driver_id);
|
||||
out_data->devtype = GUINT32_TO_LE(data->devtype);
|
||||
out_data->data_type = data->type;
|
||||
|
||||
list_item = data->prints;
|
||||
while (list_item) {
|
||||
item = list_item->data;
|
||||
out_item = (struct fpi_print_data_item_fp2 *)buf;
|
||||
out_item->length = GUINT32_TO_LE(item->length);
|
||||
/* FIXME: fp_print_data_item->data content is not endianess agnostic */
|
||||
memcpy(out_item->data, item->data, item->length);
|
||||
buf += sizeof(*out_item);
|
||||
buf += item->length;
|
||||
list_item = g_slist_next(list_item);
|
||||
}
|
||||
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static struct fp_print_data *fpi_print_data_from_fp1_data(unsigned char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
size_t print_data_len;
|
||||
struct fp_print_data *data;
|
||||
struct fp_print_data_item *item;
|
||||
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
|
||||
|
||||
print_data_len = buflen - sizeof(*raw);
|
||||
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
|
||||
GUINT32_FROM_LE(raw->devtype), raw->data_type);
|
||||
item = fpi_print_data_item_new(print_data_len);
|
||||
/* FIXME: fp_print_data->data content is not endianess agnostic */
|
||||
memcpy(item->data, raw->data, print_data_len);
|
||||
data->prints = g_slist_prepend(data->prints, item);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static struct fp_print_data *fpi_print_data_from_fp2_data(unsigned char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
size_t total_data_len, item_len;
|
||||
struct fp_print_data *data;
|
||||
struct fp_print_data_item *item;
|
||||
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
|
||||
unsigned char *raw_buf;
|
||||
struct fpi_print_data_item_fp2 *raw_item;
|
||||
|
||||
total_data_len = buflen - sizeof(*raw);
|
||||
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
|
||||
GUINT32_FROM_LE(raw->devtype), raw->data_type);
|
||||
raw_buf = raw->data;
|
||||
while (total_data_len) {
|
||||
if (total_data_len < sizeof(*raw_item))
|
||||
break;
|
||||
total_data_len -= sizeof(*raw_item);
|
||||
|
||||
raw_item = (struct fpi_print_data_item_fp2 *)raw_buf;
|
||||
item_len = GUINT32_FROM_LE(raw_item->length);
|
||||
fp_dbg("item len %d, total_data_len %d", item_len, total_data_len);
|
||||
if (total_data_len < item_len) {
|
||||
fp_err("corrupted fingerprint data");
|
||||
break;
|
||||
}
|
||||
total_data_len -= item_len;
|
||||
|
||||
item = fpi_print_data_item_new(item_len);
|
||||
/* FIXME: fp_print_data->data content is not endianess agnostic */
|
||||
memcpy(item->data, raw_item->data, item_len);
|
||||
data->prints = g_slist_prepend(data->prints, item);
|
||||
|
||||
raw_buf += sizeof(*raw_item);
|
||||
raw_buf += item_len;
|
||||
}
|
||||
|
||||
if (g_slist_length(data->prints) == 0) {
|
||||
fp_print_data_free(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Load a stored print from a data buffer. The contents of said buffer must
|
||||
* be the untouched contents of a buffer previously supplied to you by the
|
||||
* fp_print_data_get_data() function.
|
||||
* \param buf the data buffer
|
||||
* \param buflen the length of the buffer
|
||||
* \returns the stored print represented by the data, or NULL on error. Must
|
||||
* be freed with fp_print_data_free() after use.
|
||||
*/
|
||||
API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
|
||||
|
||||
fp_dbg("buffer size %zd", buflen);
|
||||
if (buflen < sizeof(*raw))
|
||||
return NULL;
|
||||
|
||||
if (strncmp(raw->prefix, "FP1", 3) == 0) {
|
||||
return fpi_print_data_from_fp1_data(buf, buflen);
|
||||
} else if (strncmp(raw->prefix, "FP2", 3) == 0) {
|
||||
return fpi_print_data_from_fp2_data(buf, buflen);
|
||||
} else {
|
||||
fp_dbg("bad header prefix");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *get_path_to_storedir(uint16_t driver_id, uint32_t devtype)
|
||||
{
|
||||
char idstr[5];
|
||||
char devtypestr[9];
|
||||
|
||||
g_snprintf(idstr, sizeof(idstr), "%04x", driver_id);
|
||||
g_snprintf(devtypestr, sizeof(devtypestr), "%08x", devtype);
|
||||
|
||||
return g_build_filename(base_store, idstr, devtypestr, NULL);
|
||||
}
|
||||
|
||||
static char *__get_path_to_print(uint16_t driver_id, uint32_t devtype,
|
||||
enum fp_finger finger)
|
||||
{
|
||||
char *dirpath;
|
||||
char *path;
|
||||
char fingername[2];
|
||||
|
||||
g_snprintf(fingername, 2, "%x", finger);
|
||||
|
||||
dirpath = get_path_to_storedir(driver_id, devtype);
|
||||
path = g_build_filename(dirpath, fingername, NULL);
|
||||
g_free(dirpath);
|
||||
return path;
|
||||
}
|
||||
|
||||
static char *get_path_to_print(struct fp_dev *dev, enum fp_finger finger)
|
||||
{
|
||||
return __get_path_to_print(dev->drv->id, dev->devtype, finger);
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Saves a stored print to disk, assigned to a specific finger. Even though
|
||||
* you are limited to storing only the 10 human fingers, this is a
|
||||
* per-device-type limit. For example, you can store the users right index
|
||||
* finger from a DigitalPersona scanner, and you can also save the right index
|
||||
* finger from a UPEK scanner. When you later come to load the print, the right
|
||||
* one will be automatically selected.
|
||||
*
|
||||
* This function will unconditionally overwrite a fingerprint previously
|
||||
* saved for the same finger and device type. The print is saved in a hidden
|
||||
* directory beneath the current user's home directory.
|
||||
* \param data the stored print to save to disk
|
||||
* \param finger the finger that this print corresponds to
|
||||
* \returns 0 on success, non-zero on error.
|
||||
*/
|
||||
API_EXPORTED int fp_print_data_save(struct fp_print_data *data,
|
||||
enum fp_finger finger)
|
||||
{
|
||||
GError *err = NULL;
|
||||
char *path;
|
||||
char *dirpath;
|
||||
unsigned char *buf;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
if (!base_store)
|
||||
storage_setup();
|
||||
|
||||
fp_dbg("save %s print from driver %04x", finger_num_to_str(finger),
|
||||
data->driver_id);
|
||||
len = fp_print_data_get_data(data, &buf);
|
||||
if (!len)
|
||||
return -ENOMEM;
|
||||
|
||||
path = __get_path_to_print(data->driver_id, data->devtype, finger);
|
||||
dirpath = g_path_get_dirname(path);
|
||||
r = g_mkdir_with_parents(dirpath, DIR_PERMS);
|
||||
if (r < 0) {
|
||||
fp_err("couldn't create storage directory");
|
||||
g_free(path);
|
||||
g_free(dirpath);
|
||||
return r;
|
||||
}
|
||||
|
||||
fp_dbg("saving to %s", path);
|
||||
g_file_set_contents(path, buf, len, &err);
|
||||
free(buf);
|
||||
g_free(dirpath);
|
||||
g_free(path);
|
||||
if (err) {
|
||||
r = err->code;
|
||||
fp_err("save failed: %s", err->message);
|
||||
g_error_free(err);
|
||||
/* FIXME interpret error codes */
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (driver_id1 != driver_id2) {
|
||||
fp_dbg("driver ID mismatch: %02x vs %02x", driver_id1, driver_id2);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (devtype1 != devtype2) {
|
||||
fp_dbg("devtype mismatch: %04x vs %04x", devtype1, devtype2);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (type1 != type2) {
|
||||
fp_dbg("type mismatch: %d vs %d", type1, type2);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int load_from_file(char *path, struct fp_print_data **data)
|
||||
{
|
||||
gsize length;
|
||||
gchar *contents;
|
||||
GError *err = NULL;
|
||||
struct fp_print_data *fdata;
|
||||
|
||||
fp_dbg("from %s", path);
|
||||
g_file_get_contents(path, &contents, &length, &err);
|
||||
if (err) {
|
||||
int r = err->code;
|
||||
fp_err("%s load failed: %s", path, err->message);
|
||||
g_error_free(err);
|
||||
/* FIXME interpret more error codes */
|
||||
if (r == G_FILE_ERROR_NOENT)
|
||||
return -ENOENT;
|
||||
else
|
||||
return r;
|
||||
}
|
||||
|
||||
fdata = fp_print_data_from_data(contents, length);
|
||||
g_free(contents);
|
||||
if (!fdata)
|
||||
return -EIO;
|
||||
*data = fdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Loads a previously stored print from disk. The print must have been saved
|
||||
* earlier using the fp_print_data_save() function.
|
||||
*
|
||||
* A return code of -ENOENT indicates that the fingerprint requested could not
|
||||
* be found. Other error codes (both positive and negative) are possible for
|
||||
* obscure error conditions (e.g. corruption).
|
||||
*
|
||||
* \param dev the device you are loading the print for
|
||||
* \param finger the finger of the file you are loading
|
||||
* \param data output location to put the corresponding stored print. Must be
|
||||
* freed with fp_print_data_free() after use.
|
||||
* \returns 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_print_data_load(struct fp_dev *dev,
|
||||
enum fp_finger finger, struct fp_print_data **data)
|
||||
{
|
||||
gchar *path;
|
||||
struct fp_print_data *fdata;
|
||||
int r;
|
||||
|
||||
if (!base_store)
|
||||
storage_setup();
|
||||
|
||||
path = get_path_to_print(dev, finger);
|
||||
r = load_from_file(path, &fdata);
|
||||
g_free(path);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (!fp_dev_supports_print_data(dev, fdata)) {
|
||||
fp_err("print data is not compatible!");
|
||||
fp_print_data_free(fdata);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*data = fdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Removes a stored print from disk previously saved with fp_print_data_save().
|
||||
* \param dev the device that the print belongs to
|
||||
* \param finger the finger of the file you are deleting
|
||||
* \returns 0 on success, negative on error
|
||||
*/
|
||||
API_EXPORTED int fp_print_data_delete(struct fp_dev *dev,
|
||||
enum fp_finger finger)
|
||||
{
|
||||
int r;
|
||||
gchar *path = get_path_to_print(dev, finger);
|
||||
|
||||
fp_dbg("remove finger %d at %s", finger, path);
|
||||
r = g_unlink(path);
|
||||
g_free(path);
|
||||
if (r < 0)
|
||||
fp_dbg("unlink failed with error %d", r);
|
||||
|
||||
/* FIXME: cleanup empty directory */
|
||||
return r;
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Attempts to load a stored print based on a \ref dscv_print
|
||||
* "discovered print" record.
|
||||
*
|
||||
* A return code of -ENOENT indicates that the file referred to by the
|
||||
* discovered print could not be found. Other error codes (both positive and
|
||||
* negative) are possible for obscure error conditions (e.g. corruption).
|
||||
*
|
||||
* \param print the discovered print
|
||||
* \param data output location to point to the corresponding stored print. Must
|
||||
* be freed with fp_print_data_free() after use.
|
||||
* \returns 0 on success, non-zero on error.
|
||||
*/
|
||||
API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
|
||||
struct fp_print_data **data)
|
||||
{
|
||||
return load_from_file(print->path, data);
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Frees a stored print. Must be called when you are finished using the print.
|
||||
* \param data the stored print to destroy. If NULL, function simply returns.
|
||||
*/
|
||||
API_EXPORTED void fp_print_data_free(struct fp_print_data *data)
|
||||
{
|
||||
if (data)
|
||||
g_slist_free_full(data->prints, (GDestroyNotify)fpi_print_data_item_free);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Gets the \ref driver_id "driver ID" for a stored print. The driver ID
|
||||
* indicates which driver the print originally came from. The print is
|
||||
* only usable with a device controlled by that driver.
|
||||
* \param data the stored print
|
||||
* \returns the driver ID of the driver compatible with the print
|
||||
*/
|
||||
API_EXPORTED uint16_t fp_print_data_get_driver_id(struct fp_print_data *data)
|
||||
{
|
||||
return data->driver_id;
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Gets the \ref devtype "devtype" for a stored print. The devtype represents
|
||||
* which type of device under the parent driver is compatible with the print.
|
||||
* \param data the stored print
|
||||
* \returns the devtype of the device range compatible with the print
|
||||
*/
|
||||
API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
|
||||
{
|
||||
return data->devtype;
|
||||
}
|
||||
|
||||
/** @defgroup dscv_print Print discovery
|
||||
* The \ref print_data "stored print" documentation detailed a simple API
|
||||
* for storing per-device prints for a single user, namely
|
||||
* fp_print_data_save(). It also detailed a load function,
|
||||
* fp_print_data_load(), but usage of this function is limited to scenarios
|
||||
* where you know which device you would like to use, and you know which
|
||||
* finger you are looking to verify.
|
||||
*
|
||||
* In other cases, it would be more useful to be able to enumerate all
|
||||
* previously saved prints, potentially even before device discovery. These
|
||||
* functions are designed to offer this functionality to you.
|
||||
*
|
||||
* Discovered prints are stored in a <tt>dscv_print</tt> structure, and you
|
||||
* can use functions documented below to access some information about these
|
||||
* prints. You can determine if a discovered print appears to be compatible
|
||||
* with a device using functions such as fp_dscv_dev_supports_dscv_print() and
|
||||
* fp_dev_supports_dscv_print().
|
||||
*
|
||||
* When you are ready to use the print, you can load it into memory in the form
|
||||
* of a stored print by using the fp_print_data_from_dscv_print() function.
|
||||
*
|
||||
* You may have noticed the use of the word "appears" in the above paragraphs.
|
||||
* libfprint performs print discovery simply by examining the file and
|
||||
* directory structure of libfprint's private data store. It does not examine
|
||||
* the actual prints themselves. Just because a print has been discovered
|
||||
* and appears to be compatible with a certain device does not necessarily mean
|
||||
* that it is usable; when you come to load or use it, under unusual
|
||||
* circumstances it may turn out that the print is corrupt or not for the
|
||||
* device that it appeared to be. Also, it is possible that the print may have
|
||||
* been deleted by the time you come to load it.
|
||||
*/
|
||||
|
||||
static GSList *scan_dev_store_dir(char *devpath, uint16_t driver_id,
|
||||
uint32_t devtype, GSList *list)
|
||||
{
|
||||
GError *err = NULL;
|
||||
const gchar *ent;
|
||||
struct fp_dscv_print *print;
|
||||
|
||||
GDir *dir = g_dir_open(devpath, 0, &err);
|
||||
if (!dir) {
|
||||
fp_err("opendir %s failed: %s", devpath, err->message);
|
||||
g_error_free(err);
|
||||
return list;
|
||||
}
|
||||
|
||||
while ((ent = g_dir_read_name(dir))) {
|
||||
/* ent is an 1 hex character fp_finger code */
|
||||
guint64 val;
|
||||
enum fp_finger finger;
|
||||
gchar *endptr;
|
||||
|
||||
if (*ent == 0 || strlen(ent) != 1)
|
||||
continue;
|
||||
|
||||
val = g_ascii_strtoull(ent, &endptr, 16);
|
||||
if (endptr == ent || !FP_FINGER_IS_VALID(val)) {
|
||||
fp_dbg("skipping print file %s", ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
finger = (enum fp_finger) val;
|
||||
print = g_malloc(sizeof(*print));
|
||||
print->driver_id = driver_id;
|
||||
print->devtype = devtype;
|
||||
print->path = g_build_filename(devpath, ent, NULL);
|
||||
print->finger = finger;
|
||||
list = g_slist_prepend(list, print);
|
||||
}
|
||||
|
||||
g_dir_close(dir);
|
||||
return list;
|
||||
}
|
||||
|
||||
static GSList *scan_driver_store_dir(char *drvpath, uint16_t driver_id,
|
||||
GSList *list)
|
||||
{
|
||||
GError *err = NULL;
|
||||
const gchar *ent;
|
||||
|
||||
GDir *dir = g_dir_open(drvpath, 0, &err);
|
||||
if (!dir) {
|
||||
fp_err("opendir %s failed: %s", drvpath, err->message);
|
||||
g_error_free(err);
|
||||
return list;
|
||||
}
|
||||
|
||||
while ((ent = g_dir_read_name(dir))) {
|
||||
/* ent is an 8 hex character devtype */
|
||||
guint64 val;
|
||||
uint32_t devtype;
|
||||
gchar *endptr;
|
||||
gchar *path;
|
||||
|
||||
if (*ent == 0 || strlen(ent) != 8)
|
||||
continue;
|
||||
|
||||
val = g_ascii_strtoull(ent, &endptr, 16);
|
||||
if (endptr == ent) {
|
||||
fp_dbg("skipping devtype %s", ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
devtype = (uint32_t) val;
|
||||
path = g_build_filename(drvpath, ent, NULL);
|
||||
list = scan_dev_store_dir(path, driver_id, devtype, list);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
g_dir_close(dir);
|
||||
return list;
|
||||
}
|
||||
|
||||
/** \ingroup dscv_print
|
||||
* Scans the users home directory and returns a list of prints that were
|
||||
* previously saved using fp_print_data_save().
|
||||
* \returns a NULL-terminated list of discovered prints, must be freed with
|
||||
* fp_dscv_prints_free() after use.
|
||||
*/
|
||||
API_EXPORTED struct fp_dscv_print **fp_discover_prints(void)
|
||||
{
|
||||
GDir *dir;
|
||||
const gchar *ent;
|
||||
GError *err = NULL;
|
||||
GSList *tmplist = NULL;
|
||||
GSList *elem;
|
||||
unsigned int tmplist_len;
|
||||
struct fp_dscv_print **list;
|
||||
unsigned int i;
|
||||
|
||||
if (!base_store)
|
||||
storage_setup();
|
||||
|
||||
dir = g_dir_open(base_store, 0, &err);
|
||||
if (!dir) {
|
||||
fp_err("opendir %s failed: %s", base_store, err->message);
|
||||
g_error_free(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((ent = g_dir_read_name(dir))) {
|
||||
/* ent is a 4 hex digit driver_id */
|
||||
gchar *endptr;
|
||||
gchar *path;
|
||||
guint64 val;
|
||||
uint16_t driver_id;
|
||||
|
||||
if (*ent == 0 || strlen(ent) != 4)
|
||||
continue;
|
||||
|
||||
val = g_ascii_strtoull(ent, &endptr, 16);
|
||||
if (endptr == ent) {
|
||||
fp_dbg("skipping drv id %s", ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
driver_id = (uint16_t) val;
|
||||
path = g_build_filename(base_store, ent, NULL);
|
||||
tmplist = scan_driver_store_dir(path, driver_id, tmplist);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
g_dir_close(dir);
|
||||
tmplist_len = g_slist_length(tmplist);
|
||||
list = g_malloc(sizeof(*list) * (tmplist_len + 1));
|
||||
elem = tmplist;
|
||||
for (i = 0; i < tmplist_len; i++, elem = g_slist_next(elem))
|
||||
list[i] = elem->data;
|
||||
list[tmplist_len] = NULL; /* NULL-terminate */
|
||||
|
||||
g_slist_free(tmplist);
|
||||
return list;
|
||||
}
|
||||
|
||||
/** \ingroup dscv_print
|
||||
* Frees a list of discovered prints. This function also frees the discovered
|
||||
* prints themselves, so make sure you do not use any discovered prints
|
||||
* after calling this function.
|
||||
* \param prints the list of discovered prints. If NULL, function simply
|
||||
* returns.
|
||||
*/
|
||||
API_EXPORTED void fp_dscv_prints_free(struct fp_dscv_print **prints)
|
||||
{
|
||||
int i;
|
||||
struct fp_dscv_print *print;
|
||||
|
||||
if (!prints)
|
||||
return;
|
||||
|
||||
for (i = 0; (print = prints[i]); i++) {
|
||||
if (print)
|
||||
g_free(print->path);
|
||||
g_free(print);
|
||||
}
|
||||
g_free(prints);
|
||||
}
|
||||
|
||||
/** \ingroup dscv_print
|
||||
* Gets the \ref driver_id "driver ID" for a discovered print. The driver ID
|
||||
* indicates which driver the print originally came from. The print is only
|
||||
* usable with a device controlled by that driver.
|
||||
* \param print the discovered print
|
||||
* \returns the driver ID of the driver compatible with the print
|
||||
*/
|
||||
API_EXPORTED uint16_t fp_dscv_print_get_driver_id(struct fp_dscv_print *print)
|
||||
{
|
||||
return print->driver_id;
|
||||
}
|
||||
|
||||
/** \ingroup dscv_print
|
||||
* Gets the \ref devtype "devtype" for a discovered print. The devtype
|
||||
* represents which type of device under the parent driver is compatible
|
||||
* with the print.
|
||||
* \param print the discovered print
|
||||
* \returns the devtype of the device range compatible with the print
|
||||
*/
|
||||
API_EXPORTED uint32_t fp_dscv_print_get_devtype(struct fp_dscv_print *print)
|
||||
{
|
||||
return print->devtype;
|
||||
}
|
||||
|
||||
/** \ingroup dscv_print
|
||||
* Gets the finger code for a discovered print.
|
||||
* \param print discovered print
|
||||
* \returns a finger code from #fp_finger
|
||||
*/
|
||||
API_EXPORTED enum fp_finger fp_dscv_print_get_finger(struct fp_dscv_print *print)
|
||||
{
|
||||
return print->finger;
|
||||
}
|
||||
|
||||
/** \ingroup dscv_print
|
||||
* Removes a discovered print from disk. After successful return of this
|
||||
* function, functions such as fp_dscv_print_get_finger() will continue to
|
||||
* operate as before, however calling fp_print_data_from_dscv_print() will
|
||||
* fail for obvious reasons.
|
||||
* \param print the discovered print to remove from disk
|
||||
* \returns 0 on success, negative on error
|
||||
*/
|
||||
API_EXPORTED int fp_dscv_print_delete(struct fp_dscv_print *print)
|
||||
{
|
||||
int r;
|
||||
fp_dbg("remove at %s", print->path);
|
||||
r = g_unlink(print->path);
|
||||
if (r < 0)
|
||||
fp_dbg("unlink failed with error %d", r);
|
||||
|
||||
/* FIXME: cleanup empty directory */
|
||||
return r;
|
||||
}
|
||||
|
|
@ -19,104 +19,77 @@
|
|||
|
||||
#define FP_COMPONENT "aes1660"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#include <fp_internal.h>
|
||||
|
||||
#include <assembling.h>
|
||||
#include <aeslib.h>
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "aeslib.h"
|
||||
#include "aesx660.h"
|
||||
#include "aes1660.h"
|
||||
#include "driver_ids.h"
|
||||
|
||||
#define FRAME_WIDTH 128
|
||||
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
|
||||
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
|
||||
|
||||
struct _FpiDeviceAes1660
|
||||
{
|
||||
FpiDeviceAesX660 parent;
|
||||
};
|
||||
G_DECLARE_FINAL_TYPE (FpiDeviceAes1660, fpi_device_aes1660, FPI,
|
||||
DEVICE_AES1660, FpiDeviceAesX660);
|
||||
G_DEFINE_TYPE (FpiDeviceAes1660, fpi_device_aes1660, FPI_TYPE_DEVICE_AES_X660);
|
||||
|
||||
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,
|
||||
.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)
|
||||
static const FpIdEntry id_table[] = {
|
||||
{ .vid = 0x08ff, .pid = 0x1660, },
|
||||
{ .vid = 0x08ff, .pid = 0x1680, },
|
||||
{ .vid = 0x08ff, .pid = 0x1681, },
|
||||
{ .vid = 0x08ff, .pid = 0x1682, },
|
||||
{ .vid = 0x08ff, .pid = 0x1683, },
|
||||
{ .vid = 0x08ff, .pid = 0x1684, },
|
||||
{ .vid = 0x08ff, .pid = 0x1685, },
|
||||
{ .vid = 0x08ff, .pid = 0x1686, },
|
||||
{ .vid = 0x08ff, .pid = 0x1687, },
|
||||
{ .vid = 0x08ff, .pid = 0x1688, },
|
||||
{ .vid = 0x08ff, .pid = 0x1689, },
|
||||
{ .vid = 0x08ff, .pid = 0x168a, },
|
||||
{ .vid = 0x08ff, .pid = 0x168b, },
|
||||
{ .vid = 0x08ff, .pid = 0x168c, },
|
||||
{ .vid = 0x08ff, .pid = 0x168d, },
|
||||
{ .vid = 0x08ff, .pid = 0x168e, },
|
||||
{ .vid = 0x08ff, .pid = 0x168f, },
|
||||
{ .vid = 0, .pid = 0, .driver_data = 0 },
|
||||
};
|
||||
|
||||
static void
|
||||
fpi_device_aes1660_init (FpiDeviceAes1660 *self)
|
||||
{
|
||||
/* TODO check that device has endpoints we're using */
|
||||
int r;
|
||||
struct aesX660_dev *aesdev;
|
||||
|
||||
r = libusb_claim_interface(dev->udev, 0);
|
||||
if (r < 0) {
|
||||
fp_err("could not claim interface 0: %s", libusb_error_name(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
|
||||
aesdev->buffer = g_malloc0(AES1660_FRAME_SIZE + AESX660_HEADER_SIZE);
|
||||
aesdev->init_seqs[0] = aes1660_init_1;
|
||||
aesdev->init_seqs_len[0] = array_n_elements(aes1660_init_1);
|
||||
aesdev->init_seqs[1] = aes1660_init_2;
|
||||
aesdev->init_seqs_len[1] = array_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)
|
||||
static void
|
||||
fpi_device_aes1660_class_init (FpiDeviceAes1660Class *klass)
|
||||
{
|
||||
struct aesX660_dev *aesdev = dev->priv;
|
||||
g_free(aesdev->buffer);
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(dev->udev, 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
||||
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
|
||||
FpiDeviceAesX660Class *aes_class = FPI_DEVICE_AES_X660_CLASS (klass);
|
||||
|
||||
dev_class->id = "aes1660";
|
||||
dev_class->full_name = "AuthenTec AES1660";
|
||||
dev_class->type = FP_DEVICE_TYPE_USB;
|
||||
dev_class->id_table = id_table;
|
||||
dev_class->scan_type = FP_SCAN_TYPE_SWIPE;
|
||||
|
||||
img_class->bz3_threshold = 20;
|
||||
|
||||
img_class->img_width = FRAME_WIDTH + FRAME_WIDTH / 2;
|
||||
img_class->img_height = -1;
|
||||
|
||||
aes_class->init_seqs[0] = aes1660_init_1;
|
||||
aes_class->init_seqs_len[0] = G_N_ELEMENTS (aes1660_init_1);
|
||||
aes_class->init_seqs[1] = aes1660_init_2;
|
||||
aes_class->init_seqs_len[1] = G_N_ELEMENTS (aes1660_init_2);
|
||||
aes_class->start_imaging_cmd = (unsigned char *) aes1660_start_imaging_cmd;
|
||||
aes_class->start_imaging_cmd_len = sizeof (aes1660_start_imaging_cmd);
|
||||
aes_class->assembling_ctx = &assembling_ctx;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
|
|
@ -19,158 +19,155 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __AES2501_H
|
||||
#define __AES2501_H
|
||||
#pragma once
|
||||
|
||||
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 */
|
||||
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 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 */
|
||||
#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)
|
||||
#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
|
||||
#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 */
|
||||
/* 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 */
|
||||
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 */
|
||||
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 */
|
||||
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 mesure drive setting, 1 = when sine wave is selected */
|
||||
#define AES2501_MEASDRV_MEASURE_SQUARE 0x10
|
||||
#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 */
|
||||
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 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 */
|
||||
#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 */
|
||||
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 */
|
||||
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
|
||||
#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
|
||||
#define AES2501_IMAGCTRL_IMG_DATA_DISABLE 0x01
|
||||
/* send histogram when imaging */
|
||||
#define AES2501_IMAGCTRL_HISTO_DATA_ENABLE 0x02
|
||||
#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
|
||||
#define AES2501_IMAGCTRL_HISTO_EACH_ROW 0x04
|
||||
/* send full image array rather than 64x64 center */
|
||||
#define AES2501_IMAGCTRL_HISTO_FULL_ARRAY 0x08
|
||||
#define AES2501_IMAGCTRL_HISTO_FULL_ARRAY 0x08
|
||||
/* return registers before data (rather than after) */
|
||||
#define AES2501_IMAGCTRL_REG_FIRST 0x10
|
||||
#define AES2501_IMAGCTRL_REG_FIRST 0x10
|
||||
/* return test registers with register dump */
|
||||
#define AES2501_IMAGCTRL_TST_REG_ENABLE 0x20
|
||||
#define AES2501_IMAGCTRL_TST_REG_ENABLE 0x20
|
||||
|
||||
#define AES2501_CHWORD1_IS_FINGER 0x01 /* If set, finger is present */
|
||||
#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_TREGC_ENABLE 0x01
|
||||
|
||||
#define AES2501_LPONT_MIN_VALUE 0x00 /* 0 ms */
|
||||
#define AES2501_LPONT_MAX_VALUE 0x1f /* About 16 ms */
|
||||
#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 */
|
||||
|
|
|
@ -17,98 +17,95 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __AES2550_H
|
||||
#define __AES2550_H
|
||||
#pragma once
|
||||
|
||||
/* 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_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_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_REG83_FINGER_PRESENT (1 << 7)
|
||||
|
||||
#define AES2550_REG85_FLUSH_PER_FRAME (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_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_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_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_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_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_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_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_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_REGDC_BP_NUM_REF_SWEEP_OFS (0)
|
||||
#define AES2550_REGDC_DEBUG_CTRL2_OFS (3)
|
||||
|
||||
#define AES2550_REGDD_DEBUG_CTRL1_OFS (0)
|
||||
#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,
|
||||
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_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
|
||||
#define AES2550_EP_IN_BUF_SIZE 8192
|
||||
|
|
|
@ -19,105 +19,79 @@
|
|||
|
||||
#define FP_COMPONENT "aes2660"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#include <fp_internal.h>
|
||||
|
||||
#include <assembling.h>
|
||||
#include <aeslib.h>
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "aeslib.h"
|
||||
#include "aesx660.h"
|
||||
#include "aes2660.h"
|
||||
#include "driver_ids.h"
|
||||
|
||||
#define FRAME_WIDTH 192
|
||||
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
|
||||
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
|
||||
|
||||
struct _FpiDeviceAes2660
|
||||
{
|
||||
FpiDeviceAesX660 parent;
|
||||
};
|
||||
G_DECLARE_FINAL_TYPE (FpiDeviceAes2660, fpi_device_aes2660, FPI,
|
||||
DEVICE_AES2660, FpiDeviceAesX660);
|
||||
G_DEFINE_TYPE (FpiDeviceAes2660, fpi_device_aes2660, FPI_TYPE_DEVICE_AES_X660);
|
||||
|
||||
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,
|
||||
.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)
|
||||
static const FpIdEntry id_table[] = {
|
||||
{ .vid = 0x08ff, .pid = 0x2660, },
|
||||
{ .vid = 0x08ff, .pid = 0x2680, },
|
||||
{ .vid = 0x08ff, .pid = 0x2681, },
|
||||
{ .vid = 0x08ff, .pid = 0x2682, },
|
||||
{ .vid = 0x08ff, .pid = 0x2683, },
|
||||
{ .vid = 0x08ff, .pid = 0x2684, },
|
||||
{ .vid = 0x08ff, .pid = 0x2685, },
|
||||
{ .vid = 0x08ff, .pid = 0x2686, },
|
||||
{ .vid = 0x08ff, .pid = 0x2687, },
|
||||
{ .vid = 0x08ff, .pid = 0x2688, },
|
||||
{ .vid = 0x08ff, .pid = 0x2689, },
|
||||
{ .vid = 0x08ff, .pid = 0x268a, },
|
||||
{ .vid = 0x08ff, .pid = 0x268b, },
|
||||
{ .vid = 0x08ff, .pid = 0x268c, },
|
||||
{ .vid = 0x08ff, .pid = 0x268d, },
|
||||
{ .vid = 0x08ff, .pid = 0x268e, },
|
||||
{ .vid = 0x08ff, .pid = 0x268f, },
|
||||
{ .vid = 0x08ff, .pid = 0x2691, },
|
||||
{ .vid = 0, .pid = 0, .driver_data = 0 },
|
||||
};
|
||||
|
||||
static void
|
||||
fpi_device_aes2660_init (FpiDeviceAes2660 *self)
|
||||
{
|
||||
/* TODO check that device has endpoints we're using */
|
||||
int r;
|
||||
struct aesX660_dev *aesdev;
|
||||
|
||||
r = libusb_claim_interface(dev->udev, 0);
|
||||
if (r < 0) {
|
||||
fp_err("could not claim interface 0: %s", libusb_error_name(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
|
||||
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] = array_n_elements(aes2660_init_1);
|
||||
aesdev->init_seqs[1] = aes2660_init_2;
|
||||
aesdev->init_seqs_len[1] = array_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)
|
||||
static void
|
||||
fpi_device_aes2660_class_init (FpiDeviceAes2660Class *klass)
|
||||
{
|
||||
struct aesX660_dev *aesdev = dev->priv;
|
||||
g_free(aesdev->buffer);
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(dev->udev, 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
||||
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
|
||||
FpiDeviceAesX660Class *aes_class = FPI_DEVICE_AES_X660_CLASS (klass);
|
||||
|
||||
dev_class->id = "aes2660";
|
||||
dev_class->full_name = "AuthenTec AES2660";
|
||||
dev_class->type = FP_DEVICE_TYPE_USB;
|
||||
dev_class->id_table = id_table;
|
||||
dev_class->scan_type = FP_SCAN_TYPE_SWIPE;
|
||||
|
||||
img_class->bz3_threshold = 20;
|
||||
|
||||
img_class->img_width = FRAME_WIDTH + FRAME_WIDTH / 2;
|
||||
img_class->img_height = -1;
|
||||
|
||||
aes_class->init_seqs[0] = aes2660_init_1;
|
||||
aes_class->init_seqs_len[0] = G_N_ELEMENTS (aes2660_init_1);
|
||||
aes_class->init_seqs[1] = aes2660_init_2;
|
||||
aes_class->init_seqs_len[1] = G_N_ELEMENTS (aes2660_init_2);
|
||||
aes_class->start_imaging_cmd = (unsigned char *) aes2660_start_imaging_cmd;
|
||||
aes_class->start_imaging_cmd_len = sizeof (aes2660_start_imaging_cmd);
|
||||
aes_class->assembling_ctx = &assembling_ctx;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
|
|
@ -29,161 +29,134 @@
|
|||
|
||||
#define FP_COMPONENT "aes3500"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include <aeslib.h>
|
||||
#include <fp_internal.h>
|
||||
|
||||
#include "aes3k.h"
|
||||
#include "driver_ids.h"
|
||||
|
||||
#define DATA_BUFLEN 0x2089
|
||||
#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
|
||||
#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 },
|
||||
/* master reset */
|
||||
{ 0x80, 0x01 },
|
||||
{ 0, 0 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
|
||||
{ 0x81, 0x00 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
{ 0x81, 0x00 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
|
||||
/* scan reset */
|
||||
{ 0x80, 0x02 },
|
||||
{ 0, 0 },
|
||||
{ 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 },
|
||||
/* 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 },
|
||||
{ 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 },
|
||||
/* 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 },
|
||||
/* 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 },
|
||||
{ 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 },
|
||||
};
|
||||
|
||||
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
static struct aes_regwrite capture_reqs[] = {
|
||||
{ 0x80, 0x00 },
|
||||
{ 0x81, 0x00 },
|
||||
{ 0, 0 },
|
||||
{ 0x81, 0x04 },
|
||||
{ 0, 0 },
|
||||
{ 0x81, 0x00 },
|
||||
};
|
||||
|
||||
struct _FpiDeviceAes3500
|
||||
{
|
||||
int r;
|
||||
struct aes3k_dev *aesdev;
|
||||
FpiDeviceAes3k parent;
|
||||
};
|
||||
G_DECLARE_FINAL_TYPE (FpiDeviceAes3500, fpi_device_aes3500, FPI,
|
||||
DEVICE_AES3500, FpiDeviceAes3k);
|
||||
G_DEFINE_TYPE (FpiDeviceAes3500, fpi_device_aes3500, FPI_TYPE_DEVICE_AES3K);
|
||||
|
||||
r = libusb_claim_interface(dev->udev, 0);
|
||||
if (r < 0) {
|
||||
fp_err("could not claim interface 0: %s", libusb_error_name(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev));
|
||||
static const FpIdEntry id_table[] = {
|
||||
{ .vid = 0x08ff, .pid = 0x5731 },
|
||||
{ .vid = 0, .pid = 0, .driver_data = 0 },
|
||||
};
|
||||
|
||||
if (!aesdev)
|
||||
return -ENOMEM;
|
||||
|
||||
aesdev->data_buflen = DATA_BUFLEN;
|
||||
aesdev->frame_width = FRAME_WIDTH;
|
||||
aesdev->frame_size = FRAME_SIZE;
|
||||
aesdev->frame_number = FRAME_NUMBER;
|
||||
aesdev->enlarge_factor = ENLARGE_FACTOR;
|
||||
aesdev->init_reqs = init_reqs;
|
||||
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
|
||||
return r;
|
||||
static void
|
||||
fpi_device_aes3500_init (FpiDeviceAes3500 *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
static void
|
||||
fpi_device_aes3500_class_init (FpiDeviceAes3500Class *klass)
|
||||
{
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(dev->udev, 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
||||
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
|
||||
FpiDeviceAes3kClass *aes_class = FPI_DEVICE_AES3K_CLASS (klass);
|
||||
|
||||
dev_class->id = "aes3500";
|
||||
dev_class->full_name = "AuthenTec AES3500";
|
||||
dev_class->id_table = id_table;
|
||||
|
||||
img_class->img_height = FRAME_WIDTH * ENLARGE_FACTOR;
|
||||
img_class->img_width = FRAME_WIDTH * ENLARGE_FACTOR;
|
||||
|
||||
aes_class->data_buflen = DATA_BUFLEN;
|
||||
aes_class->frame_width = FRAME_WIDTH;
|
||||
aes_class->frame_size = FRAME_SIZE;
|
||||
aes_class->frame_number = FRAME_NUMBER;
|
||||
aes_class->enlarge_factor = ENLARGE_FACTOR;
|
||||
aes_class->init_reqs = init_reqs;
|
||||
aes_class->init_reqs_len = G_N_ELEMENTS (init_reqs);
|
||||
aes_class->capture_reqs = capture_reqs;
|
||||
aes_class->capture_reqs_len = G_N_ELEMENTS (capture_reqs);
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
|
|
@ -36,134 +36,237 @@
|
|||
|
||||
#define FP_COMPONENT "aes3k"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include <aeslib.h>
|
||||
#include <fp_internal.h>
|
||||
|
||||
#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)
|
||||
typedef struct
|
||||
{
|
||||
size_t row, column;
|
||||
/* This is used both as a flag that we are in a capture operation
|
||||
* and for cancellation.
|
||||
*/
|
||||
GCancellable *img_capture_cancel;
|
||||
} FpiDeviceAes3kPrivate;
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
#define CTRL_TIMEOUT 1000
|
||||
#define EP_IN (1 | FPI_USB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | FPI_USB_ENDPOINT_OUT)
|
||||
|
||||
static void do_capture (FpImageDevice *dev);
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (FpiDeviceAes3k, fpi_device_aes3k, FP_TYPE_IMAGE_DEVICE);
|
||||
|
||||
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)
|
||||
static void
|
||||
img_cb (FpiUsbTransfer *transfer, FpDevice *device,
|
||||
gpointer user_data, GError *error)
|
||||
{
|
||||
struct fp_img_dev *dev = transfer->user_data;
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
unsigned char *ptr = transfer->buffer;
|
||||
struct fp_img *tmp;
|
||||
struct fp_img *img;
|
||||
int i;
|
||||
FpImageDevice *dev = FP_IMAGE_DEVICE (device);
|
||||
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (device);
|
||||
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
|
||||
FpiDeviceAes3kClass *cls = FPI_DEVICE_AES3K_GET_CLASS (self);
|
||||
unsigned char *ptr = transfer->buffer;
|
||||
FpImage *tmp;
|
||||
FpImage *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;
|
||||
}
|
||||
/* Image capture operation is finished (error/completed) */
|
||||
g_clear_object (&priv->img_capture_cancel);
|
||||
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
if (error)
|
||||
{
|
||||
if (g_error_matches (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_CANCELLED))
|
||||
{
|
||||
/* Cancellation implies we are deactivating. */
|
||||
g_error_free (error);
|
||||
fpi_image_device_deactivate_complete (dev, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
fpi_image_device_session_error (dev, error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: this is an ugly hack to make the image big enough for NBIS
|
||||
* to process reliably */
|
||||
img = fpi_im_resize(tmp, aesdev->enlarge_factor, aesdev->enlarge_factor);
|
||||
fp_img_free(tmp);
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
fpi_image_device_report_finger_status (dev, TRUE);
|
||||
|
||||
/* 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);
|
||||
tmp = fp_image_new (cls->frame_width, cls->frame_width);
|
||||
tmp->width = cls->frame_width;
|
||||
tmp->height = cls->frame_width;
|
||||
tmp->flags = FPI_IMAGE_COLORS_INVERTED | FPI_IMAGE_V_FLIPPED | FPI_IMAGE_H_FLIPPED;
|
||||
for (i = 0; i < cls->frame_number; i++)
|
||||
{
|
||||
fp_dbg ("frame header byte %02x", *ptr);
|
||||
ptr++;
|
||||
aes3k_assemble_image (ptr, cls->frame_width, AES3K_FRAME_HEIGHT, tmp->data + (i * cls->frame_width * AES3K_FRAME_HEIGHT));
|
||||
ptr += cls->frame_size;
|
||||
}
|
||||
|
||||
do_capture(dev);
|
||||
/* FIXME: this is an ugly hack to make the image big enough for NBIS
|
||||
* to process reliably */
|
||||
img = fpi_image_resize (tmp, cls->enlarge_factor, cls->enlarge_factor);
|
||||
g_object_unref (tmp);
|
||||
fpi_image_device_image_captured (dev, img);
|
||||
|
||||
err:
|
||||
g_free(transfer->buffer);
|
||||
aesdev->img_trf = NULL;
|
||||
libusb_free_transfer(transfer);
|
||||
/* FIXME: rather than assuming finger has gone, we should poll regs until
|
||||
* it really has. */
|
||||
fpi_image_device_report_finger_status (dev, FALSE);
|
||||
|
||||
/* Note: The transfer is re-started when we switch to the AWAIT_FINGER_ON state. */
|
||||
}
|
||||
|
||||
static void do_capture(struct fp_img_dev *dev)
|
||||
static void
|
||||
do_capture (FpImageDevice *dev)
|
||||
{
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
unsigned char *data;
|
||||
int r;
|
||||
g_autoptr(FpiUsbTransfer) img_trf = NULL;
|
||||
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
|
||||
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
|
||||
FpiDeviceAes3kClass *cls = FPI_DEVICE_AES3K_GET_CLASS (self);
|
||||
|
||||
aesdev->img_trf = libusb_alloc_transfer(0);
|
||||
if (!aesdev->img_trf) {
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
return;
|
||||
}
|
||||
|
||||
data = g_malloc(aesdev->data_buflen);
|
||||
libusb_fill_bulk_transfer(aesdev->img_trf, dev->udev, 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);
|
||||
}
|
||||
img_trf = fpi_usb_transfer_new (FP_DEVICE (dev));
|
||||
fpi_usb_transfer_fill_bulk (img_trf, EP_IN, cls->data_buflen);
|
||||
img_trf->short_is_error = TRUE;
|
||||
fpi_usb_transfer_submit (g_steal_pointer (&img_trf), 0,
|
||||
priv->img_capture_cancel,
|
||||
img_cb, NULL);
|
||||
}
|
||||
|
||||
static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
|
||||
static void
|
||||
capture_reqs_cb (FpImageDevice *dev, GError *result, void *user_data)
|
||||
{
|
||||
fpi_imgdev_activate_complete(dev, result);
|
||||
if (result == 0)
|
||||
do_capture(dev);
|
||||
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
|
||||
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
|
||||
|
||||
if (result)
|
||||
{
|
||||
g_clear_object (&priv->img_capture_cancel);
|
||||
fpi_image_device_session_error (dev, result);
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: we never cancel a pending capture. So we are likely leaving the
|
||||
* hardware in a bad state should we abort the capture operation and the
|
||||
* user does not touch the device.
|
||||
* But, we don't know how we might cancel, so just leave it as is. */
|
||||
do_capture (dev);
|
||||
}
|
||||
|
||||
int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
|
||||
static void
|
||||
do_capture_start (FpImageDevice *dev)
|
||||
{
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
aes_write_regv(dev, aesdev->init_reqs, aesdev->init_reqs_len, init_reqs_cb, NULL);
|
||||
return 0;
|
||||
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
|
||||
FpiDeviceAes3kClass *cls = FPI_DEVICE_AES3K_GET_CLASS (self);
|
||||
|
||||
aes_write_regv (dev, cls->capture_reqs, cls->capture_reqs_len, capture_reqs_cb, NULL);
|
||||
}
|
||||
|
||||
void aes3k_dev_deactivate(struct fp_img_dev *dev)
|
||||
static void
|
||||
init_reqs_cb (FpImageDevice *dev, GError *result, void *user_data)
|
||||
{
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
|
||||
/* FIXME: should wait for cancellation to complete before returning
|
||||
* from deactivation, otherwise app may legally exit before we've
|
||||
* cleaned up */
|
||||
if (aesdev->img_trf)
|
||||
libusb_cancel_transfer(aesdev->img_trf);
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
fpi_image_device_activate_complete (dev, result);
|
||||
}
|
||||
|
||||
static void
|
||||
aes3k_dev_activate (FpImageDevice *dev)
|
||||
{
|
||||
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
|
||||
FpiDeviceAes3kClass *cls = FPI_DEVICE_AES3K_GET_CLASS (self);
|
||||
|
||||
aes_write_regv (dev, cls->init_reqs, cls->init_reqs_len, init_reqs_cb, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
aes3k_dev_deactivate (FpImageDevice *dev)
|
||||
{
|
||||
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
|
||||
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
|
||||
|
||||
/* If a capture is running, then deactivation finishes from the cancellation handler */
|
||||
if (priv->img_capture_cancel)
|
||||
g_cancellable_cancel (priv->img_capture_cancel);
|
||||
else
|
||||
fpi_image_device_deactivate_complete (dev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
aes3k_dev_change_state (FpImageDevice *dev, FpiImageDeviceState state)
|
||||
{
|
||||
FpiDeviceAes3k *self = FPI_DEVICE_AES3K (dev);
|
||||
FpiDeviceAes3kPrivate *priv = fpi_device_aes3k_get_instance_private (self);
|
||||
|
||||
if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
|
||||
{
|
||||
g_assert (!priv->img_capture_cancel);
|
||||
priv->img_capture_cancel = g_cancellable_new ();
|
||||
|
||||
do_capture_start (dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_aes3k_init (FpiDeviceAes3k *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
aes3k_dev_init (FpImageDevice *dev)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_usb_device_claim_interface (fpi_device_get_usb_device (FP_DEVICE (dev)), 0, 0, &error))
|
||||
{
|
||||
fpi_image_device_open_complete (dev, error);
|
||||
return;
|
||||
}
|
||||
|
||||
fpi_image_device_open_complete (dev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
aes3k_dev_deinit (FpImageDevice *dev)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (dev)),
|
||||
0, 0, &error);
|
||||
fpi_image_device_close_complete (dev, error);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fpi_device_aes3k_class_init (FpiDeviceAes3kClass *klass)
|
||||
{
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
||||
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
|
||||
|
||||
dev_class->type = FP_DEVICE_TYPE_USB;
|
||||
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
|
||||
|
||||
img_class->img_open = aes3k_dev_init;
|
||||
img_class->img_close = aes3k_dev_deinit;
|
||||
img_class->activate = aes3k_dev_activate;
|
||||
img_class->change_state = aes3k_dev_change_state;
|
||||
img_class->deactivate = aes3k_dev_deactivate;
|
||||
|
||||
/* Extremely low due to low image quality. */
|
||||
img_class->bz3_threshold = 9;
|
||||
|
||||
/* Everything else is set by the subclasses. */
|
||||
}
|
||||
|
|
|
@ -34,25 +34,29 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef __AES3K_H
|
||||
#define __AES3K_H
|
||||
#pragma once
|
||||
#include "fpi-image-device.h"
|
||||
#include "aeslib.h"
|
||||
|
||||
#define AES3K_FRAME_HEIGHT 16
|
||||
#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;
|
||||
G_DECLARE_DERIVABLE_TYPE (FpiDeviceAes3k, fpi_device_aes3k, FPI,
|
||||
DEVICE_AES3K, FpImageDevice)
|
||||
|
||||
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;
|
||||
#define FPI_TYPE_DEVICE_AES3K (fpi_device_aes3k_get_type ())
|
||||
|
||||
struct _FpiDeviceAes3kClass
|
||||
{
|
||||
FpImageDeviceClass parent;
|
||||
|
||||
gsize frame_width; /* image size = frame_width x frame_width */
|
||||
gsize frame_size; /* 4 bits/pixel: frame_width x AES3K_FRAME_HEIGHT / 2 */
|
||||
gsize frame_number; /* number of frames */
|
||||
gsize enlarge_factor;
|
||||
|
||||
gsize data_buflen; /* buffer length of usb bulk transfer */
|
||||
struct aes_regwrite *init_reqs; /* initial values sent to device */
|
||||
gsize init_reqs_len;
|
||||
struct aes_regwrite *capture_reqs; /* capture values sent to device */
|
||||
gsize capture_reqs_len;
|
||||
};
|
||||
|
||||
|
||||
int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state);
|
||||
void aes3k_dev_deactivate(struct fp_img_dev *dev);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,161 +26,134 @@
|
|||
|
||||
#define FP_COMPONENT "aes4000"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include <aeslib.h>
|
||||
#include <fp_internal.h>
|
||||
|
||||
#include "aes3k.h"
|
||||
#include "driver_ids.h"
|
||||
|
||||
#define DATA_BUFLEN 0x1259
|
||||
#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
|
||||
#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 },
|
||||
/* master reset */
|
||||
{ 0x80, 0x01 },
|
||||
{ 0, 0 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
|
||||
{ 0x81, 0x00 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
{ 0x81, 0x00 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
|
||||
/* scan reset */
|
||||
{ 0x80, 0x02 },
|
||||
{ 0, 0 },
|
||||
{ 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 },
|
||||
/* 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 },
|
||||
{ 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 },
|
||||
/* 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 },
|
||||
/* 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 },
|
||||
{ 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 },
|
||||
};
|
||||
|
||||
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
static struct aes_regwrite capture_reqs[] = {
|
||||
{ 0x80, 0x00 },
|
||||
{ 0x81, 0x00 },
|
||||
{ 0, 0 },
|
||||
{ 0x81, 0x04 },
|
||||
{ 0, 0 },
|
||||
{ 0x81, 0x00 },
|
||||
};
|
||||
|
||||
struct _FpiDeviceAes4000
|
||||
{
|
||||
int r;
|
||||
struct aes3k_dev *aesdev;
|
||||
FpiDeviceAes3k parent;
|
||||
};
|
||||
G_DECLARE_FINAL_TYPE (FpiDeviceAes4000, fpi_device_aes4000, FPI,
|
||||
DEVICE_AES4000, FpiDeviceAes3k);
|
||||
G_DEFINE_TYPE (FpiDeviceAes4000, fpi_device_aes4000, FPI_TYPE_DEVICE_AES3K);
|
||||
|
||||
r = libusb_claim_interface(dev->udev, 0);
|
||||
if (r < 0) {
|
||||
fp_err("could not claim interface 0: %s", libusb_error_name(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev));
|
||||
static const FpIdEntry id_table[] = {
|
||||
{ .pid = 0x08ff, .vid = 0x5501 },
|
||||
{ .vid = 0, .pid = 0, .driver_data = 0 },
|
||||
};
|
||||
|
||||
if (!aesdev)
|
||||
return -ENOMEM;
|
||||
|
||||
aesdev->data_buflen = DATA_BUFLEN;
|
||||
aesdev->frame_width = FRAME_WIDTH;
|
||||
aesdev->frame_size = FRAME_SIZE;
|
||||
aesdev->frame_number = FRAME_NUMBER;
|
||||
aesdev->enlarge_factor = ENLARGE_FACTOR;
|
||||
aesdev->init_reqs = init_reqs;
|
||||
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
|
||||
return r;
|
||||
static void
|
||||
fpi_device_aes4000_init (FpiDeviceAes4000 *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
static void
|
||||
fpi_device_aes4000_class_init (FpiDeviceAes4000Class *klass)
|
||||
{
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(dev->udev, 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
||||
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
|
||||
FpiDeviceAes3kClass *aes_class = FPI_DEVICE_AES3K_CLASS (klass);
|
||||
|
||||
dev_class->id = "aes4000";
|
||||
dev_class->full_name = "AuthenTec AES4000";
|
||||
dev_class->id_table = id_table;
|
||||
|
||||
img_class->img_height = FRAME_WIDTH * ENLARGE_FACTOR;
|
||||
img_class->img_width = FRAME_WIDTH * ENLARGE_FACTOR;
|
||||
|
||||
aes_class->data_buflen = DATA_BUFLEN;
|
||||
aes_class->frame_width = FRAME_WIDTH;
|
||||
aes_class->frame_size = FRAME_SIZE;
|
||||
aes_class->frame_number = FRAME_NUMBER;
|
||||
aes_class->enlarge_factor = ENLARGE_FACTOR;
|
||||
aes_class->init_reqs = init_reqs;
|
||||
aes_class->init_reqs_len = G_N_ELEMENTS (init_reqs);
|
||||
aes_class->capture_reqs = capture_reqs;
|
||||
aes_class->capture_reqs_len = G_N_ELEMENTS (capture_reqs);
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
|
169
libfprint/drivers/aeslib.c
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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 "drivers_api.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aeslib.h"
|
||||
|
||||
#define MAX_REGWRITES_PER_REQUEST 16
|
||||
|
||||
#define BULK_TIMEOUT 4000
|
||||
#define EP_IN (1 | FPI_USB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | FPI_USB_ENDPOINT_OUT)
|
||||
|
||||
struct write_regv_data
|
||||
{
|
||||
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 (FpImageDevice *dev,
|
||||
struct write_regv_data *wdata);
|
||||
|
||||
/* libusb bulk callback for regv write completion transfer. continues the
|
||||
* transaction */
|
||||
static void
|
||||
write_regv_trf_complete (FpiUsbTransfer *transfer, FpDevice *device,
|
||||
gpointer user_data, GError *error)
|
||||
{
|
||||
struct write_regv_data *wdata = user_data;
|
||||
|
||||
if (error)
|
||||
{
|
||||
wdata->callback (FP_IMAGE_DEVICE (device), error, wdata->user_data);
|
||||
g_free (wdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue_write_regv (FP_IMAGE_DEVICE (device), wdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* write from wdata->offset to upper_bound (inclusive) of wdata->regs */
|
||||
static void
|
||||
do_write_regv (FpImageDevice *dev, 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 int i;
|
||||
size_t data_offset = 0;
|
||||
FpiUsbTransfer *transfer = fpi_usb_transfer_new (FP_DEVICE (dev));
|
||||
|
||||
fpi_usb_transfer_fill_bulk (transfer, EP_OUT, alloc_size);
|
||||
|
||||
for (i = offset; i < offset + num; i++)
|
||||
{
|
||||
const struct aes_regwrite *regwrite = &wdata->regs[i];
|
||||
transfer->buffer[data_offset++] = regwrite->reg;
|
||||
transfer->buffer[data_offset++] = regwrite->value;
|
||||
}
|
||||
|
||||
transfer->short_is_error = TRUE;
|
||||
fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL,
|
||||
write_regv_trf_complete, wdata);
|
||||
}
|
||||
|
||||
/* 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 (FpImageDevice *dev, struct write_regv_data *wdata)
|
||||
{
|
||||
unsigned int offset = wdata->offset;
|
||||
unsigned int regs_remaining;
|
||||
unsigned int limit;
|
||||
unsigned int upper_bound;
|
||||
int i;
|
||||
|
||||
/* 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 (dev, 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;
|
||||
}
|
||||
|
||||
do_write_regv (dev, wdata, upper_bound);
|
||||
|
||||
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 (FpImageDevice *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->num_regs = num_regs;
|
||||
wdata->regs = regs;
|
||||
wdata->offset = 0;
|
||||
wdata->callback = callback;
|
||||
wdata->user_data = user_data;
|
||||
continue_write_regv (dev, 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;
|
||||
}
|
46
libfprint/drivers/aeslib.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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)(FpImageDevice *dev,
|
||||
GError *error,
|
||||
void *user_data);
|
||||
|
||||
void aes_write_regv (FpImageDevice *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);
|
|
@ -17,8 +17,7 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __AESX660_H
|
||||
#define __AESX660_H
|
||||
#pragma once
|
||||
|
||||
#define AESX660_HEADER_SIZE 3
|
||||
#define AESX660_RESPONSE_TYPE_OFFSET 0x00
|
||||
|
@ -43,80 +42,70 @@
|
|||
|
||||
#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;
|
||||
G_DECLARE_DERIVABLE_TYPE (FpiDeviceAesX660, fpi_device_aes_x660, FPI,
|
||||
DEVICE_AES_X660, FpImageDevice)
|
||||
|
||||
/* 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;
|
||||
#define FPI_TYPE_DEVICE_AES_X660 (fpi_device_aes_x660_get_type ())
|
||||
|
||||
struct _FpiDeviceAesX660Class
|
||||
{
|
||||
FpImageDeviceClass parent;
|
||||
|
||||
struct aesX660_cmd *init_seqs[2];
|
||||
gsize init_seqs_len[2];
|
||||
guint8 *start_imaging_cmd;
|
||||
gsize start_imaging_cmd_len;
|
||||
struct fpi_frame_asmbl_ctx *assembling_ctx;
|
||||
};
|
||||
|
||||
struct aesX660_cmd {
|
||||
const unsigned char *cmd;
|
||||
size_t len;
|
||||
struct aesX660_cmd
|
||||
{
|
||||
const guint8 *cmd;
|
||||
gsize 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
|
||||
static const guint8 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 guint8 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
|
||||
static const guint8 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 guint8 pkt1371[] = {
|
||||
0x40, 0x01, 0x00, 0x01
|
||||
};
|
||||
*/
|
||||
|
||||
static const unsigned char set_idle_cmd[] = {
|
||||
0x0d, /* Reset or "set idle"? */
|
||||
static const guint8 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 guint8 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,
|
||||
static const guint8 calibrate_cmd[] = {
|
||||
0x44, 0x02, 0x00, 0x04, 0x00,
|
||||
0x06,
|
||||
};
|
||||
|
||||
int aesX660_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state);
|
||||
void aesX660_dev_deactivate(struct fp_img_dev *dev);
|
||||
|
||||
#endif
|
||||
|
|
444
libfprint/drivers/egis0570.c
Normal file
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* Egis Technology Inc. (aka. LighTuning) 0570 driver for libfprint
|
||||
* Copyright (C) 2021 Maxim Kolesnikov <kolesnikov@svyazcom.ru>
|
||||
* Copyright (C) 2021 Saeed/Ali Rk <saeed.ali.rahimi@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 "egis0570"
|
||||
|
||||
#include "egis0570.h"
|
||||
#include "drivers_api.h"
|
||||
|
||||
/* Packet types */
|
||||
#define PKT_TYPE_INIT 0
|
||||
#define PKT_TYPE_REPEAT 1
|
||||
|
||||
/* Struct */
|
||||
struct _FpDeviceEgis0570
|
||||
{
|
||||
FpImageDevice parent;
|
||||
|
||||
gboolean running;
|
||||
gboolean stop;
|
||||
|
||||
GSList *strips;
|
||||
guint8 *background;
|
||||
gsize strips_len;
|
||||
|
||||
int pkt_num;
|
||||
int pkt_type;
|
||||
};
|
||||
G_DECLARE_FINAL_TYPE (FpDeviceEgis0570, fpi_device_egis0570, FPI, DEVICE_EGIS0570, FpImageDevice);
|
||||
G_DEFINE_TYPE (FpDeviceEgis0570, fpi_device_egis0570, FP_TYPE_IMAGE_DEVICE);
|
||||
|
||||
static unsigned char
|
||||
egis_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 = EGIS0570_IMGWIDTH,
|
||||
.frame_height = EGIS0570_RFMGHEIGHT,
|
||||
.image_width = EGIS0570_IMGWIDTH * 4 / 3,
|
||||
.get_pixel = egis_get_pixel,
|
||||
};
|
||||
|
||||
/*
|
||||
* Service
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
is_last_pkt (FpDevice *dev)
|
||||
{
|
||||
FpDeviceEgis0570 *self = FPI_DEVICE_EGIS0570 (dev);
|
||||
|
||||
int type = self->pkt_type;
|
||||
int num = self->pkt_num;
|
||||
|
||||
gboolean r;
|
||||
|
||||
r = ((type == PKT_TYPE_INIT) && (num == (EGIS0570_INIT_TOTAL - 1)));
|
||||
r |= ((type == PKT_TYPE_REPEAT) && (num == (EGIS0570_REPEAT_TOTAL - 1)));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a bit for each frame on whether or not a finger has been detected.
|
||||
* e.g. 00110 means that there is a finger in frame two and three.
|
||||
*/
|
||||
static char
|
||||
postprocess_frames (FpDeviceEgis0570 *self, guint8 * img)
|
||||
{
|
||||
size_t mean[EGIS0570_IMGCOUNT] = {0, 0, 0, 0, 0};
|
||||
|
||||
if (!self->background)
|
||||
{
|
||||
self->background = g_malloc (EGIS0570_IMGWIDTH * EGIS0570_RFMGHEIGHT);
|
||||
memset (self->background, 255, EGIS0570_IMGWIDTH * EGIS0570_RFMGHEIGHT);
|
||||
|
||||
for (size_t k = 0; k < EGIS0570_IMGCOUNT; k += 1)
|
||||
{
|
||||
guint8 * frame = &img[(k * EGIS0570_IMGSIZE) + EGIS0570_RFMDIS * EGIS0570_IMGWIDTH];
|
||||
|
||||
for (size_t i = 0; i < EGIS0570_IMGWIDTH * EGIS0570_RFMGHEIGHT; i += 1)
|
||||
self->background[i] = MIN (self->background[i], frame[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t k = 0; k < EGIS0570_IMGCOUNT; k += 1)
|
||||
{
|
||||
guint8 * frame = &img[(k * EGIS0570_IMGSIZE) + EGIS0570_RFMDIS * EGIS0570_IMGWIDTH];
|
||||
|
||||
for (size_t i = 0; i < EGIS0570_IMGWIDTH * EGIS0570_RFMGHEIGHT; i += 1)
|
||||
{
|
||||
if (frame[i] - EGIS0570_MARGIN > self->background[i])
|
||||
frame[i] -= self->background[i];
|
||||
else
|
||||
frame[i] = 0;
|
||||
|
||||
mean[k] += frame[i];
|
||||
}
|
||||
|
||||
mean[k] /= EGIS0570_IMGWIDTH * EGIS0570_RFMGHEIGHT;
|
||||
}
|
||||
|
||||
char result = 0;
|
||||
|
||||
for (size_t k = 0; k < EGIS0570_IMGCOUNT; k += 1)
|
||||
{
|
||||
fp_dbg ("Finger status (picture number, mean) : %ld , %ld", k, mean[k]);
|
||||
if (mean[k] > EGIS0570_MIN_MEAN)
|
||||
result |= 1 << k;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Device communication
|
||||
*/
|
||||
|
||||
static void
|
||||
data_resp_cb (FpiUsbTransfer *transfer, FpDevice *dev, gpointer user_data, GError *error)
|
||||
{
|
||||
unsigned char *stripdata;
|
||||
gboolean end = FALSE;
|
||||
FpImageDevice *img_self = FP_IMAGE_DEVICE (dev);
|
||||
FpDeviceEgis0570 *self = FPI_DEVICE_EGIS0570 (dev);
|
||||
|
||||
if (error)
|
||||
{
|
||||
fpi_ssm_mark_failed (transfer->ssm, error);
|
||||
return;
|
||||
}
|
||||
|
||||
int where_finger_is = postprocess_frames (self, transfer->buffer);
|
||||
|
||||
if (where_finger_is > 0)
|
||||
{
|
||||
FpiImageDeviceState state;
|
||||
|
||||
fpi_image_device_report_finger_status (img_self, TRUE);
|
||||
|
||||
g_object_get (dev, "fpi-image-device-state", &state, NULL);
|
||||
if (state == FPI_IMAGE_DEVICE_STATE_CAPTURE)
|
||||
{
|
||||
for (size_t k = 0; k < EGIS0570_IMGCOUNT; k += 1)
|
||||
{
|
||||
if (where_finger_is & (1 << k))
|
||||
{
|
||||
struct fpi_frame *stripe = g_malloc (EGIS0570_IMGWIDTH * EGIS0570_RFMGHEIGHT + sizeof (struct fpi_frame));
|
||||
stripe->delta_x = 0;
|
||||
stripe->delta_y = 0;
|
||||
stripdata = stripe->data;
|
||||
memcpy (stripdata, (transfer->buffer) + (((k) * EGIS0570_IMGSIZE) + EGIS0570_IMGWIDTH * EGIS0570_RFMDIS), EGIS0570_IMGWIDTH * EGIS0570_RFMGHEIGHT);
|
||||
self->strips = g_slist_prepend (self->strips, stripe);
|
||||
self->strips_len += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
end = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
end = TRUE;
|
||||
}
|
||||
|
||||
if (end)
|
||||
{
|
||||
if (!self->stop && (self->strips_len > 0))
|
||||
{
|
||||
FpImage *img;
|
||||
self->strips = g_slist_reverse (self->strips);
|
||||
fpi_do_movement_estimation (&assembling_ctx, self->strips);
|
||||
img = fpi_assemble_frames (&assembling_ctx, self->strips);
|
||||
img->flags |= (FPI_IMAGE_COLORS_INVERTED | FPI_IMAGE_PARTIAL);
|
||||
g_slist_free_full (self->strips, g_free);
|
||||
self->strips = NULL;
|
||||
self->strips_len = 0;
|
||||
FpImage *resizeImage = fpi_image_resize (img, EGIS0570_RESIZE, EGIS0570_RESIZE);
|
||||
fpi_image_device_image_captured (img_self, resizeImage);
|
||||
}
|
||||
|
||||
fpi_image_device_report_finger_status (img_self, FALSE);
|
||||
}
|
||||
|
||||
fpi_ssm_next_state (transfer->ssm);
|
||||
}
|
||||
|
||||
static void
|
||||
recv_data_resp (FpiSsm *ssm, FpDevice *dev)
|
||||
{
|
||||
FpiUsbTransfer *transfer = fpi_usb_transfer_new (dev);
|
||||
|
||||
fpi_usb_transfer_fill_bulk (transfer, EGIS0570_EPIN, EGIS0570_INPSIZE);
|
||||
|
||||
transfer->ssm = ssm;
|
||||
transfer->short_is_error = TRUE;
|
||||
|
||||
fpi_usb_transfer_submit (transfer, EGIS0570_TIMEOUT, NULL, data_resp_cb, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_resp_cb (FpiUsbTransfer *transfer, FpDevice *dev, gpointer user_data, GError *error)
|
||||
{
|
||||
if (error)
|
||||
fpi_ssm_mark_failed (transfer->ssm, error);
|
||||
}
|
||||
|
||||
static void
|
||||
recv_cmd_resp (FpiSsm *ssm, FpDevice *dev)
|
||||
{
|
||||
FpiUsbTransfer *transfer = fpi_usb_transfer_new (dev);
|
||||
|
||||
fpi_usb_transfer_fill_bulk (transfer, EGIS0570_EPIN, EGIS0570_PKTSIZE);
|
||||
|
||||
transfer->ssm = ssm;
|
||||
|
||||
fpi_usb_transfer_submit (transfer, EGIS0570_TIMEOUT, NULL, cmd_resp_cb, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
send_cmd_req (FpiSsm *ssm, FpDevice *dev, unsigned char *pkt)
|
||||
{
|
||||
FpiUsbTransfer *transfer = fpi_usb_transfer_new (dev);
|
||||
|
||||
fpi_usb_transfer_fill_bulk_full (transfer, EGIS0570_EPOUT, pkt, EGIS0570_PKTSIZE, NULL);
|
||||
|
||||
transfer->ssm = ssm;
|
||||
transfer->short_is_error = TRUE;
|
||||
|
||||
fpi_usb_transfer_submit (transfer, EGIS0570_TIMEOUT, NULL, fpi_ssm_usb_transfer_cb, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* SSM States
|
||||
*/
|
||||
|
||||
enum sm_states {
|
||||
SM_INIT,
|
||||
SM_START,
|
||||
SM_REQ,
|
||||
SM_RESP,
|
||||
SM_REC_DATA,
|
||||
SM_DONE,
|
||||
SM_STATES_NUM
|
||||
};
|
||||
|
||||
static void
|
||||
ssm_run_state (FpiSsm *ssm, FpDevice *dev)
|
||||
{
|
||||
FpDeviceEgis0570 *self = FPI_DEVICE_EGIS0570 (dev);
|
||||
FpImageDevice *img_dev = FP_IMAGE_DEVICE (dev);
|
||||
|
||||
switch (fpi_ssm_get_cur_state (ssm))
|
||||
{
|
||||
case SM_INIT:
|
||||
self->pkt_type = PKT_TYPE_INIT;
|
||||
fpi_ssm_next_state (ssm);
|
||||
break;
|
||||
|
||||
case SM_START:
|
||||
if (self->stop)
|
||||
{
|
||||
fp_dbg ("deactivating, marking completed");
|
||||
fpi_ssm_mark_completed (ssm);
|
||||
fpi_image_device_deactivate_complete (img_dev, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->pkt_num = 0;
|
||||
fpi_ssm_next_state (ssm);
|
||||
}
|
||||
break;
|
||||
|
||||
case SM_REQ:
|
||||
if (self->pkt_type == PKT_TYPE_INIT)
|
||||
send_cmd_req (ssm, dev, init_pkts[self->pkt_num]);
|
||||
else
|
||||
send_cmd_req (ssm, dev, repeat_pkts[self->pkt_num]);
|
||||
break;
|
||||
|
||||
case SM_RESP:
|
||||
if (is_last_pkt (dev) == FALSE)
|
||||
{
|
||||
recv_cmd_resp (ssm, dev);
|
||||
self->pkt_num += 1;
|
||||
fpi_ssm_jump_to_state (ssm, SM_REQ);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->pkt_type == PKT_TYPE_INIT)
|
||||
self->pkt_type = PKT_TYPE_REPEAT;
|
||||
|
||||
fpi_ssm_next_state (ssm);
|
||||
}
|
||||
break;
|
||||
|
||||
case SM_REC_DATA:
|
||||
recv_data_resp (ssm, dev);
|
||||
break;
|
||||
|
||||
case SM_DONE:
|
||||
fpi_ssm_jump_to_state (ssm, SM_START);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Activation
|
||||
*/
|
||||
|
||||
static void
|
||||
loop_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
|
||||
{
|
||||
FpImageDevice *img_dev = FP_IMAGE_DEVICE (dev);
|
||||
FpDeviceEgis0570 *self = FPI_DEVICE_EGIS0570 (dev);
|
||||
|
||||
self->running = FALSE;
|
||||
g_clear_pointer (&self->background, g_free);
|
||||
|
||||
if (error)
|
||||
fpi_image_device_session_error (img_dev, error);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_activate (FpImageDevice *dev)
|
||||
{
|
||||
FpDeviceEgis0570 *self = FPI_DEVICE_EGIS0570 (dev);
|
||||
FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (dev), ssm_run_state, SM_STATES_NUM);
|
||||
|
||||
self->stop = FALSE;
|
||||
|
||||
fpi_ssm_start (ssm, loop_complete);
|
||||
|
||||
self->running = TRUE;
|
||||
|
||||
fpi_image_device_activate_complete (dev, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opening
|
||||
*/
|
||||
|
||||
static void
|
||||
dev_init (FpImageDevice *dev)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_usb_device_claim_interface (fpi_device_get_usb_device (FP_DEVICE (dev)), 0, 0, &error);
|
||||
|
||||
fpi_image_device_open_complete (dev, error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Closing
|
||||
*/
|
||||
|
||||
static void
|
||||
dev_deinit (FpImageDevice *dev)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (dev)), 0, 0, &error);
|
||||
|
||||
fpi_image_device_close_complete (dev, error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deactivation
|
||||
*/
|
||||
|
||||
static void
|
||||
dev_deactivate (FpImageDevice *dev)
|
||||
{
|
||||
FpDeviceEgis0570 *self = FPI_DEVICE_EGIS0570 (dev);
|
||||
|
||||
if (self->running)
|
||||
self->stop = TRUE;
|
||||
else
|
||||
fpi_image_device_deactivate_complete (dev, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Driver data
|
||||
*/
|
||||
|
||||
static const FpIdEntry id_table[] = {
|
||||
{ .vid = 0x1c7a, .pid = 0x0570, },
|
||||
{ .vid = 0x1c7a, .pid = 0x0571, },
|
||||
{ .vid = 0, .pid = 0, },
|
||||
};
|
||||
|
||||
static void
|
||||
fpi_device_egis0570_init (FpDeviceEgis0570 *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_device_egis0570_class_init (FpDeviceEgis0570Class *klass)
|
||||
{
|
||||
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
||||
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
|
||||
|
||||
dev_class->id = "egis0570";
|
||||
dev_class->full_name = "Egis Technology Inc. (aka. LighTuning) 0570";
|
||||
dev_class->type = FP_DEVICE_TYPE_USB;
|
||||
dev_class->id_table = id_table;
|
||||
dev_class->scan_type = FP_SCAN_TYPE_SWIPE;
|
||||
|
||||
img_class->img_open = dev_init;
|
||||
img_class->img_close = dev_deinit;
|
||||
img_class->activate = dev_activate;
|
||||
img_class->deactivate = dev_deactivate;
|
||||
|
||||
img_class->img_width = EGIS0570_IMGWIDTH;
|
||||
img_class->img_height = -1;
|
||||
|
||||
img_class->bz3_threshold = EGIS0570_BZ3_THRESHOLD; /* security issue */
|
||||
}
|
177
libfprint/drivers/egis0570.h
Normal file
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Egis Technology Inc. (aka. LighTuning) 0570 driver for libfprint
|
||||
* Copyright (C) 2021 Maxim Kolesnikov <kolesnikov@svyazcom.ru>
|
||||
* Copyright (C) 2021 Saeed/Ali Rk <saeed.ali.rahimi@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 __EGIS0570_H
|
||||
|
||||
#define __EGIS0570_H 1
|
||||
|
||||
/*
|
||||
* Device data
|
||||
*/
|
||||
|
||||
#define EGIS0570_CONF 1
|
||||
#define EGIS0570_INTF 0
|
||||
|
||||
/*
|
||||
* Device endpoints
|
||||
*/
|
||||
|
||||
#define EGIS0570_EPOUT 0x04 /* ( 4 | FPI_USB_ENDPOINT_OUT ) */
|
||||
#define EGIS0570_EPIN 0x83 /* ( 3 | FPI_USB_ENDPOINT_IN ) */
|
||||
|
||||
/*
|
||||
* Initialization packets (7 bytes each)
|
||||
*
|
||||
* First 4 bytes are equivalent to string "EGIS", which must be just a company identificator
|
||||
* Other 3 bytes are not recognized yet and may be not important, as they are always the same
|
||||
|
||||
* Answers for each packet contain 7 bytes again
|
||||
* First 4 bytes are reversed "EGIS", which is "SIGE", which is company ID again
|
||||
* Other 3 bytes are not recognized yet
|
||||
* But there is a pattern.
|
||||
* Sending last packet makes sensor return image
|
||||
*/
|
||||
|
||||
#define EGIS0570_TIMEOUT 10000
|
||||
#define EGIS0570_PKTSIZE 7
|
||||
|
||||
#define EGIS0570_INIT_TOTAL (sizeof ((init_pkts)) / sizeof ((init_pkts[0])))
|
||||
|
||||
static unsigned char init_pkts[][EGIS0570_PKTSIZE] =
|
||||
{
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x20, 0x3f },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x58, 0x3f },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x21, 0x09 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x57, 0x09 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x22, 0x03 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x56, 0x03 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x23, 0x01 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x55, 0x01 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x24, 0x01 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x54, 0x01 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x16, 0x3e },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x09, 0x0b },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x14, 0x03 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x15, 0x00 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x02, 0x0f },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x10, 0x00 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x11, 0x38 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x12, 0x00 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x13, 0x71 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x03, 0x80 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x00, 0x02, 0x80 },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x02, 0x2f },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x06, 0x00, 0xfe } /* image returned after this packet */
|
||||
};
|
||||
|
||||
/* There is another Packet !
|
||||
* That just Work the same !!
|
||||
* And the Size is different !!!
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
#define EGIS0570_INIT_TOTAL2 (sizeof((init_pkts2)) / sizeof((init_pkts2[0])))
|
||||
|
||||
static unsigned char init_pkts2[][EGIS0570_PKTSIZE] =
|
||||
{
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x10, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x11, 0x38},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x12, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x13, 0x71},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x20, 0x3f},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x58, 0x3f},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x21, 0x07},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x57, 0x07},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x22, 0x02},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x56, 0x02},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x23, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x55, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x24, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x54, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x25, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x53, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x15, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x16, 0x3b},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x09, 0x0a},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x14, 0x00},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x02, 0x0f},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x03, 0x80},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x00, 0x02, 0x80},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x01, 0x02, 0x2f},
|
||||
{0x45, 0x47, 0x49, 0x53, 0x06, 0x00, 0xfe}
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* After sending initial packets device returns image data (32512 bytes)
|
||||
* To ask device to send image data again, host needs to send four additional packets
|
||||
* Further work is to repeatedly send four repeat packets and read image data
|
||||
*/
|
||||
|
||||
#define EGIS0570_INPSIZE 32512
|
||||
|
||||
/* 5 image with captured in different time of size 114 * 57 = 6498
|
||||
* 5 * 6498 = 32490 plus 22 extra unrecognized char size data
|
||||
* Two continuous image in this 5 images may have time delay of less than 20ms
|
||||
*/
|
||||
|
||||
#define EGIS0570_IMGSIZE 6498
|
||||
#define EGIS0570_IMGWIDTH 114
|
||||
#define EGIS0570_IMGHEIGHT 57
|
||||
|
||||
/* size of middle area that is used from each frame */
|
||||
#define EGIS0570_RFMGHEIGHT 17
|
||||
/* rows to ignore from top and bottom of the image*/
|
||||
#define EGIS0570_RFMDIS (EGIS0570_IMGHEIGHT - EGIS0570_RFMGHEIGHT) / 2
|
||||
#define EGIS0570_IMGCOUNT 5
|
||||
|
||||
/*
|
||||
* Image repeat request
|
||||
* First 4 bytes are the same as in initialization packets
|
||||
* Have no idea what the other 3 bytes mean
|
||||
*/
|
||||
|
||||
#define EGIS0570_REPEAT_TOTAL (sizeof ((repeat_pkts)) / sizeof ((repeat_pkts[0])))
|
||||
|
||||
static unsigned char repeat_pkts[][EGIS0570_PKTSIZE] =
|
||||
{
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x02, 0x0f },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x00, 0x02, 0x0f },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x01, 0x02, 0x2f },
|
||||
{ 0x45, 0x47, 0x49, 0x53, 0x06, 0x00, 0xfe } /* image returned after this packet */
|
||||
};
|
||||
|
||||
/*
|
||||
* This sensor is small so I decided to reduce bz3_threshold from
|
||||
* 40 to 10 to have more success to fail ratio
|
||||
* Bozorth3 Algorithm seems not fine at the end
|
||||
* foreget about security :))
|
||||
*/
|
||||
|
||||
#define EGIS0570_BZ3_THRESHOLD 25 /* and even less What a joke */
|
||||
|
||||
#define EGIS0570_MIN_MEAN 20
|
||||
#define EGIS0570_MARGIN 3
|
||||
|
||||
#define EGIS0570_RESIZE 2
|
||||
|
||||
#endif
|
1010
libfprint/drivers/elan.c
Normal file
234
libfprint/drivers/elan.h
Normal file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.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 | FPI_USB_ENDPOINT_OUT)
|
||||
#define ELAN_EP_CMD_IN (0x3 | FPI_USB_ENDPOINT_IN)
|
||||
#define ELAN_EP_IMG_IN (0x2 | FPI_USB_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
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FpiDeviceElan, fpi_device_elan, FPI, DEVICE_ELAN,
|
||||
FpImageDevice);
|
||||
|
||||
struct elan_cmd
|
||||
{
|
||||
unsigned char cmd[ELAN_CMD_LEN];
|
||||
int response_len;
|
||||
int response_in;
|
||||
unsigned short devices;
|
||||
gboolean never_cancel;
|
||||
};
|
||||
|
||||
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,
|
||||
.never_cancel = TRUE,
|
||||
};
|
||||
|
||||
static const FpIdEntry elan_id_table[] = {
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0903, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0907, .driver_data = ELAN_0907},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c01, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c02, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c03, .driver_data = ELAN_0C03},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c04, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c05, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c06, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c07, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c08, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c09, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c0a, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c0b, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c0c, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c0d, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c0e, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c0f, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c10, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c11, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c12, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c13, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c14, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c15, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c16, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c17, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c18, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c19, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c1a, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c1b, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c1c, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c1d, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c1e, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c1f, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c20, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c21, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c22, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c23, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c24, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c25, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c26, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c27, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c28, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c29, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c2a, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c2b, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c2c, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c2d, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c2e, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c2f, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c30, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c31, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c32, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c33, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c3d, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c42, .driver_data = ELAN_0C42},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c4d, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c4f, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c63, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c6e, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = ELAN_VEND_ID, .pid = 0x0c58, .driver_data = ELAN_ALL_DEV},
|
||||
{.vid = 0, .pid = 0, .driver_data = 0},
|
||||
};
|
||||
|
||||
static void elan_cmd_done (FpiSsm *ssm);
|
||||
static void elan_cmd_read (FpiSsm *ssm,
|
||||
FpDevice *dev);
|
||||
|
||||
static void elan_calibrate (FpiDeviceElan *self);
|
||||
static void elan_capture (FpiDeviceElan *self);
|
||||
|
||||
static void dev_change_state (FpImageDevice *dev,
|
||||
FpiImageDeviceState state);
|
1139
libfprint/drivers/elanmoc/elanmoc.c
Normal file
195
libfprint/drivers/elanmoc/elanmoc.h
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Elan Microelectronics
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fpi-device.h"
|
||||
#include "fpi-ssm.h"
|
||||
#include <libusb.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FpiDeviceElanmoc, fpi_device_elanmoc, FPI, DEVICE_ELANMOC, FpDevice)
|
||||
|
||||
#define ELAN_MOC_DRIVER_FULLNAME "Elan MOC Sensors"
|
||||
#define ELAN_M0C_CMD_LEN 0x3
|
||||
#define ELAN_EP_CMD_OUT (0x1 | LIBUSB_ENDPOINT_OUT)
|
||||
#define ELAN_EP_CMD_IN (0x3 | LIBUSB_ENDPOINT_IN)
|
||||
#define ELAN_EP_MOC_CMD_IN (0x4 | LIBUSB_ENDPOINT_IN)
|
||||
#define ELAN_EP_IMG_IN (0x2 | LIBUSB_ENDPOINT_IN)
|
||||
|
||||
#define ELAN_MOC_CMD_TIMEOUT 5000
|
||||
#define ELAN_MOC_CAL_RETRY 500
|
||||
#define ELAN_MOC_ENROLL_TIMES 9
|
||||
#define ELAN_MAX_USER_ID_LEN 92
|
||||
#define ELAN_MAX_ENROLL_NUM 9
|
||||
|
||||
#define ELAN_MSG_VERIFY_ERR 0xfd
|
||||
#define ELAN_MSG_DIRTY 0xfb
|
||||
#define ELAN_MSG_AREA_NOT_ENOUGH 0xfe
|
||||
#define ELAN_MSG_TOO_HIGH 0x41
|
||||
#define ELAN_MSG_TOO_LEFT 0x42
|
||||
#define ELAN_MSG_TOO_LOW 0x43
|
||||
#define ELAN_MSG_TOO_RIGHT 0x44
|
||||
#define ELAN_MSG_OK 0x00
|
||||
|
||||
#define ELAN_MAX_HDR_LEN 3
|
||||
#define ELAN_USERDATE_SIZE (ELAN_MAX_USER_ID_LEN + 3)
|
||||
|
||||
#define ELAN_MSG_DRIVER_VERSION "1004"
|
||||
|
||||
struct elanmoc_cmd
|
||||
{
|
||||
unsigned char cmd_header[ELAN_MAX_HDR_LEN];
|
||||
int cmd_len;
|
||||
int resp_len;
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd fw_ver_cmd = {
|
||||
.cmd_header = {0x40, 0x19},
|
||||
.cmd_len = 2,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd sensor_dim_cmd = {
|
||||
.cmd_header = {0x00, 0x0c},
|
||||
.cmd_len = 2,
|
||||
.resp_len = 4,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd cal_status_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x00},
|
||||
.cmd_len = 3,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd enrolled_number_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x04},
|
||||
.cmd_len = 3,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd elanmoc_verify_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x73},
|
||||
.cmd_len = 5,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd elanmoc_above_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x02},
|
||||
.cmd_len = 3,
|
||||
.resp_len = 0,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd elanmoc_enroll_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x01},
|
||||
.cmd_len = 7,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd elanmoc_delete_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x13},
|
||||
.cmd_len = 128,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd elanmoc_enroll_commit_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x11},
|
||||
.cmd_len = 128,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd elanmoc_remove_all_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x98},
|
||||
.cmd_len = 3,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd elanmoc_get_userid_cmd = {
|
||||
.cmd_header = {0x43, 0x21, 0x00},
|
||||
.cmd_len = 3,
|
||||
.resp_len = 97,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd elanmoc_set_mod_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x14},
|
||||
.cmd_len = 4,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
static const struct elanmoc_cmd elanmoc_check_reenroll_cmd = {
|
||||
.cmd_header = {0x40, 0xff, 0x22},
|
||||
.cmd_len = 3 + ELAN_USERDATE_SIZE,
|
||||
.resp_len = 2,
|
||||
};
|
||||
|
||||
typedef void (*ElanCmdMsgCallback) (FpiDeviceElanmoc *self,
|
||||
GError *error);
|
||||
|
||||
enum moc_enroll_states {
|
||||
MOC_ENROLL_GET_ENROLLED_NUM,
|
||||
MOC_ENROLL_REENROLL_CHECK,
|
||||
MOC_ENROLL_WAIT_FINGER,
|
||||
MOC_ENROLL_COMMIT_RESULT,
|
||||
MOC_ENROLL_NUM_STATES,
|
||||
};
|
||||
|
||||
enum moc_list_states {
|
||||
MOC_LIST_GET_ENROLLED,
|
||||
MOC_LIST_GET_FINGER,
|
||||
MOC_LIST_NUM_STATES,
|
||||
};
|
||||
|
||||
enum delete_states {
|
||||
DELETE_SEND_CMD,
|
||||
DELETE_NUM_STATES,
|
||||
};
|
||||
|
||||
enum dev_init_states {
|
||||
DEV_WAIT_READY,
|
||||
DEV_SET_MODE,
|
||||
DEV_GET_VER,
|
||||
DEV_GET_DIM,
|
||||
DEV_GET_ENROLLED,
|
||||
DEV_INIT_STATES,
|
||||
};
|
||||
|
||||
enum dev_exit_states {
|
||||
DEV_EXIT_ABOVE,
|
||||
DEV_EXIT_STATES,
|
||||
};
|
||||
|
||||
struct _FpiDeviceElanmoc
|
||||
{
|
||||
FpDevice parent;
|
||||
FpiSsm *task_ssm;
|
||||
FpiSsm *cmd_ssm;
|
||||
FpiUsbTransfer *cmd_transfer;
|
||||
gboolean cmd_cancelable;
|
||||
gsize cmd_len_in;
|
||||
unsigned short fw_ver;
|
||||
unsigned char x_trace;
|
||||
unsigned char y_trace;
|
||||
int num_frames;
|
||||
int curr_enrolled;
|
||||
int cancel_result;
|
||||
int cmd_retry_cnt;
|
||||
int list_index;
|
||||
GPtrArray *list_result;
|
||||
};
|
1700
libfprint/drivers/elanspi.c
Normal file
351
libfprint/drivers/elanspi.h
Normal file
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
* Elan SPI driver for libfprint
|
||||
*
|
||||
* Copyright (C) 2021 Matthew Mirvish <matthew@mm12.xyz>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifndef HAVE_UDEV
|
||||
#error "elanspi requires udev"
|
||||
#endif
|
||||
|
||||
#include <fp-device.h>
|
||||
#include <fpi-device.h>
|
||||
|
||||
#define ELANSPI_TP_PID 0x04f3
|
||||
|
||||
/* Sensor ID information copied from the windows driver */
|
||||
|
||||
struct elanspi_sensor_entry
|
||||
{
|
||||
unsigned char sensor_id, height, width, ic_version;
|
||||
gboolean is_otp_model;
|
||||
const gchar * name;
|
||||
};
|
||||
|
||||
static const struct elanspi_sensor_entry elanspi_sensor_table[] = {
|
||||
{0x0, 0x78, 0x78, 0x0, 0x0, "eFSA120S"},
|
||||
{0x1, 0x78, 0x78, 0x1, 0x1, "eFSA120SA"},
|
||||
{0x2, 0xA0, 0xA0, 0x0, 0x0, "eFSA160S"},
|
||||
{0x3, 0xd0, 0x50, 0x0, 0x0, "eFSA820R"},
|
||||
{0x4, 0xC0, 0x38, 0x0, 0x0, "eFSA519R"},
|
||||
{0x5, 0x60, 0x60, 0x0, 0x0, "eFSA96S"},
|
||||
{0x6, 0x60, 0x60, 0x1, 0x1, "eFSA96SA"},
|
||||
{0x7, 0x60, 0x60, 0x2, 0x1, "eFSA96SB"},
|
||||
{0x8, 0xa0, 0x50, 0x1, 0x1, "eFSA816RA"},
|
||||
{0x9, 0x90, 0x40, 0x1, 0x1, "eFSA614RA"},
|
||||
{0xA, 0x90, 0x40, 0x2, 0x1, "eFSA614RB"},
|
||||
{0xB, 0x40, 0x58, 0x1, 0x1, "eFSA688RA"},
|
||||
{0xC, 0x50, 0x50, 0x1, 0x0, "eFSA80SA"},
|
||||
{0xD, 0x47, 0x80, 0x1, 0x1, "eFSA712RA"},
|
||||
{0xE, 0x50, 0x50, 0x2, 0x0, "eFSA80SC"},
|
||||
{0, 0, 0, 0, 0, NULL}
|
||||
};
|
||||
|
||||
struct elanspi_reg_entry
|
||||
{
|
||||
unsigned char addr, value;
|
||||
/* terminates with 0xFF, 0xFF since register 0x0 is valid */
|
||||
};
|
||||
|
||||
struct elanspi_regtable
|
||||
{
|
||||
const struct elanspi_reg_entry *other;
|
||||
struct
|
||||
{
|
||||
unsigned char sid;
|
||||
const struct elanspi_reg_entry *table;
|
||||
} entries[];
|
||||
};
|
||||
|
||||
static const struct elanspi_reg_entry elanspi_calibration_table_default[] = {
|
||||
{0x05, 0x60},
|
||||
{0x06, 0xc0},
|
||||
{0x07, 0x80},
|
||||
{0x08, 0x04},
|
||||
{0x0a, 0x97},
|
||||
{0x0b, 0x72},
|
||||
{0x0c, 0x69},
|
||||
{0x0f, 0x2a},
|
||||
{0x11, 0x2a},
|
||||
{0x13, 0x27},
|
||||
{0x15, 0x67},
|
||||
{0x18, 0x04},
|
||||
{0x21, 0x20},
|
||||
{0x22, 0x36},
|
||||
{0x2a, 0x5f},
|
||||
{0x2b, 0xc0},
|
||||
{0x2e, 0xff},
|
||||
|
||||
{0xff, 0xff}
|
||||
};
|
||||
|
||||
static const struct elanspi_reg_entry elanspi_calibration_table_id567[] = {
|
||||
{0x2A, 0x07},
|
||||
{0x5, 0x60},
|
||||
{0x6, 0xC0},
|
||||
{0x7, 0x80},
|
||||
{0x8, 0x04},
|
||||
{0xA, 0x97},
|
||||
{0xB, 0x72},
|
||||
{0xC, 0x69},
|
||||
{0xF, 0x2A},
|
||||
{0x11, 0x2A},
|
||||
{0x13, 0x27},
|
||||
{0x15, 0x67},
|
||||
{0x18, 0x04},
|
||||
{0x21, 0x20},
|
||||
{0x22, 0x36},
|
||||
{0x2A, 0x5F},
|
||||
{0x2B, 0xC0},
|
||||
{0x2E, 0xFF},
|
||||
|
||||
{0xff, 0xff}
|
||||
};
|
||||
|
||||
static const struct elanspi_reg_entry elanspi_calibration_table_id0[] = {
|
||||
{0x5, 0x60},
|
||||
{0x6, 0xC0},
|
||||
{0x8, 0x04},
|
||||
{0xA, 0x97},
|
||||
{0xB, 0x72},
|
||||
{0xC, 0x69},
|
||||
{0xF, 0x2B},
|
||||
{0x11, 0x2B},
|
||||
{0x13, 0x28},
|
||||
{0x15, 0x28},
|
||||
{0x18, 0x04},
|
||||
{0x21, 0x20},
|
||||
{0x2A, 0x4B},
|
||||
|
||||
{0xff, 0xff}
|
||||
};
|
||||
|
||||
// old style sensor calibration, with only one page of registers
|
||||
static const struct elanspi_regtable elanspi_calibration_table_old = {
|
||||
.other = elanspi_calibration_table_default,
|
||||
.entries = {
|
||||
{ .sid = 0x0, .table = elanspi_calibration_table_id0 },
|
||||
{ .sid = 0x5, .table = elanspi_calibration_table_id567 },
|
||||
{ .sid = 0x6, .table = elanspi_calibration_table_id567 },
|
||||
{ .sid = 0x7, .table = elanspi_calibration_table_id567 },
|
||||
{ .sid = 0x0, .table = NULL }
|
||||
}
|
||||
};
|
||||
|
||||
// new style sensor calibration, with two pages of registers
|
||||
static const struct elanspi_reg_entry elanspi_calibration_table_page0_id14[] = {
|
||||
{0x00, 0x5a},
|
||||
{0x01, 0x00},
|
||||
{0x02, 0x4f},
|
||||
{0x03, 0x00},
|
||||
{0x04, 0x4f},
|
||||
{0x05, 0xa0},
|
||||
{0x06, 0x00},
|
||||
{0x07, 0x00},
|
||||
{0x08, 0x00},
|
||||
{0x09, 0x04},
|
||||
{0x0a, 0x74},
|
||||
{0x0b, 0x05},
|
||||
{0x0c, 0x08},
|
||||
{0x0d, 0x00},
|
||||
{0x0e, 0x00},
|
||||
{0x0f, 0x14},
|
||||
{0x10, 0x3c},
|
||||
{0x11, 0x41},
|
||||
{0x12, 0x0c},
|
||||
{0x13, 0x00},
|
||||
{0x14, 0x00},
|
||||
{0x15, 0x04},
|
||||
{0x16, 0x02},
|
||||
{0x17, 0x00},
|
||||
{0x18, 0x01},
|
||||
{0x19, 0xf4},
|
||||
{0x1a, 0x00},
|
||||
{0x1b, 0x00},
|
||||
{0x1c, 0x00},
|
||||
{0x1d, 0x00},
|
||||
{0x1e, 0x00},
|
||||
{0x1f, 0x00},
|
||||
{0x20, 0x00},
|
||||
{0x21, 0x80},
|
||||
{0x22, 0x06},
|
||||
{0x23, 0x00},
|
||||
{0x24, 0x00},
|
||||
{0x25, 0x00},
|
||||
{0x26, 0x00},
|
||||
{0x27, 0x00},
|
||||
{0x28, 0x00},
|
||||
{0x29, 0x04},
|
||||
{0x2a, 0x5f},
|
||||
{0x2b, 0xe2},
|
||||
{0x2c, 0xa0},
|
||||
{0x2d, 0x00},
|
||||
{0x2e, 0xff},
|
||||
{0x2f, 0x40},
|
||||
{0x30, 0x01},
|
||||
{0x31, 0x38},
|
||||
{0x32, 0x00},
|
||||
{0x33, 0x00},
|
||||
{0x34, 0x00},
|
||||
{0x35, 0x1f},
|
||||
{0x36, 0xff},
|
||||
{0x37, 0x00},
|
||||
{0x38, 0x00},
|
||||
{0x39, 0x00},
|
||||
{0x3a, 0x00},
|
||||
{0xff, 0xff}
|
||||
};
|
||||
|
||||
static const struct elanspi_reg_entry elanspi_calibration_table_page1_id14[] = {
|
||||
{0x00, 0x7b},
|
||||
{0x01, 0x7f},
|
||||
{0x02, 0x77},
|
||||
{0x03, 0xd4},
|
||||
{0x04, 0x7d},
|
||||
{0x05, 0x19},
|
||||
{0x06, 0x80},
|
||||
{0x07, 0x40},
|
||||
{0x08, 0x11},
|
||||
{0x09, 0x00},
|
||||
{0x0a, 0x00},
|
||||
{0x0b, 0x14},
|
||||
{0x0c, 0x00},
|
||||
{0x0d, 0x00},
|
||||
{0x0e, 0x32},
|
||||
{0x0f, 0x02},
|
||||
{0x10, 0x08},
|
||||
{0x11, 0x6c},
|
||||
{0x12, 0x00},
|
||||
{0x13, 0x00},
|
||||
{0x14, 0x32},
|
||||
{0x15, 0x01},
|
||||
{0x16, 0x16},
|
||||
{0x17, 0x01},
|
||||
{0x18, 0x14},
|
||||
{0x19, 0x01},
|
||||
{0x1a, 0x16},
|
||||
{0x1b, 0x01},
|
||||
{0x1c, 0x17},
|
||||
{0x1d, 0x01},
|
||||
{0x1e, 0x0a},
|
||||
{0x1f, 0x01},
|
||||
{0x20, 0x0a},
|
||||
{0x21, 0x02},
|
||||
{0x22, 0x08},
|
||||
{0x23, 0x29},
|
||||
{0x24, 0x00},
|
||||
{0x25, 0x0c},
|
||||
{0x26, 0x1a},
|
||||
{0x27, 0x30},
|
||||
{0x28, 0x1a},
|
||||
{0x29, 0x30},
|
||||
{0x2a, 0x00},
|
||||
{0x2b, 0x00},
|
||||
{0x2c, 0x01},
|
||||
{0x2d, 0x16},
|
||||
{0x2e, 0x01},
|
||||
{0x2f, 0x17},
|
||||
{0x30, 0x03},
|
||||
{0x31, 0x2d},
|
||||
{0x32, 0x03},
|
||||
{0x33, 0x2d},
|
||||
{0x34, 0x14},
|
||||
{0x35, 0x00},
|
||||
{0x36, 0x00},
|
||||
{0x37, 0x00},
|
||||
{0x38, 0x00},
|
||||
{0x39, 0x03},
|
||||
{0x3a, 0xfe},
|
||||
{0x3b, 0x00},
|
||||
{0x3c, 0x00},
|
||||
{0x3d, 0x02},
|
||||
{0x3e, 0x00},
|
||||
{0x3f, 0x00},
|
||||
{0xff, 0xff}
|
||||
};
|
||||
|
||||
static const struct elanspi_regtable elanspi_calibration_table_new_page0 = {
|
||||
.other = NULL,
|
||||
.entries = {
|
||||
{ .sid = 0xe, .table = elanspi_calibration_table_page0_id14 },
|
||||
{ .sid = 0x0, .table = NULL }
|
||||
}
|
||||
};
|
||||
|
||||
static const struct elanspi_regtable elanspi_calibration_table_new_page1 = {
|
||||
.other = NULL,
|
||||
.entries = {
|
||||
{ .sid = 0xe, .table = elanspi_calibration_table_page1_id14 },
|
||||
{ .sid = 0x0, .table = NULL }
|
||||
}
|
||||
};
|
||||
|
||||
#define ELANSPI_NO_ROTATE 0
|
||||
#define ELANSPI_90LEFT_ROTATE 1
|
||||
#define ELANSPI_180_ROTATE 2
|
||||
#define ELANSPI_90RIGHT_ROTATE 3
|
||||
|
||||
#define ELANSPI_HV_FLIPPED 1
|
||||
|
||||
#define ELANSPI_UDEV_TYPES FPI_DEVICE_UDEV_SUBTYPE_SPIDEV | FPI_DEVICE_UDEV_SUBTYPE_HIDRAW
|
||||
#define ELANSPI_TP_VID 0x04f3
|
||||
|
||||
// using checkargs ACPI:HIDPID
|
||||
static const FpIdEntry elanspi_id_table[] = {
|
||||
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x3057}, .driver_data = ELANSPI_180_ROTATE},
|
||||
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x3087}, .driver_data = ELANSPI_180_ROTATE},
|
||||
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x30c6}, .driver_data = ELANSPI_180_ROTATE},
|
||||
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN70A1", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x3134}, .driver_data = ELANSPI_90LEFT_ROTATE},
|
||||
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x3148}, .driver_data = ELANSPI_180_ROTATE},
|
||||
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x30b2}, .driver_data = ELANSPI_NO_ROTATE},
|
||||
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN70A1", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x30b2}, .driver_data = ELANSPI_NO_ROTATE},
|
||||
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x309f}, .driver_data = ELANSPI_180_ROTATE},
|
||||
{.udev_types = 0}
|
||||
};
|
||||
|
||||
#define ELANSPI_MAX_OLD_STAGE1_CALIBRATION_MEAN 1000
|
||||
|
||||
#define ELANSPI_MIN_OLD_STAGE2_CALBIRATION_MEAN 3000
|
||||
#define ELANSPI_MAX_OLD_STAGE2_CALBIRATION_MEAN 8000
|
||||
|
||||
#define ELANSPI_HV_CALIBRATION_TARGET_MEAN 3000
|
||||
|
||||
#define ELANSPI_MIN_EMPTY_INVALID_PERCENT 6
|
||||
#define ELANSPI_MAX_REAL_INVALID_PERCENT 3
|
||||
|
||||
#define ELANSPI_MIN_REAL_STDDEV (592 * 592)
|
||||
#define ELANSPI_MAX_EMPTY_STDDEV (350 * 350)
|
||||
|
||||
#define ELANSPI_MIN_FRAMES_DEBOUNCE 2
|
||||
|
||||
#define ELANSPI_SWIPE_FRAMES_DISCARD 1
|
||||
#define ELANSPI_MIN_FRAMES_SWIPE (7 + ELANSPI_SWIPE_FRAMES_DISCARD)
|
||||
#define ELANSPI_MAX_FRAMES_SWIPE (20 + ELANSPI_SWIPE_FRAMES_DISCARD)
|
||||
|
||||
#define ELANSPI_MAX_FRAME_HEIGHT 43
|
||||
#define ELANSPI_MIN_FRAME_TO_FRAME_DIFF (250 * 250)
|
||||
|
||||
#define ELANSPI_HV_SENSOR_FRAME_DELAY 23
|
||||
|
||||
#define ELANSPI_OTP_TIMEOUT_USEC (12 * 1000)
|
||||
|
||||
#define ELANSPI_OLD_CAPTURE_TIMEOUT_USEC (100 * 1000)
|
||||
#define ELANSPI_HV_CAPTURE_TIMEOUT_USEC (50 * 1000)
|
|
@ -1,325 +0,0 @@
|
|||
/*
|
||||
* Secugen FDU2000 driver for libfprint
|
||||
* Copyright (C) 2007 Gustavo Chain <g@0xff.cl>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#define FP_COMPONENT "fdu2000"
|
||||
#include <fp_internal.h>
|
||||
|
||||
#include "driver_ids.h"
|
||||
|
||||
#ifndef HAVE_MEMMEM
|
||||
gpointer
|
||||
memmem(const gpointer haystack, size_t haystack_len, const gpointer needle, size_t needle_len) {
|
||||
const gchar *begin;
|
||||
const char *const last_possible = (const char *) haystack + haystack_len - needle_len;
|
||||
|
||||
/* The first occurrence of the empty string is deemed to occur at
|
||||
* the beginning of the string. */
|
||||
if (needle_len == 0)
|
||||
return (void *) haystack;
|
||||
|
||||
/* Sanity check, otherwise the loop might search through the whole
|
||||
* memory. */
|
||||
if (haystack_len < needle_len)
|
||||
return NULL;
|
||||
|
||||
for (begin = (const char *) haystack; begin <= last_possible; ++begin)
|
||||
if (begin[0] == ((const char *) needle)[0] &&
|
||||
!memcmp((const void *) &begin[1],
|
||||
(const void *) ((const char *) needle + 1),
|
||||
needle_len - 1))
|
||||
return (void *) begin;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif /* HAVE_MEMMEM */
|
||||
|
||||
#define EP_IMAGE ( 0x02 | LIBUSB_ENDPOINT_IN )
|
||||
#define EP_REPLY ( 0x01 | LIBUSB_ENDPOINT_IN )
|
||||
#define EP_CMD ( 0x01 | LIBUSB_ENDPOINT_OUT )
|
||||
#define BULK_TIMEOUT 200
|
||||
|
||||
/* fdu_req[] index */
|
||||
typedef enum {
|
||||
CAPTURE_READY,
|
||||
CAPTURE_READ,
|
||||
CAPTURE_END,
|
||||
LED_OFF,
|
||||
LED_ON
|
||||
} req_index;
|
||||
|
||||
|
||||
#define CMD_LEN 2
|
||||
#define ACK_LEN 8
|
||||
static const struct fdu2000_req {
|
||||
const gchar cmd[CMD_LEN]; // Command to send
|
||||
const gchar ack[ACK_LEN]; // Expected ACK
|
||||
const guint ack_len; // ACK has variable length
|
||||
} fdu_req[] = {
|
||||
/* Capture */
|
||||
{
|
||||
.cmd = { 0x00, 0x04 },
|
||||
.ack = { 0x00, 0x04, 0x01, 0x01 },
|
||||
.ack_len = 4
|
||||
},
|
||||
|
||||
{
|
||||
.cmd = { 0x00, 0x01 },
|
||||
.ack = { 0x00, 0x01, 0x01, 0x01 },
|
||||
.ack_len = 4
|
||||
},
|
||||
|
||||
{
|
||||
.cmd = { 0x00, 0x05 },
|
||||
.ack = { 0x00, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
|
||||
.ack_len = 8
|
||||
},
|
||||
|
||||
/* Led */
|
||||
{
|
||||
.cmd = { 0x05, 0x00 },
|
||||
.ack = {},
|
||||
.ack_len = 0
|
||||
},
|
||||
|
||||
{
|
||||
.cmd = { 0x05, 0x01 },
|
||||
.ack = {},
|
||||
.ack_len = 0
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Write a command and verify reponse
|
||||
*/
|
||||
static gint
|
||||
bulk_write_safe(libusb_dev_handle *dev, req_index rIndex) {
|
||||
|
||||
gchar reponse[ACK_LEN];
|
||||
gint r;
|
||||
gchar *cmd = (gchar *)fdu_req[rIndex].cmd;
|
||||
gchar *ack = (gchar *)fdu_req[rIndex].ack;
|
||||
gint ack_len = fdu_req[rIndex].ack_len;
|
||||
struct libusb_bulk_transfer wrmsg = {
|
||||
.endpoint = EP_CMD,
|
||||
.data = cmd,
|
||||
.length = sizeof(cmd),
|
||||
};
|
||||
struct libusb_bulk_transfer readmsg = {
|
||||
.endpoint = EP_REPLY,
|
||||
.data = reponse,
|
||||
.length = sizeof(reponse),
|
||||
};
|
||||
int trf;
|
||||
|
||||
r = libusb_bulk_transfer(dev, &wrmsg, &trf, BULK_TIMEOUT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ack_len == 0)
|
||||
return 0;
|
||||
|
||||
/* Check reply from FP */
|
||||
r = libusb_bulk_transfer(dev, &readmsg, &trf, BULK_TIMEOUT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!strncmp(ack, reponse, ack_len))
|
||||
return 0;
|
||||
|
||||
fp_err("Expected different ACK from dev");
|
||||
return 1; /* Error */
|
||||
}
|
||||
|
||||
static gint
|
||||
capture(struct fp_img_dev *dev, gboolean unconditional,
|
||||
struct fp_img **ret)
|
||||
{
|
||||
#define RAW_IMAGE_WIDTH 398
|
||||
#define RAW_IMAGE_HEIGTH 301
|
||||
#define RAW_IMAGE_SIZE (RAW_IMAGE_WIDTH * RAW_IMAGE_HEIGTH)
|
||||
|
||||
struct fp_img *img = NULL;
|
||||
int bytes, r;
|
||||
const gchar SOF[] = { 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0c, 0x07 }; // Start of frame
|
||||
const gchar SOL[] = { 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0b, 0x06 }; // Start of line + { L L } (L: Line num) (8 nibbles)
|
||||
gchar *buffer = g_malloc0(RAW_IMAGE_SIZE * 6);
|
||||
gchar *image;
|
||||
gchar *p;
|
||||
guint offset;
|
||||
struct libusb_bulk_transfer msg = {
|
||||
.endpoint = EP_IMAGE,
|
||||
.data = buffer,
|
||||
.length = RAW_IMAGE_SIZE * 6,
|
||||
};
|
||||
|
||||
image = g_malloc0(RAW_IMAGE_SIZE);
|
||||
|
||||
if ((r = bulk_write_safe(dev->udev, LED_ON))) {
|
||||
fp_err("Command: LED_ON");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = bulk_write_safe(dev->udev, CAPTURE_READY))) {
|
||||
fp_err("Command: CAPTURE_READY");
|
||||
goto out;
|
||||
}
|
||||
|
||||
read:
|
||||
if ((r = bulk_write_safe(dev->udev, CAPTURE_READ))) {
|
||||
fp_err("Command: CAPTURE_READ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Now we are ready to read from dev */
|
||||
|
||||
r = libusb_bulk_transfer(dev->udev, &msg, &bytes, BULK_TIMEOUT * 10);
|
||||
if (r < 0 || bytes < 1)
|
||||
goto read;
|
||||
|
||||
/*
|
||||
* Find SOF (start of line)
|
||||
*/
|
||||
p = memmem(buffer, RAW_IMAGE_SIZE * 6,
|
||||
(const gpointer)SOF, sizeof SOF);
|
||||
fp_dbg("Read %d byte/s from dev", bytes);
|
||||
if (!p)
|
||||
goto out;
|
||||
|
||||
p += sizeof SOF;
|
||||
|
||||
int i = 0;
|
||||
bytes = 0;
|
||||
while(p) {
|
||||
if ( i >= RAW_IMAGE_HEIGTH )
|
||||
break;
|
||||
|
||||
offset = p - buffer;
|
||||
p = memmem(p, (RAW_IMAGE_SIZE * 6) - (offset),
|
||||
(const gpointer)SOL, sizeof SOL);
|
||||
if (p) {
|
||||
p += sizeof SOL + 4;
|
||||
int j;
|
||||
for (j = 0; j < RAW_IMAGE_WIDTH; j++) {
|
||||
/**
|
||||
* Convert from 4 to 8 bits
|
||||
* The SECUGEN-FDU2000 has 4 lines of data, so we need to join 2 bytes into 1
|
||||
*/
|
||||
*(image + bytes + j) = *(p + (j * 2) + 0) << 4 & 0xf0;
|
||||
*(image + bytes + j) |= *(p + (j * 2) + 1) & 0x0f;
|
||||
}
|
||||
p += RAW_IMAGE_WIDTH * 2;
|
||||
bytes += RAW_IMAGE_WIDTH;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((r = bulk_write_safe(dev->udev, CAPTURE_END))) {
|
||||
fp_err("Command: CAPTURE_END");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = bulk_write_safe(dev->udev, LED_OFF))) {
|
||||
fp_err("Command: LED_OFF");
|
||||
goto out;
|
||||
}
|
||||
|
||||
img = fpi_img_new_for_imgdev(dev);
|
||||
memcpy(img->data, image, RAW_IMAGE_SIZE);
|
||||
img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
|
||||
*ret = img;
|
||||
|
||||
out:
|
||||
g_free(buffer);
|
||||
g_free(image);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static
|
||||
gint dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
gint r;
|
||||
//if ( (r = usb_set_configuration(dev->udev, 1)) < 0 )
|
||||
// goto out;
|
||||
|
||||
if ( (r = libusb_claim_interface(dev->udev, 0)) < 0 ) {
|
||||
fp_err("could not claim interface 0: %s", libusb_error_name(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
//if ( (r = usb_set_altinterface(dev->udev, 1)) < 0 )
|
||||
// goto out;
|
||||
|
||||
//if ( (r = usb_clear_halt(dev->udev, EP_CMD)) < 0 )
|
||||
// goto out;
|
||||
|
||||
/* Make sure sensor mode is not capture_{ready|read} */
|
||||
if ((r = bulk_write_safe(dev->udev, CAPTURE_END))) {
|
||||
fp_err("Command: CAPTURE_END");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = bulk_write_safe(dev->udev, LED_OFF))) {
|
||||
fp_err("Command: LED_OFF");
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
fp_err("could not init dev");
|
||||
return r;
|
||||
}
|
||||
|
||||
static
|
||||
void dev_exit(struct fp_img_dev *dev)
|
||||
{
|
||||
if (bulk_write_safe(dev->udev, CAPTURE_END))
|
||||
fp_err("Command: CAPTURE_END");
|
||||
|
||||
libusb_release_interface(dev->udev, 0);
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x1162, .product = 0x0300 },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver fdu2000_driver = {
|
||||
.driver = {
|
||||
.id = FDU2000_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "Secugen FDU 2000",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||
},
|
||||
.img_height = RAW_IMAGE_HEIGTH,
|
||||
.img_width = RAW_IMAGE_WIDTH,
|
||||
.bz3_threshold = 23,
|
||||
|
||||
.init = dev_init,
|
||||
.exit = dev_exit,
|
||||
.capture = capture,
|
||||
};
|
1583
libfprint/drivers/goodixmoc/goodix.c
Normal file
62
libfprint/drivers/goodixmoc/goodix.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Goodix Moc driver for libfprint
|
||||
* Copyright (C) 2019 Shenzhen Goodix Technology Co., Ltd.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fpi-device.h"
|
||||
#include "fpi-ssm.h"
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FpiDeviceGoodixMoc, fpi_device_goodixmoc, FPI, DEVICE_GOODIXMOC, FpDevice)
|
||||
|
||||
typedef enum {
|
||||
FP_CMD_SEND = 0,
|
||||
FP_CMD_GET_ACK,
|
||||
FP_CMD_GET_DATA,
|
||||
FP_CMD_NUM_STATES,
|
||||
} FpCmdState;
|
||||
|
||||
|
||||
typedef enum {
|
||||
FP_INIT_VERSION = 0,
|
||||
FP_INIT_CONFIG,
|
||||
FP_INIT_NUM_STATES,
|
||||
} FpInitState;
|
||||
|
||||
|
||||
typedef enum {
|
||||
FP_ENROLL_PWR_BTN_SHIELD_ON = 0,
|
||||
FP_ENROLL_ENUM,
|
||||
FP_ENROLL_IDENTIFY,
|
||||
FP_ENROLL_CREATE,
|
||||
FP_ENROLL_CAPTURE,
|
||||
FP_ENROLL_UPDATE,
|
||||
FP_ENROLL_WAIT_FINGER_UP,
|
||||
FP_ENROLL_CHECK_DUPLICATE,
|
||||
FP_ENROLL_COMMIT,
|
||||
FP_ENROLL_PWR_BTN_SHIELD_OFF,
|
||||
FP_ENROLL_NUM_STATES,
|
||||
} FpEnrollState;
|
||||
|
||||
typedef enum {
|
||||
FP_VERIFY_PWR_BTN_SHIELD_ON = 0,
|
||||
FP_VERIFY_CAPTURE,
|
||||
FP_VERIFY_IDENTIFY,
|
||||
FP_VERIFY_PWR_BTN_SHIELD_OFF,
|
||||
FP_VERIFY_NUM_STATES,
|
||||
} FpVerifyState;
|