Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Validity VFS101 driver for libfprint | ||
3 | * Copyright (C) 2011 Sergio Cerlesi <sergio.cerlesi@gmail.com> | ||
4 | * | ||
5 | * This library is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; either | ||
8 | * version 2.1 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This library is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this library; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | #define FP_COMPONENT "vfs101" | ||
21 | |||
22 | #include "drivers_api.h" | ||
23 | |||
24 | /* Input-Output usb endpoint */ | ||
25 | #define EP_IN(n) (n | FPI_USB_ENDPOINT_IN) | ||
26 | #define EP_OUT(n) (n | FPI_USB_ENDPOINT_OUT) | ||
27 | |||
28 | /* Usb bulk timeout */ | ||
29 | #define BULK_TIMEOUT 100 | ||
30 | |||
31 | /* The device send back the image into block of 16 frames of 292 bytes */ | ||
32 | #define VFS_FRAME_SIZE 292 | ||
33 | #define VFS_BLOCK_SIZE 16 * VFS_FRAME_SIZE | ||
34 | |||
35 | /* Buffer height */ | ||
36 | #define VFS_BUFFER_HEIGHT 5000 | ||
37 | |||
38 | /* Buffer size */ | ||
39 | #define VFS_BUFFER_SIZE (VFS_BUFFER_HEIGHT * VFS_FRAME_SIZE) | ||
40 | |||
41 | /* Image width */ | ||
42 | #define VFS_IMG_WIDTH 200 | ||
43 | |||
44 | /* Maximum image height */ | ||
45 | #define VFS_IMG_MAX_HEIGHT 1023 | ||
46 | |||
47 | /* Minimum image height */ | ||
48 | #define VFS_IMG_MIN_HEIGHT 200 | ||
49 | |||
50 | /* Scan level threshold */ | ||
51 | #define VFS_IMG_SLT_BEGIN 768 | ||
52 | #define VFS_IMG_SLT_END 64 | ||
53 | #define VFS_IMG_SLT_LINES 4 | ||
54 | |||
55 | /* Minimum image level */ | ||
56 | #define VFS_IMG_MIN_IMAGE_LEVEL 144 | ||
57 | |||
58 | /* Best image contrast */ | ||
59 | #define VFS_IMG_BEST_CONTRAST 128 | ||
60 | |||
61 | /* Device parameters address */ | ||
62 | #define VFS_PAR_000E 0x000e | ||
63 | #define VFS_PAR_0011 0x0011 | ||
64 | #define VFS_PAR_THRESHOLD 0x0057 | ||
65 | #define VFS_PAR_STATE_3 0x005e | ||
66 | #define VFS_PAR_STATE_5 0x005f | ||
67 | #define VFS_PAR_INFO_RATE 0x0062 | ||
68 | #define VFS_PAR_0076 0x0076 | ||
69 | #define VFS_PAR_INFO_CONTRAST 0x0077 | ||
70 | #define VFS_PAR_0078 0x0078 | ||
71 | |||
72 | /* Device regiones address */ | ||
73 | #define VFS_REG_IMG_EXPOSURE 0xff500e | ||
74 | #define VFS_REG_IMG_CONTRAST 0xff5038 | ||
75 | |||
76 | /* Device settings */ | ||
77 | #define VFS_VAL_000E 0x0001 | ||
78 | #define VFS_VAL_0011 0x0008 | ||
79 | #define VFS_VAL_THRESHOLD 0x0096 | ||
80 | #define VFS_VAL_STATE_3 0x0064 | ||
81 | #define VFS_VAL_STATE_5 0x00c8 | ||
82 | #define VFS_VAL_INFO_RATE 0x0001 | ||
83 | #define VFS_VAL_0076 0x0012 | ||
84 | #define VFS_VAL_0078 0x2230 | ||
85 | #define VFS_VAL_IMG_EXPOSURE 0x21c0 | ||
86 | |||
87 | /* Structure for Validity device */ | ||
88 | struct _FpDeviceVfs101 | ||
89 | { | ||
90 | FpImageDevice parent; | ||
91 | |||
92 | /* Action state */ | ||
93 | gboolean active; | ||
94 | gboolean deactivate; | ||
95 | |||
96 | /* Sequential number */ | ||
97 | unsigned int seqnum; | ||
98 | |||
99 | /* Buffer for input/output */ | ||
100 | unsigned char *buffer; | ||
101 | |||
102 | /* Length of data to send or received */ | ||
103 | unsigned int length; | ||
104 | |||
105 | /* Ignore usb error */ | ||
106 | int ignore_error; | ||
107 | |||
108 | /* Loop counter */ | ||
109 | int counter; | ||
110 | |||
111 | /* Image contrast */ | ||
112 | int contrast; | ||
113 | |||
114 | /* Best contrast */ | ||
115 | int best_contrast; | ||
116 | |||
117 | /* Best contrast level */ | ||
118 | int best_clevel; | ||
119 | |||
120 | /* Bottom line of image */ | ||
121 | int bottom; | ||
122 | |||
123 | /* Image height */ | ||
124 | int height; | ||
125 | }; | ||
126 | G_DECLARE_FINAL_TYPE (FpDeviceVfs101, fpi_device_vfs101, FPI, DEVICE_VFS101, | ||
127 | FpImageDevice); | ||
128 |
4/5✓ Branch 0 taken 120 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 120 times.
✗ Branch 4 not taken.
|
768 | G_DEFINE_TYPE (FpDeviceVfs101, fpi_device_vfs101, FP_TYPE_IMAGE_DEVICE); |
129 | |||
130 | /* Return byte at specified position */ | ||
131 | static inline unsigned char | ||
132 | ✗ | byte (int position, int value) | |
133 | { | ||
134 | ✗ | return (value >> (position * 8)) & 0xff; | |
135 | } | ||
136 | |||
137 | /* Return sequential number */ | ||
138 | static inline unsigned short | ||
139 | ✗ | get_seqnum (int h, int l) | |
140 | { | ||
141 | ✗ | return (h << 8) | l; | |
142 | } | ||
143 | |||
144 | /* Check sequential number */ | ||
145 | static inline int | ||
146 | ✗ | check_seqnum (FpDeviceVfs101 *vdev) | |
147 | { | ||
148 | ✗ | if ((byte (0, vdev->seqnum) == vdev->buffer[0]) && | |
149 | ✗ | (byte (1, vdev->seqnum) == vdev->buffer[1])) | |
150 | return 0; | ||
151 | else | ||
152 | ✗ | return 1; | |
153 | } | ||
154 | |||
155 | /* Internal result codes */ | ||
156 | enum { | ||
157 | RESULT_RETRY, | ||
158 | RESULT_RETRY_SHORT, | ||
159 | RESULT_RETRY_REMOVE, | ||
160 | RESULT_COUNT, | ||
161 | }; | ||
162 | |||
163 | /* Dump buffer for debug */ | ||
164 | #define dump_buffer(buf) \ | ||
165 | fp_dbg ("%02x %02x %02x %02x %02x %02x %02x %02x", \ | ||
166 | buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13] \ | ||
167 | ) | ||
168 | |||
169 | /* Callback of asynchronous send */ | ||
170 | static void | ||
171 | ✗ | async_send_cb (FpiUsbTransfer *transfer, FpDevice *device, | |
172 | gpointer user_data, GError *error) | ||
173 | { | ||
174 | ✗ | FpImageDevice *dev = FP_IMAGE_DEVICE (device); | |
175 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
176 | |||
177 | /* Skip error check if ignore_error is set */ | ||
178 | ✗ | if (error) | |
179 | { | ||
180 | ✗ | if (!self->ignore_error) | |
181 | { | ||
182 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
183 | ✗ | return; | |
184 | } | ||
185 | else | ||
186 | { | ||
187 | ✗ | g_error_free (error); | |
188 | ✗ | fp_dbg ("Ignoring send error: %s", error->message); | |
189 | } | ||
190 | } | ||
191 | /* Reset ignore_error flag */ | ||
192 | ✗ | self->ignore_error = FALSE; | |
193 | |||
194 | /* Dump buffer for debug */ | ||
195 | ✗ | dump_buffer (self->buffer); | |
196 | |||
197 | ✗ | fpi_ssm_next_state (transfer->ssm); | |
198 | } | ||
199 | |||
200 | /* Submit asynchronous send */ | ||
201 | static void | ||
202 | ✗ | async_send (FpiSsm *ssm, | |
203 | FpImageDevice *dev) | ||
204 | { | ||
205 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
206 | ✗ | FpiUsbTransfer *transfer; | |
207 | |||
208 | ✗ | transfer = fpi_usb_transfer_new (FP_DEVICE (dev)); | |
209 | |||
210 | /* Put sequential number into the buffer */ | ||
211 | ✗ | self->seqnum++; | |
212 | ✗ | self->buffer[0] = byte (0, self->seqnum); | |
213 | ✗ | self->buffer[1] = byte (1, self->seqnum); | |
214 | |||
215 | /* Prepare bulk transfer */ | ||
216 | ✗ | fpi_usb_transfer_fill_bulk_full (transfer, EP_OUT (1), | |
217 | ✗ | self->buffer, self->length, NULL); | |
218 | ✗ | transfer->ssm = ssm; | |
219 | ✗ | transfer->short_is_error = TRUE; | |
220 | ✗ | fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, | |
221 | async_send_cb, NULL); | ||
222 | ✗ | } | |
223 | |||
224 | /* Callback of asynchronous recv */ | ||
225 | static void | ||
226 | ✗ | async_recv_cb (FpiUsbTransfer *transfer, FpDevice *device, | |
227 | gpointer user_data, GError *error) | ||
228 | { | ||
229 | ✗ | FpImageDevice *dev = FP_IMAGE_DEVICE (device); | |
230 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
231 | |||
232 | /* Skip error check if ignore_error is set */ | ||
233 | ✗ | if (!self->ignore_error) | |
234 | { | ||
235 | ✗ | if (error) | |
236 | { | ||
237 | /* Transfer not completed, return IO error */ | ||
238 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
239 | ✗ | return; | |
240 | } | ||
241 | |||
242 | ✗ | if (check_seqnum (self)) | |
243 | { | ||
244 | /* Sequential number received mismatch, return protocol error */ | ||
245 | ✗ | fp_err ("seqnum mismatch, got %04x, expected %04x", | |
246 | get_seqnum (self->buffer[1], self->buffer[0]), | ||
247 | self->seqnum); | ||
248 | ✗ | fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
249 | ✗ | return; | |
250 | } | ||
251 | } | ||
252 | |||
253 | ✗ | g_clear_pointer (&error, g_error_free); | |
254 | |||
255 | /* Reset ignore_error flag */ | ||
256 | ✗ | self->ignore_error = FALSE; | |
257 | |||
258 | /* Dump buffer for debug */ | ||
259 | ✗ | dump_buffer (self->buffer); | |
260 | |||
261 | /* Set length of received data */ | ||
262 | ✗ | self->length = transfer->actual_length; | |
263 | |||
264 | ✗ | fpi_ssm_next_state (transfer->ssm); | |
265 | } | ||
266 | |||
267 | /* Submit asynchronous recv */ | ||
268 | static void | ||
269 | ✗ | async_recv (FpiSsm *ssm, | |
270 | FpImageDevice *dev) | ||
271 | { | ||
272 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
273 | ✗ | FpiUsbTransfer *transfer; | |
274 | |||
275 | /* Allocation of transfer */ | ||
276 | ✗ | transfer = fpi_usb_transfer_new (FP_DEVICE (dev)); | |
277 | |||
278 | /* Prepare bulk transfer */ | ||
279 | ✗ | fpi_usb_transfer_fill_bulk_full (transfer, EP_IN (1), self->buffer, | |
280 | 0x0f, NULL); | ||
281 | ✗ | transfer->ssm = ssm; | |
282 | ✗ | fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, | |
283 | async_recv_cb, NULL); | ||
284 | ✗ | } | |
285 | |||
286 | static void async_load (FpiSsm *ssm, | ||
287 | FpImageDevice *dev); | ||
288 | |||
289 | /* Callback of asynchronous load */ | ||
290 | static void | ||
291 | ✗ | async_load_cb (FpiUsbTransfer *transfer, FpDevice *device, | |
292 | gpointer user_data, GError *error) | ||
293 | { | ||
294 | ✗ | FpImageDevice *dev = FP_IMAGE_DEVICE (device); | |
295 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
296 | |||
297 | /* Skip error check if ignore_error is set */ | ||
298 | ✗ | if (!self->ignore_error) | |
299 | { | ||
300 | ✗ | if (error) | |
301 | { | ||
302 | /* Transfer not completed */ | ||
303 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
304 | ✗ | return; | |
305 | } | ||
306 | |||
307 | ✗ | if (transfer->actual_length % VFS_FRAME_SIZE) | |
308 | { | ||
309 | /* Received incomplete frame, return protocol error */ | ||
310 | ✗ | fp_err ("received incomplete frame"); | |
311 | ✗ | fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
312 | ✗ | return; | |
313 | } | ||
314 | } | ||
315 | |||
316 | /* Any error has been ignored. */ | ||
317 | ✗ | g_clear_pointer (&error, g_error_free); | |
318 | |||
319 | /* Increase image length */ | ||
320 | ✗ | self->length += transfer->actual_length; | |
321 | |||
322 | ✗ | if (transfer->actual_length == VFS_BLOCK_SIZE) | |
323 | { | ||
324 | ✗ | if ((VFS_BUFFER_SIZE - self->length) < VFS_BLOCK_SIZE) | |
325 | { | ||
326 | /* Buffer full, image too large, return no memory error */ | ||
327 | ✗ | fp_err ("buffer full, image too large"); | |
328 | ✗ | fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
329 | ✗ | return; | |
330 | } | ||
331 | else | ||
332 | { | ||
333 | /* Image load not completed, submit another asynchronous load */ | ||
334 | ✗ | async_load (transfer->ssm, dev); | |
335 | } | ||
336 | } | ||
337 | else | ||
338 | { | ||
339 | /* Reset ignore_error flag */ | ||
340 | ✗ | self->ignore_error = FALSE; | |
341 | |||
342 | /* Image load completed, go to next state */ | ||
343 | ✗ | self->height = self->length / VFS_FRAME_SIZE; | |
344 | ✗ | fp_dbg ("image loaded, height = %d", self->height); | |
345 | ✗ | fpi_ssm_next_state (transfer->ssm); | |
346 | } | ||
347 | } | ||
348 | |||
349 | /* Submit asynchronous load */ | ||
350 | static void | ||
351 | ✗ | async_load (FpiSsm *ssm, | |
352 | FpImageDevice *dev) | ||
353 | { | ||
354 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
355 | ✗ | FpiUsbTransfer *transfer; | |
356 | ✗ | unsigned char *buffer; | |
357 | |||
358 | /* Allocation of transfer */ | ||
359 | ✗ | transfer = fpi_usb_transfer_new (FP_DEVICE (dev)); | |
360 | |||
361 | /* Append new data into the buffer */ | ||
362 | ✗ | buffer = self->buffer + self->length; | |
363 | |||
364 | /* Prepare bulk transfer */ | ||
365 | ✗ | fpi_usb_transfer_fill_bulk_full (transfer, EP_IN (2), buffer, | |
366 | VFS_BLOCK_SIZE, NULL); | ||
367 | ✗ | transfer->ssm = ssm; | |
368 | ✗ | fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, | |
369 | async_load_cb, NULL); | ||
370 | ✗ | } | |
371 | |||
372 | /* Swap ssm states */ | ||
373 | enum { | ||
374 | M_SWAP_SEND, | ||
375 | M_SWAP_RECV, | ||
376 | M_SWAP_NUM_STATES, | ||
377 | }; | ||
378 | |||
379 | /* Exec swap sequential state machine */ | ||
380 | static void | ||
381 | ✗ | m_swap_state (FpiSsm *ssm, FpDevice *dev) | |
382 | { | ||
383 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
384 | { | ||
385 | case M_SWAP_SEND: | ||
386 | /* Send data */ | ||
387 | ✗ | async_send (ssm, FP_IMAGE_DEVICE (dev)); | |
388 | ✗ | break; | |
389 | |||
390 | case M_SWAP_RECV: | ||
391 | /* Recv response */ | ||
392 | ✗ | async_recv (ssm, FP_IMAGE_DEVICE (dev)); | |
393 | ✗ | break; | |
394 | } | ||
395 | ✗ | } | |
396 | |||
397 | /* Start swap sequential state machine */ | ||
398 | static void | ||
399 | ✗ | m_swap (FpiSsm *ssm, | |
400 | FpImageDevice *dev, | ||
401 | unsigned char *data, | ||
402 | size_t length) | ||
403 | { | ||
404 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
405 | ✗ | FpiSsm *subsm; | |
406 | |||
407 | /* Prepare data for sending */ | ||
408 | ✗ | memcpy (self->buffer, data, length); | |
409 | ✗ | memset (self->buffer + length, 0, 16 - length); | |
410 | ✗ | self->length = length; | |
411 | |||
412 | /* Start swap ssm */ | ||
413 | ✗ | subsm = fpi_ssm_new (FP_DEVICE (dev), m_swap_state, M_SWAP_NUM_STATES); | |
414 | ✗ | fpi_ssm_start_subsm (ssm, subsm); | |
415 | ✗ | } | |
416 | |||
417 | /* Retrieve fingerprint image */ | ||
418 | static void | ||
419 | ✗ | vfs_get_print (FpiSsm *ssm, | |
420 | FpImageDevice *dev, | ||
421 | unsigned int param, | ||
422 | int type) | ||
423 | { | ||
424 | ✗ | unsigned char data[2][0x0e] = { | |
425 | { 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, | ||
426 | 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01 }, | ||
427 | { 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, | ||
428 | 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01 } | ||
429 | }; | ||
430 | |||
431 | ✗ | fp_dbg ("param = %04x, type = %d", param, type); | |
432 | |||
433 | /* Prepare data for sending */ | ||
434 | ✗ | data[type][6] = byte (0, param); | |
435 | ✗ | data[type][7] = byte (1, param); | |
436 | |||
437 | /* Run swap sequential state machine */ | ||
438 | ✗ | m_swap (ssm, dev, data[type], 0x0e); | |
439 | ✗ | } | |
440 | |||
441 | /* Set a parameter value on the device */ | ||
442 | static void | ||
443 | ✗ | vfs_set_param (FpiSsm *ssm, | |
444 | FpImageDevice *dev, | ||
445 | unsigned int param, | ||
446 | unsigned int value) | ||
447 | { | ||
448 | ✗ | unsigned char data[0x0a] = { 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
449 | |||
450 | ✗ | fp_dbg ("param = %04x, value = %04x", param, value); | |
451 | |||
452 | /* Prepare data for sending */ | ||
453 | ✗ | data[6] = byte (0, param); | |
454 | ✗ | data[7] = byte (1, param); | |
455 | ✗ | data[8] = byte (0, value); | |
456 | ✗ | data[9] = byte (1, value); | |
457 | |||
458 | /* Run swap sequential state machine */ | ||
459 | ✗ | m_swap (ssm, dev, data, 0x0a); | |
460 | ✗ | } | |
461 | |||
462 | /* Abort previous print */ | ||
463 | static void | ||
464 | ✗ | vfs_abort_print (FpiSsm *ssm, | |
465 | FpImageDevice *dev) | ||
466 | { | ||
467 | ✗ | unsigned char data[0x06] = { 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00 }; | |
468 | |||
469 | ✗ | G_DEBUG_HERE (); | |
470 | |||
471 | /* Run swap sequential state machine */ | ||
472 | ✗ | m_swap (ssm, dev, data, 0x06); | |
473 | ✗ | } | |
474 | |||
475 | /* Poke a value on a region */ | ||
476 | static void | ||
477 | ✗ | vfs_poke (FpiSsm *ssm, | |
478 | FpImageDevice *dev, | ||
479 | unsigned int addr, | ||
480 | unsigned int value, | ||
481 | unsigned int size) | ||
482 | { | ||
483 | ✗ | unsigned char data[0x0f] = { 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
484 | |||
485 | ✗ | fp_dbg ("addr = %04x, value = %04x", addr, value); | |
486 | |||
487 | /* Prepare data for sending */ | ||
488 | ✗ | data[6] = byte (0, addr); | |
489 | ✗ | data[7] = byte (1, addr); | |
490 | ✗ | data[8] = byte (2, addr); | |
491 | ✗ | data[9] = byte (3, addr); | |
492 | ✗ | data[10] = byte (0, value); | |
493 | ✗ | data[11] = byte (1, value); | |
494 | ✗ | data[12] = byte (2, value); | |
495 | ✗ | data[13] = byte (3, value); | |
496 | ✗ | data[14] = byte (0, size); | |
497 | |||
498 | /* Run swap sequential state machine */ | ||
499 | ✗ | m_swap (ssm, dev, data, 0x0f); | |
500 | ✗ | } | |
501 | |||
502 | /* Get current finger state */ | ||
503 | static void | ||
504 | ✗ | vfs_get_finger_state (FpiSsm *ssm, | |
505 | FpImageDevice *dev) | ||
506 | { | ||
507 | ✗ | unsigned char data[0x06] = { 0x00, 0x00, 0x00, 0x00, 0x16, 0x00 }; | |
508 | |||
509 | ✗ | G_DEBUG_HERE (); | |
510 | |||
511 | /* Run swap sequential state machine */ | ||
512 | ✗ | m_swap (ssm, dev, data, 0x06); | |
513 | ✗ | } | |
514 | |||
515 | /* Load raw image from reader */ | ||
516 | static void | ||
517 | ✗ | vfs_img_load (FpiSsm *ssm, | |
518 | FpImageDevice *dev) | ||
519 | { | ||
520 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
521 | |||
522 | ✗ | G_DEBUG_HERE (); | |
523 | |||
524 | /* Reset buffer length */ | ||
525 | ✗ | self->length = 0; | |
526 | |||
527 | /* Reset image properties */ | ||
528 | ✗ | self->bottom = 0; | |
529 | ✗ | self->height = -1; | |
530 | |||
531 | /* Asynchronous load */ | ||
532 | ✗ | async_load (ssm, dev); | |
533 | ✗ | } | |
534 | |||
535 | #define offset(x, y) ((x) + ((y) * VFS_FRAME_SIZE)) | ||
536 | |||
537 | /* Screen image to remove noise and find bottom line and height of image */ | ||
538 | static void | ||
539 | ✗ | img_screen (FpDeviceVfs101 *vdev) | |
540 | { | ||
541 | ✗ | int y, x, count, top; | |
542 | ✗ | long int level; | |
543 | ✗ | int last_line = vdev->height - 1; | |
544 | |||
545 | ✗ | fp_dbg ("image height before screen = %d", vdev->height); | |
546 | |||
547 | ✗ | count = 0; | |
548 | |||
549 | /* Image returned from sensor can contain many empty lines, | ||
550 | * for remove these lines compare byte 282-283 (scan level information) | ||
551 | * with two different thresholds, one for the begin of finger image and | ||
552 | * one for the end. To increase stability of the code use a counter | ||
553 | * of lines that satisfy the threshold. | ||
554 | */ | ||
555 | ✗ | for (y = last_line, top = last_line; y >= 0; y--) | |
556 | { | ||
557 | /* Take image scan level */ | ||
558 | ✗ | level = vdev->buffer[offset (283, y)] * 256 + | |
559 | ✗ | vdev->buffer[offset (282, y)]; | |
560 | |||
561 | ✗ | fp_dbg ("line = %d, scan level = %ld", y, level); | |
562 | |||
563 | ✗ | if (level >= VFS_IMG_SLT_BEGIN && top == last_line) | |
564 | { | ||
565 | /* Begin threshold satisfied */ | ||
566 | ✗ | if (count < VFS_IMG_SLT_LINES) | |
567 | { | ||
568 | /* Increase count */ | ||
569 | ✗ | count++; | |
570 | } | ||
571 | else | ||
572 | { | ||
573 | /* Found top fingerprint line */ | ||
574 | ✗ | top = y + VFS_IMG_SLT_LINES; | |
575 | ✗ | count = 0; | |
576 | } | ||
577 | } | ||
578 | ✗ | else if ((level < VFS_IMG_SLT_END || level >= 65535) && | |
579 | top != last_line) | ||
580 | { | ||
581 | /* End threshold satisfied */ | ||
582 | ✗ | if (count < VFS_IMG_SLT_LINES) | |
583 | { | ||
584 | /* Increase count */ | ||
585 | ✗ | count++; | |
586 | } | ||
587 | else | ||
588 | { | ||
589 | /* Found bottom fingerprint line */ | ||
590 | ✗ | vdev->bottom = y + VFS_IMG_SLT_LINES + 1; | |
591 | ✗ | break; | |
592 | } | ||
593 | } | ||
594 | else | ||
595 | { | ||
596 | /* Not threshold satisfied, reset count */ | ||
597 | count = 0; | ||
598 | } | ||
599 | } | ||
600 | |||
601 | ✗ | vdev->height = top - vdev->bottom + 1; | |
602 | |||
603 | /* Check max height */ | ||
604 | ✗ | if (vdev->height > VFS_IMG_MAX_HEIGHT) | |
605 | ✗ | vdev->height = VFS_IMG_MAX_HEIGHT; | |
606 | |||
607 | ✗ | fp_dbg ("image height after screen = %d", vdev->height); | |
608 | |||
609 | /* Scan image and remove noise */ | ||
610 | ✗ | for (y = vdev->bottom; y <= top; y++) | |
611 | ✗ | for (x = 6; x < VFS_IMG_WIDTH + 6; x++) | |
612 | ✗ | if (vdev->buffer[offset (x, y)] > VFS_IMG_MIN_IMAGE_LEVEL) | |
613 | ✗ | vdev->buffer[offset (x, y)] = 255; | |
614 | ✗ | }; | |
615 | |||
616 | /* Copy image from reader buffer and put it into image data */ | ||
617 | static void | ||
618 | ✗ | img_copy (FpDeviceVfs101 *self, FpImage *img) | |
619 | { | ||
620 | ✗ | unsigned int line; | |
621 | ✗ | unsigned char *img_buffer = img->data; | |
622 | ✗ | unsigned char *vdev_buffer = self->buffer + (self->bottom * VFS_FRAME_SIZE) + 6; | |
623 | |||
624 | ✗ | for (line = 0; line < img->height; line++) | |
625 | { | ||
626 | /* Copy image line from reader buffer to image data */ | ||
627 | ✗ | memcpy (img_buffer, vdev_buffer, VFS_IMG_WIDTH); | |
628 | |||
629 | /* Next line of reader buffer */ | ||
630 | ✗ | vdev_buffer = vdev_buffer + VFS_FRAME_SIZE; | |
631 | |||
632 | /* Next line of image buffer */ | ||
633 | ✗ | img_buffer = img_buffer + VFS_IMG_WIDTH; | |
634 | } | ||
635 | ✗ | } | |
636 | |||
637 | /* Extract fingerpint image from raw data */ | ||
638 | static void | ||
639 | ✗ | img_extract (FpiSsm *ssm, | |
640 | FpImageDevice *dev) | ||
641 | { | ||
642 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
643 | ✗ | FpImage *img; | |
644 | |||
645 | /* Screen image to remove noise and find top and bottom line */ | ||
646 | ✗ | img_screen (self); | |
647 | |||
648 | /* Check image height */ | ||
649 | ✗ | if (self->height < VFS_IMG_MIN_HEIGHT) | |
650 | { | ||
651 | /* Image too short */ | ||
652 | ✗ | self->height = 0; | |
653 | ✗ | fpi_image_device_retry_scan (dev, FP_DEVICE_RETRY_TOO_SHORT); | |
654 | ✗ | return; | |
655 | } | ||
656 | |||
657 | /* Create new image */ | ||
658 | ✗ | img = fp_image_new (self->height, VFS_IMG_WIDTH); | |
659 | ✗ | img->width = VFS_IMG_WIDTH; | |
660 | ✗ | img->height = self->height; | |
661 | ✗ | img->flags = FPI_IMAGE_V_FLIPPED; | |
662 | |||
663 | /* Copy data into image */ | ||
664 | ✗ | img_copy (self, img); | |
665 | |||
666 | /* Notify image captured */ | ||
667 | ✗ | fpi_image_device_image_captured (dev, img); | |
668 | }; | ||
669 | |||
670 | /* Finger states */ | ||
671 | enum { | ||
672 | VFS_FINGER_EMPTY, | ||
673 | VFS_FINGER_PRESENT, | ||
674 | VFS_FINGER_UNKNOWN, | ||
675 | }; | ||
676 | |||
677 | /* Return finger state */ | ||
678 | static inline int | ||
679 | ✗ | vfs_finger_state (FpDeviceVfs101 *vdev) | |
680 | { | ||
681 | /* Check finger state */ | ||
682 | ✗ | switch (vdev->buffer[0x0a]) | |
683 | { | ||
684 | case 0x00: | ||
685 | case 0x01: | ||
686 | /* Finger is empty */ | ||
687 | return VFS_FINGER_EMPTY; | ||
688 | ✗ | break; | |
689 | |||
690 | case 0x02: | ||
691 | case 0x03: | ||
692 | case 0x04: | ||
693 | case 0x05: | ||
694 | case 0x06: | ||
695 | /* Finger is present */ | ||
696 | ✗ | return VFS_FINGER_PRESENT; | |
697 | ✗ | break; | |
698 | |||
699 | default: | ||
700 | ✗ | return VFS_FINGER_UNKNOWN; | |
701 | } | ||
702 | }; | ||
703 | |||
704 | /* Check contrast of image */ | ||
705 | static void | ||
706 | ✗ | vfs_check_contrast (FpDeviceVfs101 *vdev) | |
707 | { | ||
708 | ✗ | int y; | |
709 | ✗ | long int count = 0; | |
710 | |||
711 | /* Check difference from byte 4 to byte 5 for verify contrast of image */ | ||
712 | ✗ | for (y = 0; y < vdev->height; y++) | |
713 | ✗ | count = count + vdev->buffer[offset (5, y)] - vdev->buffer[offset (4, y)]; | |
714 | ✗ | count = count / vdev->height; | |
715 | |||
716 | ✗ | if (count < 16) | |
717 | { | ||
718 | /* Contrast not valid, retry */ | ||
719 | ✗ | vdev->contrast++; | |
720 | ✗ | return; | |
721 | } | ||
722 | |||
723 | ✗ | fp_dbg ("contrast = %d, level = %ld", vdev->contrast, count); | |
724 | |||
725 | ✗ | if (labs (count - VFS_IMG_BEST_CONTRAST) < abs (vdev->best_clevel - VFS_IMG_BEST_CONTRAST)) | |
726 | { | ||
727 | /* Better contrast found, use it */ | ||
728 | ✗ | vdev->best_contrast = vdev->contrast; | |
729 | ✗ | vdev->best_clevel = count; | |
730 | } | ||
731 | } | ||
732 | |||
733 | /* Loop ssm states */ | ||
734 | enum { | ||
735 | /* Step 0 - Scan finger */ | ||
736 | M_LOOP_0_GET_PRINT, | ||
737 | M_LOOP_0_SLEEP, | ||
738 | M_LOOP_0_GET_STATE, | ||
739 | M_LOOP_0_LOAD_IMAGE, | ||
740 | M_LOOP_0_EXTRACT_IMAGE, | ||
741 | M_LOOP_0_CHECK_ACTION, | ||
742 | |||
743 | /* Step 1 - Scan failed */ | ||
744 | M_LOOP_1_GET_STATE, | ||
745 | M_LOOP_1_CHECK_STATE, | ||
746 | M_LOOP_1_GET_PRINT, | ||
747 | M_LOOP_1_LOAD_IMAGE, | ||
748 | M_LOOP_1_LOOP, | ||
749 | M_LOOP_1_SLEEP, | ||
750 | |||
751 | /* Step 2 - Abort print */ | ||
752 | M_LOOP_2_ABORT_PRINT, | ||
753 | M_LOOP_2_LOAD_IMAGE, | ||
754 | |||
755 | /* Step 3 - Wait aborting */ | ||
756 | M_LOOP_3_GET_PRINT, | ||
757 | M_LOOP_3_LOAD_IMAGE, | ||
758 | M_LOOP_3_CHECK_IMAGE, | ||
759 | M_LOOP_3_LOOP, | ||
760 | |||
761 | /* Number of states */ | ||
762 | M_LOOP_NUM_STATES, | ||
763 | }; | ||
764 | |||
765 | /* Exec loop sequential state machine */ | ||
766 | static void | ||
767 | ✗ | m_loop_state (FpiSsm *ssm, FpDevice *_dev) | |
768 | { | ||
769 | ✗ | FpImageDevice *dev = FP_IMAGE_DEVICE (_dev); | |
770 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (_dev); | |
771 | |||
772 | /* Complete if deactivation was requested */ | ||
773 | ✗ | if (self->deactivate) | |
774 | { | ||
775 | ✗ | fpi_ssm_mark_completed (ssm); | |
776 | ✗ | return; | |
777 | } | ||
778 | |||
779 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
780 | { | ||
781 | ✗ | case M_LOOP_0_GET_PRINT: | |
782 | /* Send get print command to the reader */ | ||
783 | ✗ | vfs_get_print (ssm, dev, VFS_BUFFER_HEIGHT, 1); | |
784 | ✗ | break; | |
785 | |||
786 | ✗ | case M_LOOP_0_SLEEP: | |
787 | /* Wait fingerprint scanning */ | ||
788 | ✗ | fpi_ssm_next_state_delayed (ssm, 50); | |
789 | ✗ | break; | |
790 | |||
791 | ✗ | case M_LOOP_0_GET_STATE: | |
792 | /* Get finger state */ | ||
793 | ✗ | vfs_get_finger_state (ssm, dev); | |
794 | ✗ | break; | |
795 | |||
796 | case M_LOOP_0_LOAD_IMAGE: | ||
797 | /* Check finger state */ | ||
798 | ✗ | switch (vfs_finger_state (self)) | |
799 | { | ||
800 | case VFS_FINGER_EMPTY: | ||
801 | ✗ | fpi_image_device_report_finger_status (dev, FALSE); | |
802 | |||
803 | /* Finger isn't present, loop */ | ||
804 | ✗ | fpi_ssm_jump_to_state (ssm, M_LOOP_0_SLEEP); | |
805 | ✗ | break; | |
806 | |||
807 | case VFS_FINGER_PRESENT: | ||
808 | ✗ | fpi_image_device_report_finger_status (dev, TRUE); | |
809 | |||
810 | /* Load image from reader */ | ||
811 | ✗ | self->ignore_error = TRUE; | |
812 | ✗ | vfs_img_load (ssm, dev); | |
813 | ✗ | break; | |
814 | |||
815 | default: | ||
816 | ✗ | fpi_image_device_report_finger_status (dev, FALSE); | |
817 | |||
818 | /* Unknown state */ | ||
819 | ✗ | fp_err ("unknown device state 0x%02x", | |
820 | self->buffer[0x0a]); | ||
821 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
822 | ✗ | break; | |
823 | } | ||
824 | break; | ||
825 | |||
826 | ✗ | case M_LOOP_0_EXTRACT_IMAGE: | |
827 | /* Fingerprint is loaded, extract image from raw data */ | ||
828 | ✗ | img_extract (ssm, dev); | |
829 | |||
830 | /* Wait handling image */ | ||
831 | ✗ | fpi_ssm_next_state_delayed (ssm, 10); | |
832 | ✗ | break; | |
833 | |||
834 | ✗ | case M_LOOP_0_CHECK_ACTION: | |
835 | /* Action not completed */ | ||
836 | ✗ | if (self->height > 0) | |
837 | /* Continue loop */ | ||
838 | ✗ | fpi_ssm_jump_to_state (ssm, M_LOOP_2_ABORT_PRINT); | |
839 | else | ||
840 | /* Error found */ | ||
841 | ✗ | fpi_ssm_next_state (ssm); | |
842 | break; | ||
843 | |||
844 | ✗ | case M_LOOP_1_GET_STATE: | |
845 | /* Get finger state */ | ||
846 | ✗ | vfs_get_finger_state (ssm, dev); | |
847 | ✗ | break; | |
848 | |||
849 | case M_LOOP_1_CHECK_STATE: | ||
850 | /* Check finger state */ | ||
851 | ✗ | if (vfs_finger_state (self) == VFS_FINGER_PRESENT) | |
852 | { | ||
853 | ✗ | fpi_image_device_report_finger_status (dev, TRUE); | |
854 | ✗ | fpi_ssm_next_state_delayed (ssm, 250); | |
855 | } | ||
856 | else | ||
857 | { | ||
858 | /* Finger not present */ | ||
859 | ✗ | fpi_image_device_report_finger_status (dev, FALSE); | |
860 | |||
861 | /* Continue */ | ||
862 | ✗ | fpi_ssm_jump_to_state (ssm, M_LOOP_1_SLEEP); | |
863 | } | ||
864 | break; | ||
865 | |||
866 | ✗ | case M_LOOP_1_GET_PRINT: | |
867 | /* Send get print command to the reader */ | ||
868 | ✗ | vfs_get_print (ssm, dev, VFS_BUFFER_HEIGHT, 1); | |
869 | ✗ | break; | |
870 | |||
871 | ✗ | case M_LOOP_1_LOAD_IMAGE: | |
872 | /* Load image */ | ||
873 | ✗ | self->ignore_error = TRUE; | |
874 | ✗ | vfs_img_load (ssm, dev); | |
875 | ✗ | break; | |
876 | |||
877 | ✗ | case M_LOOP_1_LOOP: | |
878 | /* Loop */ | ||
879 | ✗ | fpi_ssm_jump_to_state (ssm, M_LOOP_1_GET_STATE); | |
880 | ✗ | break; | |
881 | |||
882 | ✗ | case M_LOOP_1_SLEEP: | |
883 | /* Wait fingerprint scanning */ | ||
884 | ✗ | fpi_ssm_next_state_delayed (ssm, 10); | |
885 | ✗ | break; | |
886 | |||
887 | ✗ | case M_LOOP_2_ABORT_PRINT: | |
888 | /* Abort print command */ | ||
889 | ✗ | vfs_abort_print (ssm, dev); | |
890 | ✗ | break; | |
891 | |||
892 | ✗ | case M_LOOP_2_LOAD_IMAGE: | |
893 | /* Load abort image */ | ||
894 | ✗ | self->ignore_error = TRUE; | |
895 | ✗ | vfs_img_load (ssm, dev); | |
896 | ✗ | break; | |
897 | |||
898 | ✗ | case M_LOOP_3_GET_PRINT: | |
899 | /* Get empty image */ | ||
900 | ✗ | vfs_get_print (ssm, dev, 0x000a, 0); | |
901 | ✗ | break; | |
902 | |||
903 | ✗ | case M_LOOP_3_LOAD_IMAGE: | |
904 | /* Load abort image */ | ||
905 | ✗ | self->ignore_error = TRUE; | |
906 | ✗ | vfs_img_load (ssm, dev); | |
907 | ✗ | break; | |
908 | |||
909 | ✗ | case M_LOOP_3_CHECK_IMAGE: | |
910 | ✗ | if (self->height == 10) | |
911 | { | ||
912 | /* Image load correctly, jump to step 0 */ | ||
913 | ✗ | self->counter = 0; | |
914 | ✗ | fpi_ssm_jump_to_state (ssm, M_LOOP_0_GET_PRINT); | |
915 | } | ||
916 | ✗ | else if (self->counter < 10) | |
917 | { | ||
918 | /* Wait aborting */ | ||
919 | ✗ | self->counter++; | |
920 | ✗ | fpi_ssm_next_state_delayed (ssm, 100); | |
921 | } | ||
922 | else | ||
923 | { | ||
924 | /* reach max loop counter, return protocol error */ | ||
925 | ✗ | fp_err ("waiting abort reach max loop counter"); | |
926 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
927 | } | ||
928 | break; | ||
929 | |||
930 | ✗ | case M_LOOP_3_LOOP: | |
931 | /* Loop */ | ||
932 | ✗ | fpi_ssm_jump_to_state (ssm, M_LOOP_3_GET_PRINT); | |
933 | ✗ | break; | |
934 | } | ||
935 | } | ||
936 | |||
937 | /* Complete loop sequential state machine */ | ||
938 | static void | ||
939 | ✗ | m_loop_complete (FpiSsm *ssm, FpDevice *dev, GError *error) | |
940 | { | ||
941 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
942 | |||
943 | /* When the loop completes, we have (successfully) deactivated */ | ||
944 | ✗ | if (self->active) | |
945 | ✗ | fpi_image_device_deactivate_complete (FP_IMAGE_DEVICE (dev), | |
946 | error); | ||
947 | |||
948 | ✗ | self->active = FALSE; | |
949 | |||
950 | ✗ | } | |
951 | |||
952 | /* Init ssm states */ | ||
953 | enum { | ||
954 | /* Step 0 - Cleanup device buffer */ | ||
955 | M_INIT_0_RECV_DIRTY, | ||
956 | M_INIT_0_ABORT_PRINT, | ||
957 | M_INIT_0_LOAD_IMAGE, | ||
958 | |||
959 | /* Step 1 - Wait aborting */ | ||
960 | M_INIT_1_GET_PRINT, | ||
961 | M_INIT_1_LOAD_IMAGE, | ||
962 | M_INIT_1_CHECK_IMAGE, | ||
963 | M_INIT_1_LOOP, | ||
964 | |||
965 | /* Step 2 - Handle unexpected finger presence */ | ||
966 | M_INIT_2_GET_STATE, | ||
967 | M_INIT_2_CHECK_STATE, | ||
968 | M_INIT_2_GET_PRINT, | ||
969 | M_INIT_2_LOAD_IMAGE, | ||
970 | M_INIT_2_LOOP, | ||
971 | |||
972 | /* Step 3 - Set parameters */ | ||
973 | M_INIT_3_SET_000E, | ||
974 | M_INIT_3_SET_0011, | ||
975 | M_INIT_3_SET_0076, | ||
976 | M_INIT_3_SET_0078, | ||
977 | M_INIT_3_SET_THRESHOLD, | ||
978 | M_INIT_3_SET_STATE3_COUNT, | ||
979 | M_INIT_3_SET_STATE5_COUNT, | ||
980 | M_INIT_3_SET_INFO_CONTRAST, | ||
981 | M_INIT_3_SET_INFO_RATE, | ||
982 | |||
983 | /* Step 4 - Autocalibrate contrast */ | ||
984 | M_INIT_4_SET_EXPOSURE, | ||
985 | M_INIT_4_SET_CONTRAST, | ||
986 | M_INIT_4_GET_PRINT, | ||
987 | M_INIT_4_LOAD_IMAGE, | ||
988 | M_INIT_4_CHECK_CONTRAST, | ||
989 | |||
990 | /* Step 5 - Set info line parameters */ | ||
991 | M_INIT_5_SET_EXPOSURE, | ||
992 | M_INIT_5_SET_CONTRAST, | ||
993 | M_INIT_5_SET_INFO_CONTRAST, | ||
994 | M_INIT_5_SET_INFO_RATE, | ||
995 | |||
996 | /* Number of states */ | ||
997 | M_INIT_NUM_STATES, | ||
998 | }; | ||
999 | |||
1000 | /* Exec init sequential state machine */ | ||
1001 | static void | ||
1002 | ✗ | m_init_state (FpiSsm *ssm, FpDevice *_dev) | |
1003 | { | ||
1004 | ✗ | FpImageDevice *dev = FP_IMAGE_DEVICE (_dev); | |
1005 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (_dev); | |
1006 | |||
1007 | /* Mark as cancelled when activation collides with deactivation. */ | ||
1008 | ✗ | if (self->deactivate) | |
1009 | { | ||
1010 | ✗ | fpi_ssm_mark_failed (ssm, | |
1011 | g_error_new (G_IO_ERROR, | ||
1012 | G_IO_ERROR_CANCELLED, | ||
1013 | "Initialisation was cancelled")); | ||
1014 | ✗ | return; | |
1015 | } | ||
1016 | |||
1017 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
1018 | { | ||
1019 | ✗ | case M_INIT_0_RECV_DIRTY: | |
1020 | /* Recv eventually dirty data */ | ||
1021 | ✗ | self->ignore_error = TRUE; | |
1022 | ✗ | async_recv (ssm, dev); | |
1023 | ✗ | break; | |
1024 | |||
1025 | ✗ | case M_INIT_0_ABORT_PRINT: | |
1026 | /* Abort print command */ | ||
1027 | ✗ | vfs_abort_print (ssm, dev); | |
1028 | ✗ | break; | |
1029 | |||
1030 | ✗ | case M_INIT_0_LOAD_IMAGE: | |
1031 | /* Load abort image */ | ||
1032 | ✗ | self->ignore_error = TRUE; | |
1033 | ✗ | vfs_img_load (ssm, dev); | |
1034 | ✗ | break; | |
1035 | |||
1036 | ✗ | case M_INIT_1_GET_PRINT: | |
1037 | /* Get empty image */ | ||
1038 | ✗ | vfs_get_print (ssm, dev, 0x000a, 0); | |
1039 | ✗ | break; | |
1040 | |||
1041 | ✗ | case M_INIT_1_LOAD_IMAGE: | |
1042 | /* Load abort image */ | ||
1043 | ✗ | self->ignore_error = TRUE; | |
1044 | ✗ | vfs_img_load (ssm, dev); | |
1045 | ✗ | break; | |
1046 | |||
1047 | ✗ | case M_INIT_1_CHECK_IMAGE: | |
1048 | ✗ | if (self->height == 10) | |
1049 | { | ||
1050 | /* Image load correctly, jump to step 2 */ | ||
1051 | ✗ | self->counter = 0; | |
1052 | ✗ | fpi_ssm_jump_to_state (ssm, M_INIT_2_GET_STATE); | |
1053 | } | ||
1054 | ✗ | else if (self->counter < 10) | |
1055 | { | ||
1056 | /* Wait aborting */ | ||
1057 | ✗ | self->counter++; | |
1058 | ✗ | fpi_ssm_next_state_delayed (ssm, 100); | |
1059 | } | ||
1060 | else | ||
1061 | { | ||
1062 | /* reach max loop counter, return protocol error */ | ||
1063 | ✗ | fp_err ("waiting abort reach max loop counter"); | |
1064 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
1065 | } | ||
1066 | break; | ||
1067 | |||
1068 | ✗ | case M_INIT_1_LOOP: | |
1069 | /* Loop */ | ||
1070 | ✗ | fpi_ssm_jump_to_state (ssm, M_INIT_1_GET_PRINT); | |
1071 | ✗ | break; | |
1072 | |||
1073 | ✗ | case M_INIT_2_GET_STATE: | |
1074 | /* Get finger state */ | ||
1075 | ✗ | vfs_get_finger_state (ssm, dev); | |
1076 | ✗ | break; | |
1077 | |||
1078 | case M_INIT_2_CHECK_STATE: | ||
1079 | /* Check finger state */ | ||
1080 | ✗ | if (vfs_finger_state (self) == VFS_FINGER_PRESENT) | |
1081 | { | ||
1082 | /* Wait a bit for finger removal; if it doesn't happen, prompt */ | ||
1083 | ✗ | if (self->counter < 2) | |
1084 | { | ||
1085 | /* Wait removing finger */ | ||
1086 | ✗ | self->counter++; | |
1087 | ✗ | fpi_ssm_next_state_delayed (ssm, 250); | |
1088 | } | ||
1089 | else | ||
1090 | { | ||
1091 | /* The user should remove their finger from the scanner */ | ||
1092 | ✗ | fp_warn ("unexpected finger find, remove finger from the scanner"); | |
1093 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_retry_new (FP_DEVICE_RETRY_REMOVE_FINGER)); | |
1094 | } | ||
1095 | } | ||
1096 | else | ||
1097 | { | ||
1098 | /* Finger not present */ | ||
1099 | ✗ | if (self->counter == 0) | |
1100 | { | ||
1101 | /* Continue */ | ||
1102 | ✗ | fpi_ssm_jump_to_state (ssm, M_INIT_3_SET_000E); | |
1103 | } | ||
1104 | else | ||
1105 | { | ||
1106 | /* Finger removed, jump to abort */ | ||
1107 | ✗ | self->counter = 0; | |
1108 | ✗ | fpi_ssm_jump_to_state (ssm, M_INIT_0_ABORT_PRINT); | |
1109 | } | ||
1110 | } | ||
1111 | break; | ||
1112 | |||
1113 | ✗ | case M_INIT_2_GET_PRINT: | |
1114 | /* Send get print command to the reader */ | ||
1115 | ✗ | vfs_get_print (ssm, dev, VFS_BUFFER_HEIGHT, 1); | |
1116 | ✗ | break; | |
1117 | |||
1118 | ✗ | case M_INIT_2_LOAD_IMAGE: | |
1119 | /* Load unexpected image */ | ||
1120 | ✗ | self->ignore_error = TRUE; | |
1121 | ✗ | vfs_img_load (ssm, dev); | |
1122 | ✗ | break; | |
1123 | |||
1124 | ✗ | case M_INIT_2_LOOP: | |
1125 | /* Loop */ | ||
1126 | ✗ | fpi_ssm_jump_to_state (ssm, M_INIT_2_GET_STATE); | |
1127 | ✗ | break; | |
1128 | |||
1129 | ✗ | case M_INIT_3_SET_000E: | |
1130 | /* Set param 0x000e, required for take image */ | ||
1131 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_000E, VFS_VAL_000E); | |
1132 | ✗ | break; | |
1133 | |||
1134 | ✗ | case M_INIT_3_SET_0011: | |
1135 | /* Set param 0x0011, required for take image */ | ||
1136 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_0011, VFS_VAL_0011); | |
1137 | ✗ | break; | |
1138 | |||
1139 | ✗ | case M_INIT_3_SET_0076: | |
1140 | /* Set param 0x0076, required for use info line */ | ||
1141 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_0076, VFS_VAL_0076); | |
1142 | ✗ | break; | |
1143 | |||
1144 | ✗ | case M_INIT_3_SET_0078: | |
1145 | /* Set param 0x0078, required for use info line */ | ||
1146 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_0078, VFS_VAL_0078); | |
1147 | ✗ | break; | |
1148 | |||
1149 | ✗ | case M_INIT_3_SET_THRESHOLD: | |
1150 | /* Set threshold */ | ||
1151 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_THRESHOLD, VFS_VAL_THRESHOLD); | |
1152 | ✗ | break; | |
1153 | |||
1154 | ✗ | case M_INIT_3_SET_STATE3_COUNT: | |
1155 | /* Set state 3 count */ | ||
1156 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_STATE_3, VFS_VAL_STATE_3); | |
1157 | ✗ | break; | |
1158 | |||
1159 | ✗ | case M_INIT_3_SET_STATE5_COUNT: | |
1160 | /* Set state 5 count */ | ||
1161 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_STATE_5, VFS_VAL_STATE_5); | |
1162 | ✗ | break; | |
1163 | |||
1164 | ✗ | case M_INIT_3_SET_INFO_CONTRAST: | |
1165 | /* Set info line contrast */ | ||
1166 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_INFO_CONTRAST, 10); | |
1167 | ✗ | break; | |
1168 | |||
1169 | ✗ | case M_INIT_3_SET_INFO_RATE: | |
1170 | /* Set info line rate */ | ||
1171 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_INFO_RATE, 32); | |
1172 | ✗ | break; | |
1173 | |||
1174 | ✗ | case M_INIT_4_SET_EXPOSURE: | |
1175 | /* Set exposure level of reader */ | ||
1176 | ✗ | vfs_poke (ssm, dev, VFS_REG_IMG_EXPOSURE, 0x4000, 0x02); | |
1177 | ✗ | self->counter = 1; | |
1178 | ✗ | break; | |
1179 | |||
1180 | ✗ | case M_INIT_4_SET_CONTRAST: | |
1181 | /* Set contrast level of reader */ | ||
1182 | ✗ | vfs_poke (ssm, dev, VFS_REG_IMG_CONTRAST, self->contrast, 0x01); | |
1183 | ✗ | break; | |
1184 | |||
1185 | ✗ | case M_INIT_4_GET_PRINT: | |
1186 | /* Get empty image */ | ||
1187 | ✗ | vfs_get_print (ssm, dev, 0x000a, 0); | |
1188 | ✗ | break; | |
1189 | |||
1190 | ✗ | case M_INIT_4_LOAD_IMAGE: | |
1191 | /* Load empty image */ | ||
1192 | ✗ | vfs_img_load (ssm, dev); | |
1193 | ✗ | break; | |
1194 | |||
1195 | ✗ | case M_INIT_4_CHECK_CONTRAST: | |
1196 | /* Check contrast */ | ||
1197 | ✗ | vfs_check_contrast (self); | |
1198 | |||
1199 | ✗ | if (self->contrast <= 6 || self->counter >= 12) | |
1200 | { | ||
1201 | /* End contrast scan, continue */ | ||
1202 | ✗ | self->contrast = self->best_contrast; | |
1203 | ✗ | self->counter = 0; | |
1204 | ✗ | fp_dbg ("use contrast value = %d", self->contrast); | |
1205 | ✗ | fpi_ssm_next_state (ssm); | |
1206 | } | ||
1207 | else | ||
1208 | { | ||
1209 | /* Continue contrast scan, loop */ | ||
1210 | ✗ | self->contrast--; | |
1211 | ✗ | self->counter++; | |
1212 | ✗ | fpi_ssm_jump_to_state (ssm, M_INIT_4_SET_CONTRAST); | |
1213 | } | ||
1214 | break; | ||
1215 | |||
1216 | ✗ | case M_INIT_5_SET_EXPOSURE: | |
1217 | /* Set exposure level of reader */ | ||
1218 | ✗ | vfs_poke (ssm, dev, VFS_REG_IMG_EXPOSURE, VFS_VAL_IMG_EXPOSURE, 0x02); | |
1219 | ✗ | break; | |
1220 | |||
1221 | ✗ | case M_INIT_5_SET_CONTRAST: | |
1222 | /* Set contrast level of reader */ | ||
1223 | ✗ | vfs_poke (ssm, dev, VFS_REG_IMG_CONTRAST, self->contrast, 0x01); | |
1224 | ✗ | break; | |
1225 | |||
1226 | ✗ | case M_INIT_5_SET_INFO_CONTRAST: | |
1227 | /* Set info line contrast */ | ||
1228 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_INFO_CONTRAST, self->contrast); | |
1229 | ✗ | break; | |
1230 | |||
1231 | ✗ | case M_INIT_5_SET_INFO_RATE: | |
1232 | /* Set info line rate */ | ||
1233 | ✗ | vfs_set_param (ssm, dev, VFS_PAR_INFO_RATE, VFS_VAL_INFO_RATE); | |
1234 | ✗ | break; | |
1235 | } | ||
1236 | } | ||
1237 | |||
1238 | /* Complete init sequential state machine */ | ||
1239 | static void | ||
1240 | ✗ | m_init_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) | |
1241 | { | ||
1242 | ✗ | FpImageDevice *dev = FP_IMAGE_DEVICE (_dev); | |
1243 | |||
1244 | /* Notify activate complete */ | ||
1245 | ✗ | fpi_image_device_activate_complete (dev, error); | |
1246 | |||
1247 | ✗ | if (!error) | |
1248 | { | ||
1249 | ✗ | FpiSsm *ssm_loop; | |
1250 | |||
1251 | /* Start loop ssm */ | ||
1252 | ✗ | ssm_loop = fpi_ssm_new (FP_DEVICE (dev), m_loop_state, M_LOOP_NUM_STATES); | |
1253 | ✗ | fpi_ssm_start (ssm_loop, m_loop_complete); | |
1254 | } | ||
1255 | |||
1256 | /* Free sequential state machine */ | ||
1257 | ✗ | } | |
1258 | |||
1259 | /* Activate device */ | ||
1260 | static void | ||
1261 | ✗ | dev_activate (FpImageDevice *dev) | |
1262 | { | ||
1263 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
1264 | ✗ | FpiSsm *ssm; | |
1265 | |||
1266 | /* Check if already active */ | ||
1267 | ✗ | g_assert (!self->active); | |
1268 | |||
1269 | /* Set active state */ | ||
1270 | ✗ | self->active = TRUE; | |
1271 | ✗ | self->deactivate = FALSE; | |
1272 | |||
1273 | /* Set contrast */ | ||
1274 | ✗ | self->contrast = 15; | |
1275 | ✗ | self->best_clevel = -1; | |
1276 | |||
1277 | /* Reset loop counter */ | ||
1278 | ✗ | self->counter = 0; | |
1279 | |||
1280 | /* Start init ssm */ | ||
1281 | ✗ | ssm = fpi_ssm_new (FP_DEVICE (dev), m_init_state, M_INIT_NUM_STATES); | |
1282 | ✗ | fpi_ssm_start (ssm, m_init_complete); | |
1283 | ✗ | } | |
1284 | |||
1285 | /* Deactivate device */ | ||
1286 | static void | ||
1287 | ✗ | dev_deactivate (FpImageDevice *dev) | |
1288 | { | ||
1289 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
1290 | |||
1291 | /* Device already deactivated, likely due to an error */ | ||
1292 | ✗ | if (!self->active) | |
1293 | { | ||
1294 | ✗ | fpi_image_device_deactivate_complete (dev, NULL); | |
1295 | ✗ | return; | |
1296 | } | ||
1297 | |||
1298 | /* Signal deactivation, deactivation will happen from the SSM | ||
1299 | * completion handler. */ | ||
1300 | ✗ | self->deactivate = TRUE; | |
1301 | } | ||
1302 | |||
1303 | /* Open device */ | ||
1304 | static void | ||
1305 | ✗ | dev_open (FpImageDevice *dev) | |
1306 | { | ||
1307 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
1308 | ✗ | GError *error = NULL; | |
1309 | |||
1310 | /* Claim usb interface */ | ||
1311 | ✗ | g_usb_device_claim_interface (fpi_device_get_usb_device (FP_DEVICE (dev)), 0, 0, &error); | |
1312 | |||
1313 | /* Initialize private structure */ | ||
1314 | ✗ | self->seqnum = -1; | |
1315 | ✗ | self->buffer = g_malloc0 (VFS_BUFFER_SIZE); | |
1316 | |||
1317 | /* Notify open complete */ | ||
1318 | ✗ | fpi_image_device_open_complete (dev, error); | |
1319 | ✗ | } | |
1320 | |||
1321 | /* Close device */ | ||
1322 | static void | ||
1323 | ✗ | dev_close (FpImageDevice *dev) | |
1324 | { | ||
1325 | ✗ | FpDeviceVfs101 *self = FPI_DEVICE_VFS101 (dev); | |
1326 | ✗ | GError *error = NULL; | |
1327 | |||
1328 | /* Release usb interface */ | ||
1329 | ✗ | g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (dev)), | |
1330 | 0, 0, &error); | ||
1331 | |||
1332 | ✗ | g_clear_pointer (&self->buffer, g_free); | |
1333 | |||
1334 | /* Notify close complete */ | ||
1335 | ✗ | fpi_image_device_close_complete (dev, error); | |
1336 | ✗ | } | |
1337 | |||
1338 | /* Usb id table of device */ | ||
1339 | static const FpIdEntry id_table[] = { | ||
1340 | { .vid = 0x138a, .pid = 0x0001, }, | ||
1341 | { .vid = 0, .pid = 0, .driver_data = 0 }, | ||
1342 | }; | ||
1343 | |||
1344 | static void | ||
1345 | ✗ | fpi_device_vfs101_init (FpDeviceVfs101 *self) | |
1346 | { | ||
1347 | ✗ | } | |
1348 | |||
1349 | static void | ||
1350 | 120 | fpi_device_vfs101_class_init (FpDeviceVfs101Class *klass) | |
1351 | { | ||
1352 | 120 | FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); | |
1353 | 120 | FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass); | |
1354 | |||
1355 | 120 | dev_class->id = "vfs101"; | |
1356 | 120 | dev_class->full_name = "Validity VFS101"; | |
1357 | 120 | dev_class->type = FP_DEVICE_TYPE_USB; | |
1358 | 120 | dev_class->id_table = id_table; | |
1359 | 120 | dev_class->scan_type = FP_SCAN_TYPE_SWIPE; | |
1360 | |||
1361 | 120 | img_class->img_open = dev_open; | |
1362 | 120 | img_class->img_close = dev_close; | |
1363 | 120 | img_class->activate = dev_activate; | |
1364 | 120 | img_class->deactivate = dev_deactivate; | |
1365 | |||
1366 | 120 | img_class->bz3_threshold = 24; | |
1367 | |||
1368 | 120 | img_class->img_width = VFS_IMG_WIDTH; | |
1369 | 120 | img_class->img_height = -1; | |
1370 | } | ||
1371 |