GCC Code Coverage Report


Directory: ./
File: libfprint/drivers/vfs7552.c
Date: 2024-09-16 14:36:32
Exec Total Coverage
Lines: 388 454 85.5%
Functions: 33 33 100.0%
Branches: 99 164 60.4%

Line Branch Exec Source
1 /*
2 * Validity Sensors, Inc. VFS7552 Fingerprint Reader driver for libfprint
3 * Copyright (C) 2013 Arseniy Lartsev <arseniy@chalmers.se>
4 * AceLan Kao <acelan.kao@canonical.com>
5 * 2018 Mark Harfouche <mark.harfouche@gmail.com>
6 * 2020 Julius Piso <julius@piso.at>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #define FP_COMPONENT "vfs7552"
24
25 #include "drivers_api.h"
26 #include "vfs7552_proto.h"
27
28 #define VFS7552_CONTROL_PIXELS (8)
29 #define VFS7552_LINE_SIZE (VFS7552_IMAGE_WIDTH + VFS7552_CONTROL_PIXELS)
30 #define VFS7552_IMAGE_CHUNKS (3)
31
32 #define CAPTURE_VARIANCE_THRESHOLD 1200
33 #define FINGER_OFF_VARIANCE_THRESHOLD 100
34 #define NOISE_VARIANCE_THRESHOLD 4000
35
36 /* =================== sync/async USB transfer sequence ==================== */
37
38 enum {
39 ACTION_SEND,
40 ACTION_RECEIVE,
41 };
42
43 struct usb_action
44 {
45 int type;
46 const char *name;
47 int endpoint;
48 int size;
49 unsigned char *data;
50 int correct_reply_size;
51 };
52
53 #define SEND(ENDPOINT, COMMAND) \
54 { \
55 .type = ACTION_SEND, \
56 .endpoint = ENDPOINT, \
57 .name = #COMMAND, \
58 .size = sizeof (COMMAND), \
59 .data = COMMAND \
60 },
61
62 #define RECV(ENDPOINT, SIZE) \
63 { \
64 .type = ACTION_RECEIVE, \
65 .endpoint = ENDPOINT, \
66 .size = SIZE, \
67 .data = NULL \
68 },
69
70 #define RECV_CHECK(ENDPOINT, SIZE, EXPECTED) \
71 { \
72 .type = ACTION_RECEIVE, \
73 .endpoint = ENDPOINT, \
74 .size = SIZE, \
75 .data = EXPECTED, \
76 .correct_reply_size = sizeof (EXPECTED) \
77 },
78
79 #define RECV_CHECK_SIZE(ENDPOINT, SIZE, EXPECTED) \
80 { \
81 .type = ACTION_RECEIVE, \
82 .endpoint = ENDPOINT, \
83 .size = SIZE, \
84 .data = NULL, \
85 .correct_reply_size = sizeof (EXPECTED) \
86 },
87
88 struct usbexchange_data
89 {
90 int stepcount;
91 struct usb_action *actions;
92 FpiUsbTransfer *last_recv;
93 int timeout;
94 };
95
96 /* ================== Class Definition =================== */
97
98 struct _FpDeviceVfs7552
99 {
100 FpImageDevice parent;
101
102 FpiImageDeviceState dev_state;
103 FpiImageDeviceState dev_state_next;
104 gboolean background_captured;
105 unsigned char background[VFS7552_IMAGE_SIZE];
106 unsigned char image[VFS7552_IMAGE_SIZE];
107 gint lines_captured;
108
109 gboolean deactivating;
110 gboolean loop_running;
111 struct usbexchange_data init_sequence;
112 FpiUsbTransfer *flying_transfer;
113 };
114
115 G_DECLARE_FINAL_TYPE (FpDeviceVfs7552, fpi_device_vfs7552, FPI, DEVICE_VFS7552,
116 FpImageDevice);
117
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 (FpDeviceVfs7552, fpi_device_vfs7552, FP_TYPE_IMAGE_DEVICE);
118
119 /* ======================= States ======================== */
120
121 enum open_states {
122 DEV_OPEN_START,
123 DEV_OPEN_NUM_STATES
124 };
125
126 enum activate_states {
127 ACTIVATE_INIT,
128 ACTIVATE_INTERRUPT_QUERY,
129 ACTIVATE_INTERRUPT_CHECK,
130 ACTIVATE_FINALIZE,
131 ACTIVATE_NUM_STATES
132 };
133
134 enum capture_states {
135 CAPTURE_QUERY_DATA_READY,
136 CAPTURE_CHECK_DATA_READY,
137 CAPTURE_REQUEST_CHUNK,
138 CAPTURE_READ_CHUNK,
139 CAPTURE_COMPLETE,
140 CAPTURE_FINALIZE,
141 CAPTURE_NUM_STATES
142 };
143
144 enum deactivate_states {
145 DEACTIVATE_ENTER,
146 DEACTIVATE_DISABLE_SENSOR,
147 DEACTIVATE_NUM_STATES
148 };
149
150 /* ============== USB Sequence Definitions =============== */
151
152 struct usb_action vfs7552_initialization[] = {
153 SEND (VFS7552_OUT_ENDPOINT, vfs7552_cmd_01)
154 RECV_CHECK_SIZE (VFS7552_IN_ENDPOINT, 64, vfs7552_cmd_01_recv)
155
156 SEND (VFS7552_OUT_ENDPOINT, vfs7552_cmd_19)
157 RECV_CHECK_SIZE (VFS7552_IN_ENDPOINT, 128, vfs7552_cmd_19_recv)
158
159 SEND (VFS7552_OUT_ENDPOINT, vfs7552_init_00)
160 RECV_CHECK (VFS7552_IN_ENDPOINT, 64, VFS7552_NORMAL_REPLY)
161
162 SEND (VFS7552_OUT_ENDPOINT, vfs7552_init_01)
163 RECV_CHECK (VFS7552_IN_ENDPOINT, 64, VFS7552_NORMAL_REPLY)
164
165 SEND (VFS7552_OUT_ENDPOINT, vfs7552_init_02)
166 RECV_CHECK (VFS7552_IN_ENDPOINT, 64, vfs7552_init_02_recv)
167
168 SEND (VFS7552_OUT_ENDPOINT, vfs7552_init_03)
169 RECV_CHECK_SIZE (VFS7552_IN_ENDPOINT, 64, vfs7552_init_03_recv)
170
171 SEND (VFS7552_OUT_ENDPOINT, vfs7552_init_04)
172 RECV_CHECK (VFS7552_IN_ENDPOINT, 64, VFS7552_NORMAL_REPLY)
173
174 /*
175 * Windows driver does this and it works
176 * But in this driver this call never returns...
177 * RECV(VFS7552_IN_ENDPOINT_CTRL2, 8)
178 */
179 };
180
181 struct usb_action vfs7552_stop_capture[] = {
182 SEND (VFS7552_OUT_ENDPOINT, vfs7552_cmd_04)
183 RECV_CHECK (VFS7552_IN_ENDPOINT, 64, VFS7552_NORMAL_REPLY)
184
185 SEND (VFS7552_OUT_ENDPOINT, vfs7552_cmd_52)
186 RECV_CHECK (VFS7552_IN_ENDPOINT, 64, VFS7552_NORMAL_REPLY)
187 };
188
189 struct usb_action vfs7552_initiate_capture[] = {
190 SEND (VFS7552_OUT_ENDPOINT, vfs7552_image_start)
191 RECV_CHECK_SIZE (VFS7552_IN_ENDPOINT, 2048, vfs7552_image_start_resp)
192 };
193
194 struct usb_action vfs7552_wait_finger_init[] = {
195 RECV_CHECK_SIZE (VFS7552_INTERRUPT_ENDPOINT, 8, interrupt_ok)
196 };
197
198 struct usb_action vfs7552_data_ready_query[] = {
199 SEND (VFS7552_OUT_ENDPOINT, vfs7552_is_image_ready)
200 RECV_CHECK_SIZE (VFS7552_IN_ENDPOINT, 64, vfs7552_is_image_ready_resp_ready)
201 };
202
203 struct usb_action vfs7552_request_chunk[] = {
204 SEND (VFS7552_OUT_ENDPOINT, vfs7552_read_image_chunk)
205 };
206
207 /* ================== USB Communication ================== */
208
209 static void
210 2423 async_send_cb (FpiUsbTransfer *transfer, FpDevice *device,
211 gpointer user_data, GError *error)
212 {
213 2423 struct usbexchange_data *data = fpi_ssm_get_data (transfer->ssm);
214 2423 struct usb_action *action;
215
216
1/2
✓ Branch 1 taken 2423 times.
✗ Branch 2 not taken.
2423 g_assert (!(fpi_ssm_get_cur_state (transfer->ssm) >= data->stepcount));
217
218 2423 action = &data->actions[fpi_ssm_get_cur_state (transfer->ssm)];
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2423 times.
2423 g_assert (!(action->type != ACTION_SEND));
220
221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2423 times.
2423 if (error)
222 {
223 fpi_ssm_mark_failed (transfer->ssm, error);
224 return;
225 }
226
227 /* success */
228 2423 fpi_ssm_next_state (transfer->ssm);
229 }
230
231 static void
232 2326 async_recv_cb (FpiUsbTransfer *transfer, FpDevice *device,
233 gpointer user_data, GError *error)
234 {
235 2326 struct usbexchange_data *data = fpi_ssm_get_data (transfer->ssm);
236 2326 struct usb_action *action;
237
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2326 times.
2326 if (error)
239 {
240 fpi_ssm_mark_failed (transfer->ssm, error);
241 return;
242 }
243
244
1/2
✓ Branch 1 taken 2326 times.
✗ Branch 2 not taken.
2326 g_assert (!(fpi_ssm_get_cur_state (transfer->ssm) >= data->stepcount));
245
246 2326 action = &data->actions[fpi_ssm_get_cur_state (transfer->ssm)];
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2326 times.
2326 g_assert (!(action->type != ACTION_RECEIVE));
248
249
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2320 times.
2326 if (action->data != NULL)
250 {
251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (transfer->actual_length != action->correct_reply_size)
252 {
253 fpi_ssm_mark_failed (transfer->ssm,
254 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
255 "Got %d bytes instead of %d",
256 (gint) transfer->actual_length,
257 action->correct_reply_size));
258 return;
259 }
260
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (memcmp (transfer->buffer, action->data,
261 action->correct_reply_size) != 0)
262 {
263 fpi_ssm_mark_failed (transfer->ssm,
264 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
265 "Received a wrong reply from the driver."));
266 return;
267 }
268 }
269 else
270 {
271 2320 fp_dbg ("Got %d bytes out of %d",
272 (gint) transfer->actual_length,
273 (gint) transfer->length);
274 }
275
276 2326 fpi_ssm_next_state (transfer->ssm);
277 }
278
279 static void
280 4749 usbexchange_loop (FpiSsm *ssm, FpDevice *_dev)
281 {
282 4749 struct usbexchange_data *data = fpi_ssm_get_data (ssm);
283 4749 struct usb_action *action = &data->actions[fpi_ssm_get_cur_state (ssm)];
284 4749 FpiUsbTransfer *transfer;
285
286
1/2
✓ Branch 1 taken 4749 times.
✗ Branch 2 not taken.
4749 g_assert (fpi_ssm_get_cur_state (ssm) < data->stepcount);
287
288
2/3
✓ Branch 0 taken 2423 times.
✓ Branch 1 taken 2326 times.
✗ Branch 2 not taken.
4749 switch (action->type)
289 {
290 2423 case ACTION_SEND:
291 2423 fp_dbg ("Sending %s", action->name);
292 2423 transfer = fpi_usb_transfer_new (_dev);
293 2423 fpi_usb_transfer_fill_bulk_full (transfer, action->endpoint,
294 2423 action->data, action->size,
295 NULL);
296 2423 transfer->ssm = ssm;
297 2423 transfer->short_is_error = TRUE;
298 2423 fpi_usb_transfer_submit (transfer, data->timeout, NULL,
299 async_send_cb, NULL);
300 2423 break;
301
302 2326 case ACTION_RECEIVE:
303 2326 fp_dbg ("Receiving %d bytes", action->size);
304 2326 transfer = fpi_usb_transfer_new (_dev);
305 2326 fpi_usb_transfer_fill_bulk (transfer, action->endpoint,
306 2326 action->size);
307 2326 transfer->ssm = ssm;
308 2326 fpi_usb_transfer_submit (transfer, data->timeout, NULL,
309 async_recv_cb, NULL);
310
311
2/2
✓ Branch 0 taken 2320 times.
✓ Branch 1 taken 6 times.
2326 g_clear_pointer (&data->last_recv, fpi_usb_transfer_unref);
312 2326 data->last_recv = fpi_usb_transfer_ref (transfer);
313 2326 break;
314
315 default:
316 g_assert_not_reached ();
317 fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
318 return;
319 }
320 }
321
322 static void
323 2418 usb_exchange_async (FpiSsm *ssm,
324 struct usbexchange_data *data,
325 const char *exchange_name)
326 {
327 2418 FpiSsm *subsm = fpi_ssm_new_full (fpi_ssm_get_device (ssm),
328 usbexchange_loop,
329 data->stepcount,
330 data->stepcount,
331 exchange_name);
332
333 2418 fpi_ssm_set_data (subsm, data, NULL);
334 2418 fpi_ssm_start_subsm (ssm, subsm);
335 2418 }
336
337 /* =========== Image Capturing and Processing ============ */
338
339 enum {
340 CHUNK_READ_FINISHED,
341 CHUNK_READ_NEED_MORE,
342 CHUNK_READ_ERROR
343 };
344
345 static int
346 32 clean_image (FpDeviceVfs7552 *self)
347 {
348 32 fp_dbg ("Cleaning image");
349 32 unsigned int sum = 0;
350
351
2/2
✓ Branch 1 taken 401408 times.
✓ Branch 2 taken 32 times.
401440 for (int i = 0; i < VFS7552_IMAGE_SIZE; i++)
352 {
353
2/2
✓ Branch 0 taken 146928 times.
✓ Branch 1 taken 254480 times.
401408 if (self->background[i] < self->image[i])
354 146928 self->image[i] = 0;
355 else
356 254480 self->image[i] = self->background[i] - self->image[i];
357
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 401392 times.
401408 if ((int) (self->image[i]) * 4 > 255)
358 16 self->image[i] = 255;
359 else
360 401392 self->image[i] *= 4;
361
362 401408 sum += self->image[i];
363 }
364
365
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (sum == 0)
366 {
367 fp_dbg ("frame darker than background; finger present during calibration?");
368 // Retake an image of the background at the next opportunity.
369 self->background_captured = FALSE;
370 return -1;
371 }
372 return 0;
373 }
374
375 static int
376 99 process_chunk (FpDeviceVfs7552 *self, FpiUsbTransfer *transfer)
377 {
378 99 unsigned char *ptr;
379 99 int n_bytes_in_chunk;
380 99 int n_lines;
381 99 int i;
382
383
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 99 times.
99 if (transfer->actual_length < 6)
384 return CHUNK_READ_ERROR;
385
386 99 ptr = transfer->buffer;
387 99 n_bytes_in_chunk = ptr[2] + ptr[3] * 256;
388
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 99 times.
99 if (transfer->actual_length < 6 + n_bytes_in_chunk)
390 return CHUNK_READ_ERROR;
391
392 99 ptr = ptr + 6;
393 99 n_lines = n_bytes_in_chunk / VFS7552_LINE_SIZE;
394
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 99 times.
99 if (n_lines + self->lines_captured > VFS7552_IMAGE_HEIGHT)
395 {
396 g_warning ("Device sent more lines that were expected! Aborting.");
397 return CHUNK_READ_ERROR;
398 }
399
400
2/2
✓ Branch 0 taken 3696 times.
✓ Branch 1 taken 99 times.
3795 for (i = 0; i < n_lines; i++)
401 {
402 3696 ptr = ptr + VFS7552_CONTROL_PIXELS;
403 3696 memcpy (&self->image[self->lines_captured * VFS7552_IMAGE_WIDTH], ptr, VFS7552_IMAGE_WIDTH);
404 3696 ptr = ptr + VFS7552_IMAGE_WIDTH;
405 3696 self->lines_captured += 1;
406 }
407
408
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 33 times.
99 if (self->lines_captured == VFS7552_IMAGE_HEIGHT)
409 return CHUNK_READ_FINISHED;
410 else
411 66 return CHUNK_READ_NEED_MORE;
412 }
413
414 static void
415 99 chunk_capture_callback (FpiUsbTransfer *transfer, FpDevice *device,
416 gpointer user_data, GError *error)
417 {
418 99 FpDeviceVfs7552 *self;
419
420 99 self = FPI_DEVICE_VFS7552 (device);
421
422
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 99 times.
99 if (error)
423 {
424 if (!self->deactivating)
425 {
426 fp_err ("Failed to capture data");
427 fpi_ssm_mark_failed (transfer->ssm, error);
428 }
429 else
430 {
431 // Clear the cancel error, because we are handling deactivation separately
432 g_error_free (error);
433 fpi_ssm_mark_completed (transfer->ssm);
434 }
435 }
436 else
437 {
438
2/4
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
99 switch (process_chunk (self, transfer))
439 {
440 33 case CHUNK_READ_FINISHED:
441 33 fpi_ssm_next_state (transfer->ssm);
442 33 break;
443
444 66 case CHUNK_READ_NEED_MORE:
445 66 fpi_ssm_jump_to_state (transfer->ssm, CAPTURE_REQUEST_CHUNK);
446 66 break;
447
448 case CHUNK_READ_ERROR:
449 fpi_ssm_mark_failed (transfer->ssm,
450 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
451 "Failed to decode image packet of length %d",
452 (int) transfer->actual_length));
453 }
454 }
455 99 }
456
457 static void
458 99 capture_chunk_async (FpiSsm *ssm, FpDevice *_dev, guint timeout)
459 {
460 99 FpiUsbTransfer *transfer;
461
462 99 transfer = fpi_usb_transfer_new (_dev);
463
464 99 fpi_usb_transfer_fill_bulk (transfer, VFS7552_IN_ENDPOINT,
465 VFS7552_RECEIVE_BUF_SIZE);
466
467 99 transfer->ssm = ssm;
468 99 fpi_usb_transfer_submit (transfer, timeout, NULL,
469 chunk_capture_callback, NULL);
470 99 }
471
472 static void
473 33 reset_state (FpDeviceVfs7552 *self)
474 {
475 33 self->lines_captured = 0;
476 }
477
478 /* ================ Run States ================= */
479
480 static void
481 2 deactivate_run_state (FpiSsm *ssm, FpDevice *_dev)
482 {
483 2 FpDeviceVfs7552 *self;
484
485 2 self = FPI_DEVICE_VFS7552 (_dev);
486
487
2/3
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 switch (fpi_ssm_get_cur_state (ssm))
488 {
489 1 case DEACTIVATE_ENTER:
490 1 fpi_ssm_next_state_delayed (ssm, 10);
491 1 break;
492
493 1 case DEACTIVATE_DISABLE_SENSOR:
494 1 self->init_sequence.stepcount = G_N_ELEMENTS (vfs7552_stop_capture);
495 1 self->init_sequence.actions = vfs7552_stop_capture;
496 1 self->init_sequence.timeout = 1000;
497 1 usb_exchange_async (ssm, &self->init_sequence, "STOP CAPTURE");
498 1 break;
499 }
500 2 }
501
502 static void
503 4859 capture_run_state (FpiSsm *ssm, FpDevice *_dev)
504 {
505 4859 FpDeviceVfs7552 *self;
506 4859 unsigned char *receive_buf;
507 4859 int variance_before;
508 4859 int variance_after;
509
510 4859 self = FPI_DEVICE_VFS7552 (_dev);
511
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4859 times.
4859 if (self->deactivating)
512 {
513 fp_dbg ("deactivating, marking completed");
514 fpi_ssm_mark_completed (ssm);
515 return;
516 }
517
5/7
✓ Branch 1 taken 2314 times.
✓ Branch 2 taken 2314 times.
✓ Branch 3 taken 99 times.
✓ Branch 4 taken 99 times.
✓ Branch 5 taken 33 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4859 switch (fpi_ssm_get_cur_state (ssm))
518 {
519 2314 case CAPTURE_QUERY_DATA_READY:
520 2314 self->init_sequence.stepcount = G_N_ELEMENTS (vfs7552_data_ready_query);
521 2314 self->init_sequence.actions = vfs7552_data_ready_query;
522 2314 self->init_sequence.timeout = 0; // Do not time out
523 2314 usb_exchange_async (ssm, &self->init_sequence, "QUERY DATA READY");
524 2314 break;
525
526 2314 case CAPTURE_CHECK_DATA_READY:
527 2314 receive_buf = ((unsigned char *) self->init_sequence.last_recv->buffer);
528
529
2/2
✓ Branch 0 taken 2281 times.
✓ Branch 1 taken 33 times.
2314 if (receive_buf[0] == vfs7552_is_image_ready_resp_not_ready[0])
530 {
531 2281 fpi_ssm_jump_to_state (ssm, CAPTURE_QUERY_DATA_READY);
532 }
533
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
33 else if (receive_buf[0] == vfs7552_is_image_ready_resp_ready[0])
534 {
535 33 reset_state (self);
536 33 fpi_ssm_next_state (ssm);
537 }
538 else if (receive_buf[0] == vfs7552_is_image_ready_resp_finger_off[0])
539 {
540 fp_dbg ("finger off response received");
541 if (self->dev_state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
542 {
543 reset_state (self);
544 fpi_ssm_next_state (ssm);
545 }
546 else
547 {
548 fpi_ssm_jump_to_state (ssm, CAPTURE_FINALIZE);
549 }
550 }
551 else
552 {
553 fpi_ssm_mark_failed (ssm,
554 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
555 "Unknown response 0x%02x",
556 receive_buf[0]));
557 }
558 break;
559
560 99 case CAPTURE_REQUEST_CHUNK:
561 99 self->init_sequence.stepcount = G_N_ELEMENTS (vfs7552_request_chunk);
562 99 self->init_sequence.actions = vfs7552_request_chunk;
563 99 self->init_sequence.timeout = 1000;
564 99 usb_exchange_async (ssm, &self->init_sequence, "REQUEST CHUNK");
565 99 break;
566
567 99 case CAPTURE_READ_CHUNK:
568 99 capture_chunk_async (ssm, _dev, 1000);
569 99 break;
570
571 33 case CAPTURE_COMPLETE:
572 // Store the image as a background reference, if the variance is below the finger off threshold.
573
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 32 times.
33 if (!self->background_captured)
574 {
575 // Calculate the variance of the captured image
576 1 variance_before = fpi_std_sq_dev (self->image, VFS7552_IMAGE_SIZE);
577 1 fp_dbg ("variance_before = %d\n", variance_before);
578 1 self->background_captured = TRUE;
579 1 memcpy (self->background, self->image, VFS7552_IMAGE_SIZE);
580 1 fp_dbg ("background stored");
581 1 fpi_ssm_jump_to_state (ssm, CAPTURE_QUERY_DATA_READY);
582 1 break;
583 }
584
585 32 clean_image (self);
586 32 variance_after = fpi_std_sq_dev (self->image, VFS7552_IMAGE_SIZE);
587 32 fp_dbg ("variance_after = %d\n", variance_after);
588
589
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 10 times.
32 if (self->dev_state == FPI_IMAGE_DEVICE_STATE_CAPTURE ||
590 self->dev_state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
591 {
592 // If the finger is placed on the sensor, the variance should ideally increase above a certain
593 // threshold. Otherwise request a new image and test again. Additionally we want to ensure
594 // that we don't capture prints with a way too high noise level (this sometimes happens).
595
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
22 if (variance_after > CAPTURE_VARIANCE_THRESHOLD && variance_after < NOISE_VARIANCE_THRESHOLD)
596 2 fpi_ssm_mark_completed (ssm);
597 else
598 20 fpi_ssm_jump_to_state (ssm, CAPTURE_QUERY_DATA_READY);
599 }
600
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 else if (self->dev_state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
601 {
602 // If the finger is removed from the sensor, the variance should ideally drop below a certain
603 // threshold. Otherwise request a new image and test again.
604
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
10 if (variance_after < FINGER_OFF_VARIANCE_THRESHOLD)
605 1 fpi_ssm_mark_completed (ssm);
606 else
607 9 fpi_ssm_jump_to_state (ssm, CAPTURE_QUERY_DATA_READY);
608 }
609 break;
610
611 case CAPTURE_FINALIZE:
612 fpi_ssm_mark_completed (ssm);
613 break;
614 }
615 }
616
617 static void
618 6 activate_run_state (FpiSsm *ssm, FpDevice *_dev)
619 {
620 6 FpDeviceVfs7552 *self;
621 6 unsigned char *receive_buf;
622
623 6 self = FPI_DEVICE_VFS7552 (_dev);
624
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (self->deactivating)
625 {
626 fp_dbg ("deactivating, marking completed");
627 fpi_ssm_mark_completed (ssm);
628 return;
629 }
630
631
4/5
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
6 switch (fpi_ssm_get_cur_state (ssm))
632 {
633 1 case ACTIVATE_INIT:
634 // This sequence prepares the sensor for capturing the image.
635 1 self->init_sequence.stepcount = G_N_ELEMENTS (vfs7552_initiate_capture);
636 1 self->init_sequence.actions = vfs7552_initiate_capture;
637 1 self->init_sequence.timeout = VFS7552_DEFAULT_WAIT_TIMEOUT;
638 1 usb_exchange_async (ssm, &self->init_sequence, "ACTIVATE INIT");
639 1 break;
640
641 2 case ACTIVATE_INTERRUPT_QUERY:
642 // This sequence configures the sensor to listen to finger placement events.
643 2 self->init_sequence.stepcount = G_N_ELEMENTS (vfs7552_wait_finger_init);
644 2 self->init_sequence.actions = vfs7552_wait_finger_init;
645 2 self->init_sequence.timeout = 0; // Do not time out
646 2 usb_exchange_async (ssm, &self->init_sequence, "ACTIVATE INTERRUPT QUERY");
647 2 break;
648
649 2 case ACTIVATE_INTERRUPT_CHECK:
650 2 receive_buf = ((unsigned char *) self->init_sequence.last_recv->buffer);
651
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (receive_buf[0] == interrupt_ok[0])
652 {
653 // This seems to mean: "Sensor is all good"
654 1 fpi_ssm_jump_to_state (ssm, ACTIVATE_INTERRUPT_QUERY);
655 }
656
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (receive_buf[0] == interrupt_ready[0])
657 {
658 // This seems to mean: "We detected a finger"
659 1 fpi_ssm_next_state (ssm);
660 }
661 else if (receive_buf[0] == interrupt_dont_ask[0])
662 {
663 // This seems to mean: "We already told you we detected a finger, stop asking us"
664 // It will not respond to another request on the interrupt endpoint
665 fpi_ssm_next_state (ssm);
666 }
667 else
668 {
669 fp_dbg ("Unknown response 0x%02x", receive_buf[0]);
670 fpi_ssm_next_state (ssm);
671 }
672 break;
673
674 1 case ACTIVATE_FINALIZE:
675 1 fpi_ssm_mark_completed (ssm);
676 1 break;
677 }
678 }
679
680 static void
681 1 open_run_state (FpiSsm *ssm, FpDevice *_dev)
682 {
683 1 FpDeviceVfs7552 *self;
684
685 1 self = FPI_DEVICE_VFS7552 (_dev);
686
687
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 switch (fpi_ssm_get_cur_state (ssm))
688 {
689 1 case DEV_OPEN_START:
690 1 self->init_sequence.stepcount = G_N_ELEMENTS (vfs7552_initialization);
691 1 self->init_sequence.actions = vfs7552_initialization;
692 1 self->init_sequence.timeout = VFS7552_DEFAULT_WAIT_TIMEOUT;
693 1 usb_exchange_async (ssm, &self->init_sequence, "DEVICE OPEN");
694 1 break;
695 }
696 1 }
697
698 /* ============= SSM Finalization Functions ============== */
699
700 static void
701 1 deactivation_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
702 {
703 1 FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
704 1 FpDeviceVfs7552 *self;
705
706 1 self = FPI_DEVICE_VFS7552 (_dev);
707
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&self->init_sequence.last_recv, fpi_usb_transfer_unref);
708
709 1 self->dev_state = FPI_IMAGE_DEVICE_STATE_INACTIVE;
710 1 self->loop_running = FALSE;
711
712
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (self->deactivating)
713 {
714 self->deactivating = FALSE;
715 fpi_image_device_deactivate_complete (dev, error);
716 return;
717 }
718
719
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!error)
720 1 fpi_image_device_report_finger_status (dev, FALSE);
721 else
722 fpi_image_device_session_error (dev, error);
723 }
724
725 static void
726 1 start_deactivation (FpDevice *_dev)
727 {
728 1 FpDeviceVfs7552 *self = FPI_DEVICE_VFS7552 (_dev);
729 1 FpiSsm *ssm;
730
731 1 self->loop_running = TRUE;
732 1 self->dev_state = FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF;
733 1 ssm = fpi_ssm_new (_dev, deactivate_run_state, DEACTIVATE_NUM_STATES);
734 1 fpi_ssm_start (ssm, deactivation_complete);
735 1 }
736
737 static void
738 1 report_finger_off (FpiSsm *ssm, FpDevice *_dev, GError *error)
739 {
740 1 FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
741 1 FpDeviceVfs7552 *self;
742
743 1 self = FPI_DEVICE_VFS7552 (_dev);
744
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&self->init_sequence.last_recv, fpi_usb_transfer_unref);
745
746
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!error)
747 1 start_deactivation (_dev);
748 else
749 fpi_image_device_session_error (dev, error);
750 1 }
751
752 static void
753 1 start_report_finger_off (FpDevice *_dev)
754 {
755 1 FpDeviceVfs7552 *self = FPI_DEVICE_VFS7552 (_dev);
756 1 FpiSsm *ssm;
757
758 1 self->loop_running = TRUE;
759 1 ssm = fpi_ssm_new (_dev, capture_run_state, CAPTURE_NUM_STATES);
760 1 fpi_ssm_start (ssm, report_finger_off);
761 1 }
762
763 static void
764 1 capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
765 {
766 1 FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
767 1 FpDeviceVfs7552 *self;
768
769 1 self = FPI_DEVICE_VFS7552 (_dev);
770
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&self->init_sequence.last_recv, fpi_usb_transfer_unref);
771
772
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (!self->deactivating && !error)
773 {
774 1 FpImage *img;
775 1 img = fp_image_new (2 * VFS7552_IMAGE_WIDTH,
776 2 * VFS7552_IMAGE_HEIGHT);
777 // Scale the image
778
2/2
✓ Branch 1 taken 112 times.
✓ Branch 2 taken 1 times.
113 for (int j = 0; j < VFS7552_IMAGE_HEIGHT; j++)
779 {
780
2/2
✓ Branch 0 taken 12544 times.
✓ Branch 1 taken 112 times.
12656 for (int i = 0; i < VFS7552_IMAGE_WIDTH; i++)
781 {
782 12544 int ref = j * VFS7552_IMAGE_WIDTH + i;
783 12544 int ref_new = 4 * j * VFS7552_IMAGE_WIDTH + 2 * i;
784 12544 img->data[ref_new] = self->image[ref];
785 12544 img->data[ref_new + 1] = self->image[ref];
786 12544 img->data[ref_new + 2 * VFS7552_IMAGE_WIDTH] = self->image[ref];
787 12544 img->data[ref_new + 2 * VFS7552_IMAGE_WIDTH + 1] = self->image[ref];
788 }
789 }
790
791 1 fp_dbg ("Image captured");
792 1 fpi_image_device_image_captured (dev, img);
793 }
794 1 self->loop_running = FALSE;
795
796
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (self->deactivating)
797 start_deactivation (_dev);
798
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 else if (error)
799 fpi_image_device_session_error (dev, error);
800 1 }
801
802 static void
803 1 start_capture (FpDevice *_dev)
804 {
805 1 FpDeviceVfs7552 *self = FPI_DEVICE_VFS7552 (_dev);
806 1 FpiSsm *ssm;
807
808 1 self->loop_running = TRUE;
809 1 ssm = fpi_ssm_new (_dev, capture_run_state, CAPTURE_NUM_STATES);
810 1 fpi_ssm_start (ssm, capture_complete);
811 1 }
812
813 static void
814 1 report_finger_on_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
815 {
816 1 FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
817 1 FpDeviceVfs7552 *self;
818
819 1 self = FPI_DEVICE_VFS7552 (_dev);
820
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&self->init_sequence.last_recv, fpi_usb_transfer_unref);
821
822
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (!self->deactivating && !error)
823 1 fpi_image_device_report_finger_status (dev, TRUE);
824 1 self->loop_running = FALSE;
825
826
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (self->deactivating)
827 start_deactivation (_dev);
828
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 else if (error)
829 fpi_image_device_session_error (dev, error);
830 1 }
831
832 static void
833 1 activate_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
834 {
835 1 FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
836 1 FpDeviceVfs7552 *self;
837
838 1 self = FPI_DEVICE_VFS7552 (_dev);
839
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&self->init_sequence.last_recv, fpi_usb_transfer_unref);
840
841
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (!self->deactivating && !error)
842 {
843 1 ssm = fpi_ssm_new (_dev, capture_run_state, CAPTURE_NUM_STATES);
844 1 fpi_ssm_start (ssm, report_finger_on_complete);
845 1 return;
846 }
847
848 if (self->deactivating)
849 {
850 start_deactivation (_dev);
851 }
852 else if (error)
853 {
854 self->loop_running = FALSE;
855 fpi_image_device_session_error (dev, error);
856 }
857 }
858
859 static void
860 1 start_report_finger_on (FpDevice *_dev)
861 {
862 1 FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
863 1 FpDeviceVfs7552 *self;
864
865 1 self = FPI_DEVICE_VFS7552 (dev);
866 1 FpiSsm *ssm;
867
868 1 self->deactivating = FALSE;
869 1 self->loop_running = TRUE;
870 1 ssm = fpi_ssm_new (_dev, activate_run_state, ACTIVATE_NUM_STATES);
871 1 fpi_ssm_start (ssm, activate_complete);
872 1 }
873
874 static void
875 3 validity_change_state (FpDevice *_dev, void *data)
876 {
877 3 FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
878 3 FpDeviceVfs7552 *self;
879
880 3 self = FPI_DEVICE_VFS7552 (dev);
881 3 FpiImageDeviceState next_state = self->dev_state_next;
882
883
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (self->dev_state == next_state)
884 {
885 fp_dbg ("already in %d", next_state);
886 return;
887 }
888 else
889 {
890 3 fp_dbg ("changing to %d", next_state);
891 }
892
893
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
3 switch (next_state)
894 {
895 1 case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
896 1 self->dev_state = FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON;
897 1 start_report_finger_on (_dev);
898 1 break;
899
900 1 case FPI_IMAGE_DEVICE_STATE_CAPTURE:
901 1 self->dev_state = FPI_IMAGE_DEVICE_STATE_CAPTURE;
902 1 start_capture (_dev);
903 1 break;
904
905 1 case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF:
906 1 self->dev_state = FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF;
907 1 start_report_finger_off (_dev);
908 1 break;
909
910 /* Ignored States */
911 case FPI_IMAGE_DEVICE_STATE_INACTIVE:
912 case FPI_IMAGE_DEVICE_STATE_ACTIVATING:
913 case FPI_IMAGE_DEVICE_STATE_DEACTIVATING:
914 case FPI_IMAGE_DEVICE_STATE_IDLE:
915 break;
916 }
917 }
918
919 static void
920 1 open_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
921 {
922 1 FpImageDevice *dev = FP_IMAGE_DEVICE (_dev);
923 1 FpDeviceVfs7552 *self;
924
925 1 self = FPI_DEVICE_VFS7552 (_dev);
926
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&self->init_sequence.last_recv, fpi_usb_transfer_unref);
927
928 1 fpi_image_device_open_complete (dev, error);
929 1 }
930
931 /* ================== Driver Entrypoints =================== */
932
933 static void
934 1 dev_close (FpImageDevice *dev)
935 {
936 1 GError *error = NULL;
937
938 1 g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (dev)),
939 0, 0, &error);
940
941 1 fpi_image_device_close_complete (dev, error);
942 1 }
943
944 static void
945 1 dev_deactivate (FpImageDevice *dev)
946 {
947 1 FpDeviceVfs7552 *self;
948
949 1 self = FPI_DEVICE_VFS7552 (dev);
950
951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (self->loop_running)
952 self->deactivating = TRUE;
953 else
954 1 fpi_image_device_deactivate_complete (dev, NULL);
955 1 }
956
957 static void
958 3 delayed_change_state (FpDevice *_dev, FpiImageDeviceState state)
959 {
960 3 GSource *timeout;
961 3 char *name;
962
963 // schedule state change instead of calling it directly to allow all actions
964 // related to the previous state to complete
965 3 timeout = fpi_device_add_timeout (_dev, 50,
966 validity_change_state,
967 NULL, NULL);
968
969 3 name = g_strdup_printf ("dev_change_state to %d", state);
970 3 g_source_set_name (timeout, name);
971 3 g_free (name);
972 3 }
973
974 static void
975 8 dev_change_state (FpImageDevice *dev, FpiImageDeviceState state)
976 {
977 8 FpDeviceVfs7552 *self;
978
979 8 self = FPI_DEVICE_VFS7552 (dev);
980
981
4/5
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
8 switch (state)
982 {
983 1 case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
984 1 self->dev_state_next = FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON;
985 1 delayed_change_state (FP_DEVICE (dev), state);
986 1 break;
987
988 1 case FPI_IMAGE_DEVICE_STATE_CAPTURE:
989 1 self->dev_state_next = FPI_IMAGE_DEVICE_STATE_CAPTURE;
990 1 delayed_change_state (FP_DEVICE (dev), state);
991 1 break;
992
993 1 case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF:
994 1 self->dev_state_next = FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF;
995 1 delayed_change_state (FP_DEVICE (dev), state);
996 1 break;
997
998 /* Ignored States */
999 case FPI_IMAGE_DEVICE_STATE_INACTIVE:
1000 case FPI_IMAGE_DEVICE_STATE_ACTIVATING:
1001 case FPI_IMAGE_DEVICE_STATE_DEACTIVATING:
1002 case FPI_IMAGE_DEVICE_STATE_IDLE:
1003 break;
1004
1005 default:
1006 g_assert_not_reached ();
1007 }
1008 8 }
1009
1010 static void
1011 1 dev_activate (FpImageDevice *dev)
1012 {
1013 1 fpi_image_device_activate_complete (dev, NULL);
1014 1 }
1015
1016 static void
1017 1 dev_open (FpImageDevice *dev)
1018 {
1019 1 FpiSsm *ssm;
1020 1 GError *error = NULL;
1021
1022 // First we need to reset the device, otherwise opening will fail at state 13
1023
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!g_usb_device_reset (fpi_device_get_usb_device (FP_DEVICE (dev)), &error))
1024 {
1025 fpi_image_device_open_complete (dev, error);
1026 return;
1027 }
1028
1029
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!g_usb_device_claim_interface (fpi_device_get_usb_device (FP_DEVICE (dev)), 0, 0, &error))
1030 {
1031 fpi_image_device_open_complete (dev, error);
1032 return;
1033 }
1034
1035 1 ssm = fpi_ssm_new (FP_DEVICE (dev), open_run_state, DEV_OPEN_NUM_STATES);
1036 1 fpi_ssm_start (ssm, open_complete);
1037 }
1038
1039 static const FpIdEntry id_table[] = {
1040 /* Validity device from some Dell XPS laptops (9560, 9360 at least) */
1041 { .vid = 0x138a, .pid = 0x0091 },
1042 { .vid = 0x0, .pid = 0x0 },
1043 };
1044
1045 static void
1046 1 fpi_device_vfs7552_init (FpDeviceVfs7552 *self)
1047 {
1048 1 }
1049
1050 static void
1051 120 fpi_device_vfs7552_class_init (FpDeviceVfs7552Class *klass)
1052 {
1053 120 FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
1054 120 FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
1055
1056 120 dev_class->id = "vfs7552";
1057 120 dev_class->full_name = "Validity VFS7552";
1058 120 dev_class->type = FP_DEVICE_TYPE_USB;
1059 120 dev_class->id_table = id_table;
1060 120 dev_class->scan_type = FP_SCAN_TYPE_PRESS;
1061
1062 120 img_class->img_close = dev_close;
1063 120 img_class->deactivate = dev_deactivate;
1064 120 img_class->change_state = dev_change_state;
1065 120 img_class->activate = dev_activate;
1066 120 img_class->img_open = dev_open;
1067
1068 120 img_class->bz3_threshold = 20;
1069
1070 120 img_class->img_width = VFS7552_IMAGE_WIDTH;
1071 120 img_class->img_height = VFS7552_IMAGE_HEIGHT;
1072 }
1073