GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 80.6% 611 / 0 / 758
Functions: 96.2% 50 / 0 / 52
Branches: 50.8% 184 / 0 / 362

libfprint/drivers/egismoc/egis_etu905.c
Line Branch Exec Source
1 /*
2 * Driver for Egis Technology (LighTuning) Match-On-Chip sensors
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #define FP_COMPONENT "egis_etu905"
20
21 #include <stdio.h>
22 #include <glib.h>
23 #include <sys/param.h>
24
25 #include "drivers_api.h"
26 #include "fpi-byte-writer.h"
27
28 #include "egis_etu905.h"
29
30 struct _FpiDeviceEgisEtu905
31 {
32 FpDevice parent;
33 FpiSsm *task_ssm;
34 FpiSsm *identify_cancel_ssm;
35 GPtrArray *enrolled_ids;
36 gint max_enroll_stages;
37 gboolean identify_started; /* TRUE after cmd_sensor_identify is sent */
38 };
39
40
4/6
fpi_device_egis_etu905_class_intern_init:
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 127 times.
fpi_device_egis_etu905_get_type:
✓ Branch 2 → 3 taken 127 times.
✓ Branch 2 → 7 taken 24 times.
✓ Branch 4 → 5 taken 127 times.
✗ Branch 4 → 7 not taken.
405 G_DEFINE_TYPE (FpiDeviceEgisEtu905, fpi_device_egis_etu905, FP_TYPE_DEVICE);
41
42 static const FpIdEntry egis_etu905_id_table[] = {
43 { .vid = 0x1c7a, .pid = 0x05ae, .driver_data = EGIS_ETU905_DRIVER_CHECK_PREFIX_TYPE1 },
44 { .vid = 0x1c7a, .pid = 0x9201, .driver_data = EGIS_ETU905_DRIVER_CHECK_PREFIX_TYPE1 },
45 { .vid = 0, .pid = 0, .driver_data = 0 }
46 };
47
48 typedef void (*SynCmdMsgCallback) (FpDevice *device,
49 guchar *buffer_in,
50 gsize length_in,
51 GError *error);
52
53 typedef struct egis_etu905_command_data
54 {
55 SynCmdMsgCallback callback;
56 FpiUsbTransfer *cmd_transfer;
57 GCancellable *cancellable;
58 guchar *buffer_in;
59 gsize length_in;
60 } CommandData;
61
62 static void
63 87 egis_etu905_command_data_free (CommandData *data)
64 {
65
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 87 times.
87 g_clear_pointer (&data->cmd_transfer, fpi_usb_transfer_unref);
66
2/2
✓ Branch 4 → 5 taken 81 times.
✓ Branch 4 → 6 taken 6 times.
87 g_clear_object (&data->cancellable);
67 87 g_free (data->buffer_in);
68 87 g_free (data);
69 87 }
70
71 typedef struct egis_etu905_enroll_print
72 {
73 FpPrint *print;
74 int stage;
75 } EnrollPrint;
76
77 static void egis_etu905_exec_cmd_full (FpDevice *device,
78 const guchar *cmd,
79 gsize cmd_length,
80 SynCmdMsgCallback callback,
81 GCancellable *cancellable);
82
83 static void egis_etu905_cancel (FpDevice *device);
84
85 static void egis_etu905_cancel_cb (FpDevice *device,
86 guchar *buffer_in,
87 gsize length_in,
88 GError *error);
89
90 static void
91 20 egis_etu905_finger_on_sensor_cb (FpiUsbTransfer *transfer,
92 FpDevice *device,
93 gpointer userdata,
94 GError *error)
95 {
96 20 fp_dbg ("Finger on sensor callback");
97 20 fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT);
98
99 20 fpi_ssm_usb_transfer_cb (transfer, device, userdata, error);
100 20 }
101
102 static void
103 20 egis_etu905_wait_finger_on_sensor (FpiSsm *ssm,
104 FpDevice *device)
105 {
106 20 fp_dbg ("Wait for finger on sensor");
107 20 g_autoptr(FpiUsbTransfer) transfer = fpi_usb_transfer_new (device);
108
109 20 fpi_usb_transfer_fill_interrupt (transfer, EGIS_ETU905_EP_CMD_INTERRUPT_IN,
110 EGIS_ETU905_USB_INTERRUPT_IN_RECV_LENGTH);
111 20 transfer->ssm = ssm;
112 /* Interrupt on this device always returns 1 byte short; this is expected */
113 20 fpi_usb_transfer_set_short_error (transfer, FALSE);
114
115 20 fpi_device_report_finger_status (device, FP_FINGER_STATUS_NEEDED);
116
117 20 fpi_usb_transfer_submit (g_steal_pointer (&transfer),
118 EGIS_ETU905_USB_INTERRUPT_TIMEOUT,
119 fpi_device_get_cancellable (device),
120 egis_etu905_finger_on_sensor_cb,
121 NULL);
122 20 }
123
124 static gboolean
125 28 egis_etu905_validate_response_prefix (const guchar *buffer_in,
126 const gsize buffer_in_len,
127 const guchar *valid_prefix,
128 const gsize valid_prefix_len)
129 {
130 28 FpiByteReader reader;
131 28 const guint8 *data = NULL;
132 28 gboolean result;
133
134 28 fpi_byte_reader_init (&reader, buffer_in, buffer_in_len);
135
136
1/2
✓ Branch 3 → 4 taken 28 times.
✗ Branch 3 → 7 not taken.
28 if (!fpi_byte_reader_set_pos (&reader, G_N_ELEMENTS (egis_etu905_read_prefix) +
137 EGIS_ETU905_CHECK_BYTES_LENGTH) ||
138
1/2
✓ Branch 5 → 6 taken 28 times.
✗ Branch 5 → 7 not taken.
28 !fpi_byte_reader_get_data (&reader, valid_prefix_len, &data))
139 {
140 fp_dbg ("Response too short for prefix validation");
141 return FALSE;
142 }
143
144 28 result = memcmp (data, valid_prefix, valid_prefix_len) == 0;
145
146
2/2
✓ Branch 6 → 9 taken 5 times.
✓ Branch 6 → 10 taken 23 times.
28 fp_dbg ("Response prefix valid: %s", result ? "yes" : "NO");
147 28 return result;
148 }
149
150 static gboolean
151 33 egis_etu905_validate_response_suffix (const guchar *buffer_in,
152 const gsize buffer_in_len,
153 const guchar *valid_suffix,
154 const gsize valid_suffix_len)
155 {
156 33 FpiByteReader reader;
157 33 const guint8 *data = NULL;
158 33 gboolean result;
159
160
1/2
✓ Branch 2 → 3 taken 33 times.
✗ Branch 2 → 8 not taken.
33 fpi_byte_reader_init (&reader, buffer_in, buffer_in_len);
161
162 /* Guard against unsigned underflow before computing the suffix position. */
163
2/4
✓ Branch 2 → 3 taken 33 times.
✗ Branch 2 → 8 not taken.
✓ Branch 4 → 5 taken 33 times.
✗ Branch 4 → 8 not taken.
66 if (valid_suffix_len > buffer_in_len ||
164 33 !fpi_byte_reader_set_pos (&reader, buffer_in_len - valid_suffix_len) ||
165
1/2
✓ Branch 6 → 7 taken 33 times.
✗ Branch 6 → 8 not taken.
33 !fpi_byte_reader_get_data (&reader, valid_suffix_len, &data))
166 {
167 fp_dbg ("Response too short for suffix validation");
168 return FALSE;
169 }
170
171 33 result = memcmp (data, valid_suffix, valid_suffix_len) == 0;
172
173
2/2
✓ Branch 7 → 10 taken 12 times.
✓ Branch 7 → 11 taken 21 times.
33 fp_dbg ("Response suffix valid: %s", result ? "yes" : "NO");
174 33 return result;
175 }
176
177 static gboolean
178 12 egis_etu905_maybe_cancel (FpDevice *device,
179 GError *error)
180 {
181 12 FpiDeviceAction action = fpi_device_get_current_action (device);
182
183
3/4
✓ Branch 4 → 5 taken 10 times.
✓ Branch 4 → 8 taken 2 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 11 taken 10 times.
22 if (!fpi_device_action_is_cancelled (device) &&
184 10 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
185 return FALSE;
186
187 2 if (action != FPI_DEVICE_ACTION_ENROLL &&
188
1/2
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 11 not taken.
2 action != FPI_DEVICE_ACTION_IDENTIFY)
189 return FALSE;
190
191 2 egis_etu905_cancel (device);
192 2 return TRUE;
193 }
194
195 static void
196 12 egis_etu905_task_ssm_done (FpiSsm *ssm,
197 FpDevice *device,
198 GError *error)
199 {
200 12 g_autoptr(GError) task_error = g_steal_pointer (&error);
201 12 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
202 12 gboolean is_cancellation;
203
204 12 fp_dbg ("Task SSM done");
205
206 /* task_ssm is going to be freed by completion of SSM */
207
2/4
✓ Branch 3 → 4 taken 12 times.
✗ Branch 3 → 6 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 12 times.
12 g_assert (!self->task_ssm || self->task_ssm == ssm);
208
209 /* Check if this is a cancellation before clearing task_ssm, so we can
210 * distinguish between "identify completed normally" and "identify was
211 * cancelled". The identify_cancel_ssm_done callback needs to know this
212 * to report the correct result. */
213 12 is_cancellation = egis_etu905_maybe_cancel (device, task_error);
214
215 12 self->task_ssm = NULL;
216
2/2
✓ Branch 7 → 8 taken 7 times.
✓ Branch 7 → 9 taken 5 times.
12 g_clear_pointer (&self->enrolled_ids, g_ptr_array_unref);
217
218 /* On cancellation we need to send the device-side cancel command before
219 * reporting the error, as the idle ->cancel vfunc may not be called due
220 * to the operation already being completed by the cancellable abort. */
221
2/2
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 12 taken 10 times.
12 if (is_cancellation)
222
1/2
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 14 not taken.
2 return;
223
224
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 10 times.
10 if (task_error)
225 fpi_device_action_error (device, g_steal_pointer (&task_error));
226 }
227
228 static void
229 1 egis_etu905_commit_start_cb (FpDevice *device,
230 guchar *buffer_in,
231 gsize length_in,
232 GError *error)
233 {
234 1 fp_dbg ("Task SSM commit start callback");
235 1 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
236
237
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 if (error)
238 fpi_ssm_mark_failed (self->task_ssm, error);
239 else
240 1 fpi_ssm_jump_to_state (self->task_ssm, ENROLL_COMMIT);
241 1 }
242
243 /*
244 * Generic callback for commands that don't need special handling on success.
245 * Advances the task SSM to the next state on success, or marks it failed on error.
246 */
247 static void
248 46 egis_etu905_task_ssm_next_state_cb (FpDevice *device,
249 guchar *buffer_in,
250 gsize length_in,
251 GError *error)
252 {
253 46 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
254
255 46 fp_dbg ("Task SSM next state callback");
256
257
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 46 times.
46 if (error)
258 fpi_ssm_mark_failed (self->task_ssm, error);
259 else
260 46 fpi_ssm_next_state (self->task_ssm);
261 46 }
262
263 static void
264 86 egis_etu905_cmd_receive_cb (FpiUsbTransfer *transfer,
265 FpDevice *device,
266 gpointer userdata,
267 GError *error)
268 {
269 86 CommandData *data = userdata;
270
271 86 fp_dbg ("Command receive callback");
272
273
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 86 times.
86 if (error)
274 {
275 fpi_ssm_mark_failed (transfer->ssm, error);
276 return;
277 }
278
279
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 10 taken 86 times.
86 if (transfer->actual_length < G_N_ELEMENTS (egis_etu905_read_prefix))
280 {
281 fpi_ssm_mark_failed (transfer->ssm,
282 fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
283 return;
284 }
285
286 /* Store the response data and let the cmd_ssm_done callback invoke
287 * the actual callback with the stored data */
288
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 86 times.
86 g_assert (data != NULL);
289 86 data->buffer_in = g_steal_pointer (&transfer->buffer);
290 86 data->length_in = transfer->actual_length;
291
292 86 fpi_ssm_mark_completed (transfer->ssm);
293 }
294
295 static void
296 173 egis_etu905_cmd_run_state (FpiSsm *ssm,
297 FpDevice *device)
298 {
299 346 g_autoptr(FpiUsbTransfer) transfer = NULL;
300 173 CommandData *data = fpi_ssm_get_data (ssm);
301
302
2/3
✓ Branch 4 → 5 taken 87 times.
✓ Branch 4 → 10 taken 86 times.
✗ Branch 4 → 15 not taken.
173 switch (fpi_ssm_get_cur_state (ssm))
303 {
304 87 case CMD_SEND:
305
1/2
✓ Branch 5 → 6 taken 87 times.
✗ Branch 5 → 8 not taken.
87 if (data->cmd_transfer)
306 {
307 87 transfer = g_steal_pointer (&data->cmd_transfer);
308 87 transfer->ssm = ssm;
309 87 fpi_usb_transfer_submit (g_steal_pointer (&transfer),
310 EGIS_ETU905_USB_SEND_TIMEOUT,
311 data->cancellable,
312 fpi_ssm_usb_transfer_cb,
313 NULL);
314 87 break;
315 }
316
317 fpi_ssm_next_state (ssm);
318 break;
319
320 86 case CMD_GET:
321 86 transfer = fpi_usb_transfer_new (device);
322 86 transfer->ssm = ssm;
323 86 fpi_usb_transfer_fill_bulk (transfer, EGIS_ETU905_EP_CMD_IN,
324 EGIS_ETU905_USB_IN_RECV_LENGTH);
325 86 fpi_usb_transfer_submit (g_steal_pointer (&transfer),
326 EGIS_ETU905_USB_RECV_TIMEOUT,
327 data->cancellable,
328 egis_etu905_cmd_receive_cb,
329 fpi_ssm_get_data (ssm));
330 86 break;
331 }
332 173 }
333
334 /*
335 * Command SSM done callback.
336 * Always invokes the callback with stored data on success or error.
337 */
338 static void
339 87 egis_etu905_cmd_ssm_done (FpiSsm *ssm,
340 FpDevice *device,
341 GError *error)
342 {
343 174 g_autoptr(GError) local_error = error;
344 87 CommandData *data = fpi_ssm_get_data (ssm);
345
346
2/4
✓ Branch 3 → 4 taken 87 times.
✗ Branch 3 → 6 not taken.
✓ Branch 4 → 5 taken 87 times.
✗ Branch 4 → 6 not taken.
87 if (data && data->callback)
347 {
348 87 data->callback (device,
349 data->buffer_in,
350 data->length_in,
351 87 g_steal_pointer (&local_error));
352 }
353 87 }
354
355 /*
356 * Derive the 2 "check bytes" for write payloads
357 * 32-bit big-endian sum of all 16-bit words (including check bytes) MOD 0xFFFF
358 * should be 0, otherwise the device will reject the payload
359 */
360 static guint16
361 87 egis_etu905_get_check_bytes (FpiByteReader *reader)
362 {
363 87 fp_dbg ("Get check bytes");
364 87 guint64 sum_values = 0;
365 87 guint16 val;
366
367 87 fpi_byte_reader_set_pos (reader, 0);
368
369
2/2
✓ Branch 6 → 7 taken 87 times.
✓ Branch 6 → 8 taken 1090 times.
1177 while (fpi_byte_reader_get_uint16_be (reader, &val))
370 1090 sum_values += val;
371
372 87 return G_MAXUINT16 - (sum_values % G_MAXUINT16);
373 }
374
375 static void
376 87 egis_etu905_exec_cmd_full (FpDevice *device,
377 const guchar *cmd,
378 const gsize cmd_length,
379 SynCmdMsgCallback callback,
380 GCancellable *cancellable)
381 {
382 174 g_auto(FpiByteWriter) writer = {0};
383 87 g_autoptr(FpiUsbTransfer) transfer = NULL;
384 87 g_autofree CommandData *data = NULL;
385 87 FpiSsm *ssm = NULL;
386 87 gsize buffer_out_length = 0;
387 87 gboolean written = TRUE;
388 87 guint16 check_value;
389
390 87 fp_dbg ("Execute command and get response");
391
392 /*
393 * buffer_out should be a fully composed command (with prefix, check bytes, etc)
394 * which looks like this:
395 * E G I S 00 00 00 01 {cb1} {cb2} {payload}
396 * where cb1 and cb2 are some check bytes generated by the
397 * egis_etu905_get_check_bytes() method and payload is what is passed via the cmd
398 * parameter
399 */
400 87 buffer_out_length = G_N_ELEMENTS (egis_etu905_write_prefix)
401 + EGIS_ETU905_CHECK_BYTES_LENGTH
402 + cmd_length;
403
404 87 fpi_byte_writer_init_with_size (&writer, buffer_out_length +
405 (buffer_out_length % 2 ? 1 : 0), TRUE);
406
407 /* Prefix */
408
1/2
✓ Branch 5 → 6 taken 87 times.
✗ Branch 5 → 7 not taken.
87 written &= fpi_byte_writer_put_data (&writer, egis_etu905_write_prefix,
409 G_N_ELEMENTS (egis_etu905_write_prefix));
410
411 /* Check Bytes - leave them as 00 for now then later generate and copy over
412 * the real ones */
413 87 written &= fpi_byte_writer_change_pos (&writer, EGIS_ETU905_CHECK_BYTES_LENGTH);
414
415 /* Command Payload */
416
1/2
✓ Branch 9 → 10 taken 87 times.
✗ Branch 9 → 11 not taken.
87 written &= fpi_byte_writer_put_data (&writer, cmd, cmd_length);
417
418 /* Now fetch and set the "real" check bytes based on the currently
419 * assembled payload */
420 87 check_value = egis_etu905_get_check_bytes (FPI_BYTE_READER (&writer));
421 87 fpi_byte_writer_set_pos (&writer, G_N_ELEMENTS (egis_etu905_write_prefix));
422
1/2
✓ Branch 14 → 15 taken 87 times.
✗ Branch 14 → 16 not taken.
87 written &= fpi_byte_writer_put_uint16_be (&writer, check_value);
423
424 /* The command runs on its own dedicated SSM, carrying all its state (the
425 * outgoing transfer and the response buffer) via the SSM data. Nothing is
426 * shared on the device instance, so commands issued from different contexts
427 * (e.g. an in-progress operation and a cancellation) cannot collide. */
428 87 data = g_new0 (CommandData, 1);
429 87 data->callback = callback;
430 87 g_set_object (&data->cancellable, cancellable);
431
432
1/2
✓ Branch 18 → 19 taken 87 times.
✗ Branch 18 → 24 not taken.
87 if (written)
433 {
434 87 transfer = fpi_usb_transfer_new (device);
435 87 fpi_usb_transfer_set_short_error (transfer, TRUE);
436
437 87 fpi_usb_transfer_fill_bulk_full (transfer,
438 EGIS_ETU905_EP_CMD_OUT,
439 fpi_byte_writer_reset_and_get_data (&writer),
440 buffer_out_length,
441 g_free);
442
443 87 data->cmd_transfer = g_steal_pointer (&transfer);
444 }
445
446 87 ssm = fpi_ssm_new (device, egis_etu905_cmd_run_state, CMD_STATES);
447 87 fpi_ssm_set_data (ssm, g_steal_pointer (&data),
448 (GDestroyNotify) egis_etu905_command_data_free);
449
450 87 fpi_ssm_start (ssm, egis_etu905_cmd_ssm_done);
451
452
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 30 taken 87 times.
87 if (!written)
453 fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
454 87 }
455
456 static inline void
457 81 egis_etu905_exec_cmd (FpDevice *device,
458 const guchar *cmd,
459 const gsize cmd_length,
460 SynCmdMsgCallback callback)
461 {
462 81 egis_etu905_exec_cmd_full (device, cmd, cmd_length, callback,
463 fpi_device_get_cancellable (device));
464 81 }
465
466 static void
467 8 egis_etu905_set_print_data (FpPrint *print,
468 const gchar *device_print_id,
469 const gchar *user_id)
470 {
471 8 GVariant *print_id_var = NULL;
472 8 GVariant *fpi_data = NULL;
473 16 g_autofree gchar *fill_user_id = NULL;
474
475
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 7 taken 7 times.
8 if (user_id)
476
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1 time.
1 fill_user_id = g_strdup (user_id);
477 else
478 7 fill_user_id = g_strndup (device_print_id, EGIS_ETU905_FINGERPRINT_DATA_SIZE);
479
480 8 fpi_print_fill_from_user_id (print, fill_user_id);
481
482 8 fpi_print_set_type (print, FPI_PRINT_RAW);
483 8 fpi_print_set_device_stored (print, TRUE);
484
485 8 g_object_set (print, "description", fill_user_id, NULL);
486
487 8 print_id_var = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
488 device_print_id,
489 EGIS_ETU905_FINGERPRINT_DATA_SIZE,
490 sizeof (guchar));
491 8 fpi_data = g_variant_new ("(@ay)", print_id_var);
492 8 g_object_set (print, "fpi-data", fpi_data, NULL);
493 8 }
494
495 static GPtrArray *
496 3 egis_etu905_get_enrolled_prints (FpDevice *device)
497 {
498 3 g_autoptr(GPtrArray) result = g_ptr_array_new_with_free_func (g_object_unref);
499 3 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
500
501
1/2
✓ Branch 3 → 9 taken 3 times.
✗ Branch 3 → 10 not taken.
3 if (!self->enrolled_ids)
502 return g_steal_pointer (&result);
503
504
2/2
✓ Branch 9 → 4 taken 7 times.
✓ Branch 9 → 10 taken 3 times.
10 for (guint i = 0; i < self->enrolled_ids->len; i++)
505 {
506 7 FpPrint *print = fp_print_new (device);
507 7 egis_etu905_set_print_data (print, g_ptr_array_index (self->enrolled_ids, i), NULL);
508 7 g_ptr_array_add (result, g_object_ref_sink (print));
509 }
510
511 return g_steal_pointer (&result);
512 }
513
514 static void
515 8 egis_etu905_list_fill_enrolled_ids_cb (FpDevice *device,
516 guchar *buffer_in,
517 gsize length_in,
518 GError *error)
519 {
520 8 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
521
522 8 fp_dbg ("List callback");
523
524
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 7 times.
8 if (error)
525 {
526 1 fpi_ssm_mark_failed (self->task_ssm, error);
527 1 return;
528 }
529
530
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 7 times.
7 g_clear_pointer (&self->enrolled_ids, g_ptr_array_unref);
531 7 self->enrolled_ids = g_ptr_array_new_with_free_func (g_free);
532
533 7 FpiByteReader reader;
534 7 gboolean read = TRUE;
535
536 7 fpi_byte_reader_init (&reader, buffer_in, length_in);
537
538 7 read &= fpi_byte_reader_set_pos (&reader, EGIS_ETU905_LIST_RESPONSE_PREFIX_SIZE);
539
540 /*
541 * Each fingerprint ID will be returned in this response as a 32 byte array
542 * The other stuff in the payload is 16 bytes long, so if there is at least 1
543 * print then the length should be at least 16+32=48 bytes long
544 */
545
1/2
✓ Branch 19 → 11 taken 26 times.
✗ Branch 19 → 20 not taken.
26 while (read)
546 {
547 26 const guint8 data[EGIS_ETU905_FINGERPRINT_DATA_SIZE];
548 26 g_autofree gchar *print_id = NULL;
549
550 26 read &= fpi_byte_reader_get_data_static (&reader, data);
551
2/2
✓ Branch 12 → 13 taken 19 times.
✓ Branch 12 → 17 taken 7 times.
26 if (!read)
552 break;
553
554 19 print_id = g_memdup2 (data, EGIS_ETU905_FINGERPRINT_DATA_SIZE);
555 19 g_ptr_array_add (self->enrolled_ids, g_steal_pointer (&print_id));
556 }
557
558 7 fp_info ("Number of currently enrolled fingerprints on the device is %d",
559 self->enrolled_ids->len);
560
561
1/2
✓ Branch 21 → 22 taken 7 times.
✗ Branch 21 → 23 not taken.
7 if (self->task_ssm)
562 7 fpi_ssm_next_state (self->task_ssm);
563 }
564
565 static void
566 6 egis_etu905_list_run_state (FpiSsm *ssm,
567 FpDevice *device)
568 {
569 12 g_autoptr(GPtrArray) enrolled_prints = NULL;
570
571
2/3
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 6 taken 3 times.
✗ Branch 3 → 10 not taken.
6 switch (fpi_ssm_get_cur_state (ssm))
572 {
573 3 case LIST_GET_ENROLLED_IDS:
574 3 egis_etu905_exec_cmd (device, cmd_list, G_N_ELEMENTS (cmd_list),
575 egis_etu905_list_fill_enrolled_ids_cb);
576 3 break;
577
578 3 case LIST_RETURN_ENROLLED_PRINTS:
579 3 enrolled_prints = egis_etu905_get_enrolled_prints (device);
580 3 fpi_device_list_complete (device, g_steal_pointer (&enrolled_prints), NULL);
581 3 fpi_ssm_next_state (ssm);
582 3 break;
583 }
584 6 }
585
586 static void
587 3 egis_etu905_list (FpDevice *device)
588 {
589 3 fp_dbg ("List");
590 3 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
591
592
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3 times.
3 g_assert (self->task_ssm == NULL);
593 3 self->task_ssm = fpi_ssm_new (device,
594 egis_etu905_list_run_state,
595 LIST_STATES);
596 3 fpi_ssm_start (self->task_ssm, egis_etu905_task_ssm_done);
597 3 }
598
599 static guchar *
600 1 egis_etu905_get_delete_cmd (FpDevice *device,
601 FpPrint *delete_print,
602 gsize *length_out)
603 {
604 1 fp_dbg ("Get delete command");
605 1 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
606 2 g_auto(FpiByteWriter) writer = {0};
607 1 g_autoptr(GVariant) print_data = NULL;
608
1/2
✓ Branch 78 → 79 taken 1 time.
✗ Branch 78 → 80 not taken.
1 g_autoptr(GVariant) print_data_id_var = NULL;
609 1 const guchar *print_data_id = NULL;
610 1 gsize print_data_id_len = 0;
611
1/2
✓ Branch 76 → 77 taken 1 time.
✗ Branch 76 → 78 not taken.
1 g_autofree gchar *print_description = NULL;
612 1 gboolean written = TRUE;
613
614 /*
615 * The final command body should contain:
616 * 1) hard-coded 00 00
617 * 2) 2-byte size indiciator, 20*Number deleted identifiers plus 7 in form of:
618 * num_to_delete * 0x20 + 0x07
619 * Since max prints can be higher than 7 then this goes up to 2 bytes
620 * (e9 + 9 = 109)
621 * 3) Hard-coded prefix (cmd_delete_prefix)
622 * 4) 2-byte size indiciator, 20*Number of enrolled identifiers without plus 7
623 * (num_to_delete * 0x20)
624 * 5) All of the currently registered prints to delete in their 32-byte device
625 * identifiers (enrolled_list)
626 */
627
628 1 int num_to_delete = 0;
629
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1 time.
1 if (delete_print)
630 num_to_delete = 1;
631 else if (self->enrolled_ids)
632 num_to_delete = self->enrolled_ids->len;
633
634 1 const gsize body_length = sizeof (guchar) * EGIS_ETU905_FINGERPRINT_DATA_SIZE *
635 num_to_delete;
636 /* total_length is the 6 various bytes plus prefix and body payload */
637 1 const gsize total_length = (sizeof (guchar) * 6) + G_N_ELEMENTS (cmd_delete_prefix) +
638 body_length;
639
640 /* pre-fill entire payload with 00s */
641 1 fpi_byte_writer_init_with_size (&writer, total_length, TRUE);
642
643 /* start with 00 00 (just move starting offset up by 2) */
644 1 written &= fpi_byte_writer_set_pos (&writer, 2);
645
646 /* Size Counter bytes */
647 /* "easiest" way to handle 2-bytes size for counter is to hard-code logic for
648 * when we go to the 2nd byte
649 * note this will not work in case any model ever supports more than 14 prints
650 * (assumed max is 10) */
651
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 16 taken 1 time.
1 if (num_to_delete > 7)
652 {
653 written &= fpi_byte_writer_put_uint8 (&writer, 0x01);
654 written &= fpi_byte_writer_put_uint8 (&writer, ((num_to_delete - 8) * 0x20) + 0x07);
655 }
656 else
657 {
658 /* first byte is 0x00, just skip it */
659 1 written &= fpi_byte_writer_change_pos (&writer, 1);
660
1/2
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 20 not taken.
1 written &= fpi_byte_writer_put_uint8 (&writer, (num_to_delete * 0x20) + 0x07);
661 }
662
663 /* command prefix */
664
1/2
✓ Branch 22 → 23 taken 1 time.
✗ Branch 22 → 24 not taken.
1 written &= fpi_byte_writer_put_data (&writer, cmd_delete_prefix,
665 G_N_ELEMENTS (cmd_delete_prefix));
666
667 /* 2-bytes size logic for counter again */
668
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 32 taken 1 time.
1 if (num_to_delete > 7)
669 {
670 written &= fpi_byte_writer_put_uint8 (&writer, 0x01);
671 written &= fpi_byte_writer_put_uint8 (&writer, (num_to_delete - 8) * 0x20);
672 }
673 else
674 {
675 /* first byte is 0x00, just skip it */
676 1 written &= fpi_byte_writer_change_pos (&writer, 1);
677
1/2
✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 36 not taken.
1 written &= fpi_byte_writer_put_uint8 (&writer, num_to_delete * 0x20);
678 }
679
680 /* append desired 32-byte fingerprint IDs */
681 /* if passed a delete_print then fetch its data from the FpPrint */
682
1/2
✓ Branch 37 → 38 taken 1 time.
✗ Branch 37 → 63 not taken.
1 if (delete_print)
683 {
684 1 g_object_get (delete_print, "description", &print_description, NULL);
685 1 g_object_get (delete_print, "fpi-data", &print_data, NULL);
686
687
2/4
✓ Branch 40 → 41 taken 1 time.
✗ Branch 40 → 43 not taken.
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 46 taken 1 time.
2 if (!print_data ||
688 1 !g_variant_check_format_string (print_data, "(@ay)", FALSE))
689 {
690 fpi_ssm_mark_failed (self->task_ssm,
691 fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID));
692 return NULL;
693 }
694
695 1 g_variant_get (print_data, "(@ay)", &print_data_id_var);
696 1 print_data_id = g_variant_get_fixed_array (print_data_id_var,
697 &print_data_id_len, sizeof (guchar));
698
699
1/2
✗ Branch 48 → 49 not taken.
✓ Branch 48 → 52 taken 1 time.
1 if (print_data_id_len != EGIS_ETU905_FINGERPRINT_DATA_SIZE)
700 {
701 fpi_ssm_mark_failed (self->task_ssm,
702 fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID,
703 "Stored print id has "
704 "unexpected length %zu",
705 print_data_id_len));
706 return NULL;
707 }
708
709
3/6
✓ Branch 52 → 53 taken 1 time.
✗ Branch 52 → 55 not taken.
✓ Branch 53 → 54 taken 1 time.
✗ Branch 53 → 55 not taken.
✗ Branch 54 → 55 not taken.
✓ Branch 54 → 58 taken 1 time.
1 if (!print_description || !g_str_has_prefix (print_description, "FP"))
710 fp_dbg ("Fingerprint '%s' was not created by libfprint; deleting anyway.",
711 print_description ? print_description : "(null)");
712
713
1/2
✓ Branch 58 → 59 taken 1 time.
✗ Branch 58 → 60 not taken.
2 fp_info ("Delete fingerprint %s", print_description ? print_description : "(null)");
714
715
1/2
✓ Branch 62 → 70 taken 1 time.
✗ Branch 62 → 72 not taken.
1 written &= fpi_byte_writer_put_data (&writer, print_data_id,
716 EGIS_ETU905_FINGERPRINT_DATA_SIZE);
717 }
718 /* Otherwise assume this is a "clear" - just loop through and append all enrolled IDs */
719 else if (self->enrolled_ids)
720 {
721 for (guint i = 0; i < self->enrolled_ids->len && written; i++)
722 {
723 written &= fpi_byte_writer_put_data (&writer,
724 g_ptr_array_index (self->enrolled_ids, i),
725 EGIS_ETU905_FINGERPRINT_DATA_SIZE);
726 }
727 }
728
729
1/2
✓ Branch 70 → 71 taken 1 time.
✗ Branch 70 → 72 not taken.
1 g_assert (written);
730
731
1/2
✓ Branch 71 → 73 taken 1 time.
✗ Branch 71 → 74 not taken.
1 if (length_out)
732 1 *length_out = total_length;
733
734 1 return fpi_byte_writer_reset_and_get_data (&writer);
735 }
736
737 static void
738 1 egis_etu905_delete_cb (FpDevice *device,
739 guchar *buffer_in,
740 gsize length_in,
741 GError *error)
742 {
743 1 fp_dbg ("Delete callback");
744 1 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
745
746
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1 time.
1 if (error)
747 {
748 fpi_ssm_mark_failed (self->task_ssm, error);
749 return;
750 }
751
752 /* Check that the read payload indicates "success" with the delete */
753
1/2
✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 18 not taken.
1 if (egis_etu905_validate_response_prefix (buffer_in,
754 length_in,
755 rsp_delete_success_prefix,
756 G_N_ELEMENTS (rsp_delete_success_prefix)))
757 {
758
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 12 taken 1 time.
1 if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_CLEAR_STORAGE)
759 {
760 fpi_device_clear_storage_complete (device, NULL);
761 fpi_ssm_next_state (self->task_ssm);
762 }
763
1/2
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 16 not taken.
1 else if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_DELETE)
764 {
765 1 fpi_device_delete_complete (device, NULL);
766 1 fpi_ssm_next_state (self->task_ssm);
767 }
768 else
769 {
770 fpi_ssm_mark_failed (self->task_ssm,
771 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
772 "Unsupported delete action."));
773 }
774 }
775 else
776 {
777 fpi_ssm_mark_failed (self->task_ssm,
778 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
779 "Delete print was not successful"));
780 }
781 }
782
783 static void
784 2 egis_etu905_delete_run_state (FpiSsm *ssm,
785 FpDevice *device)
786 {
787 2 g_autofree guchar *payload = NULL;
788 2 gsize payload_length = 0;
789
790
2/3
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 1 time.
✗ Branch 3 → 16 not taken.
2 switch (fpi_ssm_get_cur_state (ssm))
791 {
792 1 case DELETE_GET_ENROLLED_IDS:
793 /* get enrolled_ids from device for use building delete payload below */
794 1 egis_etu905_exec_cmd (device, cmd_list, G_N_ELEMENTS (cmd_list),
795 egis_etu905_list_fill_enrolled_ids_cb);
796 1 break;
797
798 1 case DELETE_DELETE:
799
1/2
✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 10 not taken.
1 if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_DELETE)
800 1 payload = egis_etu905_get_delete_cmd (device, fpi_ssm_get_data (ssm),
801 &payload_length);
802 else
803 payload = egis_etu905_get_delete_cmd (device, NULL, &payload_length);
804
805 /* get_delete_cmd already marked task_ssm as failed */
806
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 14 taken 1 time.
1 if (!payload)
807 return;
808
809 1 egis_etu905_exec_cmd (device, payload, payload_length,
810 egis_etu905_delete_cb);
811 1 break;
812 }
813 }
814
815 static void
816 egis_etu905_clear_storage (FpDevice *device)
817 {
818 fp_dbg ("Clear storage");
819 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
820
821 g_assert (self->task_ssm == NULL);
822 self->task_ssm = fpi_ssm_new (device,
823 egis_etu905_delete_run_state,
824 DELETE_STATES);
825 fpi_ssm_start (self->task_ssm, egis_etu905_task_ssm_done);
826 }
827
828 static void
829 1 egis_etu905_delete (FpDevice *device)
830 {
831 1 fp_dbg ("Delete");
832 1 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
833 1 FpPrint *delete_print = NULL;
834
835 1 fpi_device_get_delete_data (device, &delete_print);
836
837
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
1 g_assert (self->task_ssm == NULL);
838 1 self->task_ssm = fpi_ssm_new (device,
839 egis_etu905_delete_run_state,
840 DELETE_STATES);
841 /* the print is owned by libfprint during deletion task */
842 1 fpi_ssm_set_data (self->task_ssm, delete_print, NULL);
843 1 fpi_ssm_start (self->task_ssm, egis_etu905_task_ssm_done);
844 1 }
845
846 static void
847 18 egis_etu905_enroll_status_report (FpDevice *device,
848 EnrollPrint *enroll_print,
849 EnrollStatus status,
850 GError *error)
851 {
852 18 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
853
854
3/5
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 5 times.
✓ Branch 2 → 7 taken 12 times.
✓ Branch 2 → 10 taken 1 time.
✗ Branch 2 → 14 not taken.
18 switch (status)
855 {
856 case ENROLL_STATUS_DEVICE_FULL:
857 case ENROLL_STATUS_DUPLICATE:
858 fpi_ssm_mark_failed (self->task_ssm, error);
859 break;
860
861 5 case ENROLL_STATUS_RETRY:
862 5 fpi_device_enroll_progress (device, enroll_print->stage, NULL, error);
863 5 break;
864
865 12 case ENROLL_STATUS_PARTIAL_OK:
866 12 enroll_print->stage++;
867 12 fp_info ("Partial capture successful. Please touch the sensor again (%d/%d)",
868 enroll_print->stage,
869 self->max_enroll_stages);
870 12 fpi_device_enroll_progress (device, enroll_print->stage, enroll_print->print, NULL);
871 12 break;
872
873 1 case ENROLL_STATUS_COMPLETE:
874 1 fp_info ("Enrollment was successful!");
875 1 fpi_device_enroll_complete (device, g_object_ref (enroll_print->print), NULL);
876 1 break;
877
878 default:
879 if (error)
880 fpi_ssm_mark_failed (self->task_ssm, error);
881 else
882 fpi_ssm_mark_failed (self->task_ssm,
883 fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
884 "Unknown error"));
885 }
886 18 }
887
888 static void
889 17 egis_etu905_read_capture_cb (FpDevice *device,
890 guchar *buffer_in,
891 gsize length_in,
892 GError *error)
893 {
894 17 fp_dbg ("Read capture callback");
895 17 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
896 17 EnrollPrint *enroll_print = fpi_ssm_get_data (self->task_ssm);
897
898
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 17 times.
17 if (error)
899 {
900 fpi_ssm_mark_failed (self->task_ssm, error);
901 return;
902 }
903
904 /* Check that the read payload indicates "success" */
905
1/2
✓ Branch 8 → 9 taken 17 times.
✗ Branch 8 → 12 not taken.
17 if (egis_etu905_validate_response_prefix (buffer_in,
906 length_in,
907 rsp_read_success_prefix,
908
2/2
✓ Branch 10 → 11 taken 12 times.
✓ Branch 10 → 12 taken 5 times.
17 G_N_ELEMENTS (rsp_read_success_prefix)) &&
909 17 egis_etu905_validate_response_suffix (buffer_in,
910 length_in,
911 rsp_read_success_suffix,
912 G_N_ELEMENTS (rsp_read_success_suffix)))
913 {
914 12 egis_etu905_enroll_status_report (device, enroll_print,
915 ENROLL_STATUS_PARTIAL_OK, NULL);
916 }
917 else
918 {
919 /* If not success then the sensor can either report "off center" or "sensor is dirty" */
920
921 /* "Off center" */
922
1/2
✓ Branch 13 → 14 taken 5 times.
✗ Branch 13 → 17 not taken.
5 if (egis_etu905_validate_response_prefix (buffer_in,
923 length_in,
924 rsp_read_offcenter_prefix,
925
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 5 times.
5 G_N_ELEMENTS (rsp_read_offcenter_prefix)) &&
926 5 egis_etu905_validate_response_suffix (buffer_in,
927 length_in,
928 rsp_read_offcenter_suffix,
929 G_N_ELEMENTS (rsp_read_offcenter_suffix)))
930 error = fpi_device_retry_new (FP_DEVICE_RETRY_CENTER_FINGER);
931
932 /* "Sensor is dirty" */
933
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 5 times.
5 else if (egis_etu905_validate_response_prefix (buffer_in,
934 length_in,
935 rsp_read_dirty_prefix,
936 G_N_ELEMENTS (rsp_read_dirty_prefix)))
937 error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER,
938 "Your device is having trouble recognizing you. "
939 "Make sure your sensor is clean.");
940
941 else
942 5 error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER,
943 "Unknown failure trying to read your finger. "
944 "Please try again.");
945
946 5 egis_etu905_enroll_status_report (device, enroll_print, ENROLL_STATUS_RETRY, error);
947 }
948
949
2/2
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 24 taken 16 times.
17 if (enroll_print->stage == self->max_enroll_stages)
950 1 fpi_ssm_next_state (self->task_ssm);
951 else
952 16 fpi_ssm_jump_to_state (self->task_ssm, ENROLL_CAPTURE_SENSOR_RESET);
953 }
954
955 static void
956 1 egis_etu905_enroll_duplicate_check_cb (FpDevice *device,
957 guchar *buffer_in,
958 gsize length_in,
959 GError *error)
960 {
961 1 fp_dbg ("Task SSM enroll duplicate check callback");
962 1 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
963
964
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1 time.
1 if (error)
965 {
966 fpi_ssm_mark_failed (self->task_ssm, error);
967 return;
968 }
969
970 /* Check that the read payload reports "not yet enrolled" */
971
1/2
✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 9 not taken.
1 if (egis_etu905_validate_response_suffix (buffer_in,
972 length_in,
973 rsp_check_not_yet_enrolled_suffix,
974 G_N_ELEMENTS (rsp_check_not_yet_enrolled_suffix)))
975 1 fpi_ssm_jump_to_state (self->task_ssm, ENROLL_COMMIT_START);
976 else
977 egis_etu905_enroll_status_report (device, NULL, ENROLL_STATUS_DUPLICATE,
978 fpi_device_error_new (FP_DEVICE_ERROR_DATA_DUPLICATE));
979 }
980
981 static void
982 1 egis_etu905_enroll_begin_cb (FpDevice *device,
983 guchar *buffer_in,
984 gsize length_in,
985 GError *error)
986 {
987 1 fp_dbg ("Enroll begin callback");
988 1 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
989
990
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1 time.
1 if (error)
991 {
992 fpi_ssm_mark_failed (self->task_ssm, error);
993 return;
994 }
995
996 1 fpi_ssm_next_state (self->task_ssm);
997 }
998
999 /*
1000 * Builds the full "check" payload which includes identifiers for all
1001 * fingerprints which currently should exist on the storage. This payload is
1002 * used during both enrollment and identify actions.
1003 */
1004 static guchar *
1005 2 egis_etu905_get_check_cmd (FpDevice *device,
1006 gsize *length_out)
1007 {
1008 2 fp_dbg ("Get check command");
1009 2 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1010 4 g_auto(FpiByteWriter) writer = {0};
1011 2 gboolean written = TRUE;
1012
1013 /*
1014 * The final command body should contain:
1015 * 1) hard-coded 00 00
1016 * 2) 2-byte size indiciator, 20*Number enrolled identifiers plus 9 in form of:
1017 * (enrolled_ids->len + 1) * 0x20 + 0x09
1018 * Since max prints can be higher than 7 then this goes up to 2 bytes
1019 * (e9 + 9 = 109)
1020 * 3) Hard-coded prefix (cmd_check_prefix)
1021 * 4) 2-byte size indiciator, 20*Number of enrolled identifiers without plus 9
1022 * ((enrolled_ids->len + 1) * 0x20)
1023 * 5) Hard-coded 32 * 0x00 bytes
1024 * 6) All of the currently registered prints in their 32-byte device identifiers
1025 * (enrolled_list)
1026 * 7) Hard-coded suffix (cmd_check_suffix)
1027 */
1028
1029
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 g_assert (self->enrolled_ids);
1030 2 const gsize body_length = sizeof (guchar) * self->enrolled_ids->len *
1031 EGIS_ETU905_FINGERPRINT_DATA_SIZE;
1032
1033 /* prefix length can depend on the type */
1034 2 const gsize check_prefix_length = (fpi_device_get_driver_data (device) &
1035 EGIS_ETU905_DRIVER_CHECK_PREFIX_TYPE2) ?
1036 G_N_ELEMENTS (cmd_check_prefix_type2) :
1037 G_N_ELEMENTS (cmd_check_prefix_type1);
1038
1039 /* total_length is the 6 various bytes plus all other prefixes/suffixes and
1040 * the body payload */
1041 2 const gsize total_length = (sizeof (guchar) * 6)
1042 + check_prefix_length
1043 + EGIS_ETU905_CMD_CHECK_SEPARATOR_LENGTH
1044 + body_length
1045 + G_N_ELEMENTS (cmd_check_suffix);
1046
1047 /* pre-fill entire payload with 00s */
1048 2 fpi_byte_writer_init_with_size (&writer, total_length, TRUE);
1049
1050 /* start with 00 00 (just move starting offset up by 2) */
1051 2 written &= fpi_byte_writer_set_pos (&writer, 2);
1052
1053 /* Size Counter bytes */
1054 /* "easiest" way to handle 2-bytes size for counter is to hard-code logic for
1055 * when we go to the 2nd byte
1056 * note this will not work in case any model ever supports more than 14 prints
1057 * (assumed max is 10) */
1058
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 16 taken 2 times.
2 if (self->enrolled_ids->len > 6)
1059 {
1060 written &= fpi_byte_writer_put_uint8 (&writer, 0x01);
1061 written &= fpi_byte_writer_put_uint8 (&writer,
1062 ((self->enrolled_ids->len - 7) * 0x20)
1063 + 0x09);
1064 }
1065 else
1066 {
1067 /* first byte is 0x00, just skip it */
1068 2 written &= fpi_byte_writer_change_pos (&writer, 1);
1069
1/2
✓ Branch 18 → 19 taken 2 times.
✗ Branch 18 → 20 not taken.
2 written &= fpi_byte_writer_put_uint8 (&writer,
1070 ((self->enrolled_ids->len + 1) * 0x20) +
1071 0x09);
1072 }
1073
1074 /* command prefix */
1075
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 27 taken 2 times.
2 if (fpi_device_get_driver_data (device) & EGIS_ETU905_DRIVER_CHECK_PREFIX_TYPE2)
1076 written &= fpi_byte_writer_put_data (&writer, cmd_check_prefix_type2,
1077 G_N_ELEMENTS (cmd_check_prefix_type2));
1078 else
1079
1/2
✓ Branch 28 → 29 taken 2 times.
✗ Branch 28 → 30 not taken.
2 written &= fpi_byte_writer_put_data (&writer, cmd_check_prefix_type1,
1080 G_N_ELEMENTS (cmd_check_prefix_type1));
1081
1082 /* 2-bytes size logic for counter again */
1083
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 39 taken 2 times.
2 if (self->enrolled_ids->len > 6)
1084 {
1085 written &= fpi_byte_writer_put_uint8 (&writer, 0x01);
1086 written &= fpi_byte_writer_put_uint8 (&writer,
1087 (self->enrolled_ids->len - 7) * 0x20);
1088 }
1089 else
1090 {
1091 /* first byte is 0x00, just skip it */
1092 2 written &= fpi_byte_writer_change_pos (&writer, 1);
1093
1/2
✓ Branch 41 → 42 taken 2 times.
✗ Branch 41 → 43 not taken.
2 written &= fpi_byte_writer_put_uint8 (&writer,
1094 (self->enrolled_ids->len + 1) * 0x20);
1095 }
1096
1097 /* add 00s "separator" to offset position */
1098 2 written &= fpi_byte_writer_change_pos (&writer,
1099 EGIS_ETU905_CMD_CHECK_SEPARATOR_LENGTH);
1100
1101
3/4
✓ Branch 50 → 51 taken 6 times.
✓ Branch 50 → 52 taken 2 times.
✓ Branch 51 → 46 taken 6 times.
✗ Branch 51 → 52 not taken.
8 for (guint i = 0; i < self->enrolled_ids->len && written; i++)
1102 {
1103
1/2
✓ Branch 47 → 48 taken 6 times.
✗ Branch 47 → 49 not taken.
6 written &= fpi_byte_writer_put_data (&writer,
1104 g_ptr_array_index (self->enrolled_ids, i),
1105 EGIS_ETU905_FINGERPRINT_DATA_SIZE);
1106 }
1107
1108 /* command suffix */
1109
1/2
✓ Branch 53 → 54 taken 2 times.
✗ Branch 53 → 56 not taken.
2 written &= fpi_byte_writer_put_data (&writer, cmd_check_suffix,
1110 G_N_ELEMENTS (cmd_check_suffix));
1111
1/2
✓ Branch 54 → 55 taken 2 times.
✗ Branch 54 → 56 not taken.
2 g_assert (written);
1112
1113
1/2
✓ Branch 55 → 57 taken 2 times.
✗ Branch 55 → 58 not taken.
2 if (length_out)
1114 2 *length_out = total_length;
1115
1116 2 return fpi_byte_writer_reset_and_get_data (&writer);
1117 }
1118
1119 static void
1120 74 egis_etu905_enroll_run_state (FpiSsm *ssm,
1121 FpDevice *device)
1122 {
1123 148 g_auto(FpiByteWriter) writer = {0};
1124 74 EnrollPrint *enroll_print = fpi_ssm_get_data (ssm);
1125 74 g_autofree guchar *payload = NULL;
1126 74 gsize payload_length = 0;
1127 74 g_autofree gchar *device_print_id = NULL;
1128 74 g_autofree gchar *user_id = NULL;
1129
1130
10/11
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 7 taken 17 times.
✓ Branch 4 → 9 taken 17 times.
✓ Branch 4 → 11 taken 17 times.
✓ Branch 4 → 13 taken 17 times.
✓ Branch 4 → 15 taken 1 time.
✓ Branch 4 → 17 taken 1 time.
✓ Branch 4 → 19 taken 1 time.
✓ Branch 4 → 41 taken 1 time.
✓ Branch 4 → 43 taken 1 time.
✗ Branch 4 → 46 not taken.
74 switch (fpi_ssm_get_cur_state (ssm))
1131 {
1132 1 case ENROLL_START:
1133 1 egis_etu905_exec_cmd (device, cmd_enroll_starting, G_N_ELEMENTS (cmd_enroll_starting),
1134 egis_etu905_enroll_begin_cb);
1135 1 break;
1136
1137 17 case ENROLL_CAPTURE_SENSOR_RESET:
1138 17 egis_etu905_exec_cmd (device, cmd_sensor_reset, G_N_ELEMENTS (cmd_sensor_reset),
1139 egis_etu905_task_ssm_next_state_cb);
1140 17 break;
1141
1142 17 case ENROLL_CAPTURE_SENSOR_START_CAPTURE:
1143 17 egis_etu905_exec_cmd (device, cmd_sensor_start_capture, G_N_ELEMENTS (cmd_sensor_start_capture),
1144 egis_etu905_task_ssm_next_state_cb);
1145 17 break;
1146
1147 17 case ENROLL_CAPTURE_WAIT_FINGER:
1148 17 egis_etu905_wait_finger_on_sensor (ssm, device);
1149 17 break;
1150
1151 17 case ENROLL_CAPTURE_READ_RESPONSE:
1152 17 egis_etu905_exec_cmd (device, cmd_read_capture, G_N_ELEMENTS (cmd_read_capture),
1153 egis_etu905_read_capture_cb);
1154 17 break;
1155
1156 1 case ENROLL_DUPLICATE_CHECK:
1157 1 egis_etu905_exec_cmd (device, cmd_duplicate_check, G_N_ELEMENTS (cmd_duplicate_check),
1158 egis_etu905_enroll_duplicate_check_cb);
1159 1 break;
1160
1161 1 case ENROLL_COMMIT_START:
1162 1 egis_etu905_exec_cmd (device, cmd_commit_starting, G_N_ELEMENTS (cmd_commit_starting),
1163 egis_etu905_commit_start_cb);
1164 1 break;
1165
1166 1 case ENROLL_COMMIT:
1167 1 user_id = fpi_print_generate_user_id (enroll_print->print);
1168 1 fp_dbg ("New fingerprint ID: %s", user_id);
1169
1170 1 device_print_id = g_malloc0 (EGIS_ETU905_FINGERPRINT_DATA_SIZE);
1171 1 memcpy (device_print_id, user_id,
1172 1 MIN (EGIS_ETU905_FINGERPRINT_DATA_SIZE, strlen (user_id)));
1173 1 egis_etu905_set_print_data (enroll_print->print, device_print_id, user_id);
1174
1175 1 fpi_byte_writer_init_with_size (&writer,
1176 G_N_ELEMENTS (cmd_new_print_prefix_type2) +
1177 1 + 2 + 2 + EGIS_ETU905_PARA_4_SIZE,
1178 TRUE);
1179
1/2
✓ Branch 25 → 26 taken 1 time.
✗ Branch 25 → 38 not taken.
1 if (!fpi_byte_writer_put_data_static (&writer, cmd_new_print_prefix_type2) ||
1180
1/2
✓ Branch 27 → 28 taken 1 time.
✗ Branch 27 → 38 not taken.
1 !fpi_byte_writer_put_uint8 (&writer, EGIS_ETU905_PARA_1_VALUE) ||
1181
1/2
✓ Branch 29 → 30 taken 1 time.
✗ Branch 29 → 38 not taken.
1 !fpi_byte_writer_put_uint16_le (&writer, EGIS_ETU905_PARA_2_VALUE) ||
1182
1/2
✓ Branch 31 → 32 taken 1 time.
✗ Branch 31 → 38 not taken.
1 !fpi_byte_writer_put_uint16_le (&writer, EGIS_ETU905_PARA_3_VALUE) ||
1183
1/2
✓ Branch 33 → 34 taken 1 time.
✗ Branch 33 → 38 not taken.
1 !fpi_byte_writer_put_data (&writer, (guint8 *) device_print_id,
1184 EGIS_ETU905_FINGERPRINT_DATA_SIZE))
1185 {
1186 fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO));
1187 break;
1188 }
1189
1190 1 payload_length = fpi_byte_writer_get_size (&writer);
1191 1 payload = fpi_byte_writer_reset_and_get_data (&writer);
1192 1 egis_etu905_exec_cmd (device, payload, payload_length,
1193 egis_etu905_task_ssm_next_state_cb);
1194 1 break;
1195
1196 1 case ENROLL_COMMIT_SENSOR_RESET:
1197 1 egis_etu905_exec_cmd (device, cmd_sensor_reset, G_N_ELEMENTS (cmd_sensor_reset),
1198 egis_etu905_task_ssm_next_state_cb);
1199 1 break;
1200
1201 1 case ENROLL_COMPLETE:
1202 1 egis_etu905_enroll_status_report (device, enroll_print, ENROLL_STATUS_COMPLETE, NULL);
1203 1 fpi_ssm_next_state (ssm);
1204 1 break;
1205 }
1206 74 }
1207
1208 static void
1209 1 egis_etu905_enroll (FpDevice *device)
1210 {
1211 1 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1212 1 EnrollPrint *enroll_print = g_new0 (EnrollPrint, 1);
1213
1214 1 fp_dbg ("Enroll");
1215
1216 1 fpi_device_get_enroll_data (device, &enroll_print->print);
1217 1 enroll_print->stage = 0;
1218
1219
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1 time.
1 g_assert (self->task_ssm == NULL);
1220 1 self->task_ssm = fpi_ssm_new (device, egis_etu905_enroll_run_state, ENROLL_STATES);
1221 1 fpi_ssm_set_data (self->task_ssm, g_steal_pointer (&enroll_print), g_free);
1222 1 fpi_ssm_start (self->task_ssm, egis_etu905_task_ssm_done);
1223 1 }
1224
1225 static void
1226 2 egis_etu905_identify_check_cb (FpDevice *device,
1227 guchar *buffer_in,
1228 gsize length_in,
1229 GError *error)
1230 {
1231 2 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1232 2 guint8 device_print_id[EGIS_ETU905_FINGERPRINT_DATA_SIZE];
1233 2 FpPrint *print = NULL;
1234 2 GPtrArray *prints;
1235 2 gboolean found = FALSE;
1236 2 guint index;
1237
1238 2 fp_dbg ("Identify check callback");
1239
1240
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
2 if (error)
1241 {
1242 fpi_ssm_mark_failed (self->task_ssm, error);
1243 return;
1244 }
1245
1246 /* Check that the read payload indicates "match" */
1247
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 34 taken 2 times.
2 if (egis_etu905_validate_response_suffix (buffer_in,
1248 length_in,
1249 rsp_identify_match_suffix,
1250 G_N_ELEMENTS (rsp_identify_match_suffix)))
1251 {
1252 gboolean id_known = FALSE;
1253 FpiByteReader reader;
1254
1255 /*
1256 On success, there is a 32 byte array of "something"(?) in chars 14-45
1257 and then the 32 byte array ID of the matched print comes as chars 46-77
1258 */
1259 fpi_byte_reader_init (&reader, buffer_in, length_in);
1260
1261 if (!fpi_byte_reader_set_pos (&reader,
1262 EGIS_ETU905_IDENTIFY_RESPONSE_PRINT_ID_OFFSET) ||
1263 !fpi_byte_reader_get_data_static (&reader, device_print_id))
1264 {
1265 fpi_ssm_mark_failed (self->task_ssm,
1266 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
1267 "Identify response too "
1268 "short for matched print "
1269 "id."));
1270 return;
1271 }
1272
1273 fp_dbg ("Device-reported matched print id: %s", device_print_id);
1274
1275 /* While the returned ID should indeed be part of the enrolled list, since
1276 * we got it, let's double check that this is really the case.
1277 */
1278 if (self->enrolled_ids)
1279 {
1280 for (guint i = 0; i < self->enrolled_ids->len; i++)
1281 {
1282 if (memcmp (g_ptr_array_index (self->enrolled_ids, i),
1283 device_print_id,
1284 EGIS_ETU905_FINGERPRINT_DATA_SIZE) == 0)
1285 {
1286 id_known = TRUE;
1287 break;
1288 }
1289 }
1290 }
1291
1292 if (!id_known)
1293 {
1294 fpi_ssm_mark_failed (self->task_ssm,
1295 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
1296 "Device reported a match "
1297 "for an unknown print id."));
1298 return;
1299 }
1300
1301 /* Create a new print from this device_print_id and then see if it matches
1302 * the one indicated
1303 */
1304 print = fp_print_new (device);
1305 egis_etu905_set_print_data (print, (const char *) device_print_id, NULL);
1306
1307 fp_info ("Identify successful for: %s", fp_print_get_description (print));
1308
1309 fpi_device_get_identify_data (device, &prints);
1310 found = g_ptr_array_find_with_equal_func (prints,
1311 print,
1312 (GEqualFunc) fp_print_equal,
1313 &index);
1314
1315 if (found)
1316 fpi_device_identify_report (device, g_ptr_array_index (prints, index), print, NULL);
1317 else
1318 fpi_device_identify_report (device, NULL, print, NULL);
1319 }
1320 /* If device was successfully read but it was a "not matched" */
1321
1/2
✓ Branch 35 → 36 taken 2 times.
✗ Branch 35 → 38 not taken.
2 else if (egis_etu905_validate_response_suffix (buffer_in,
1322 length_in,
1323 rsp_identify_notmatch_suffix,
1324 G_N_ELEMENTS (rsp_identify_notmatch_suffix)))
1325 {
1326 2 fp_info ("Print was not identified by the device");
1327
1328 2 fpi_device_identify_report (device, NULL, NULL, NULL);
1329 }
1330 else
1331 {
1332 fpi_ssm_mark_failed (self->task_ssm,
1333 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
1334 "Unrecognized response from device."));
1335 return;
1336 }
1337
1338 2 fpi_ssm_next_state (self->task_ssm);
1339 }
1340
1341 static void egis_etu905_identify_cancel_run_state (FpiSsm *ssm,
1342 FpDevice *device);
1343
1344 static void
1345 2 egis_etu905_identify_cancel_ssm_done (FpiSsm *ssm,
1346 FpDevice *device,
1347 GError *error)
1348 {
1349 2 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1350
1351 2 self->identify_cancel_ssm = NULL;
1352
1353
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 2 times.
2 if (error)
1354 {
1355 g_warning ("Cancel command failed: %s", error->message);
1356 g_clear_error (&error);
1357 }
1358
1359 /* The cancel flow is triggered in two scenarios:
1360 * 1. Identify was cancelled before completion (task_ssm failed with
1361 * G_IO_ERROR_CANCELLED). We need to report the cancel error.
1362 * 2. Identify completed normally but was cancelled just as it finished
1363 * (task_ssm completed, fpi_device_identify_complete was already called
1364 * in IDENTIFY_COMPLETE state). We just need to clean up.
1365 *
1366 * We distinguish these by checking if the action is still cancelled.
1367 * If fpi_device_action_is_cancelled returns TRUE, the identify was
1368 * cancelled and we need to report the error. Otherwise, identify
1369 * completed successfully and we just complete the action. */
1370
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 11 not taken.
2 if (fpi_device_action_is_cancelled (device))
1371 {
1372 2 fp_dbg ("Cancel completed, reporting cancel error");
1373 2 error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED,
1374 "Operation was cancelled");
1375 2 fpi_device_action_error (device, g_steal_pointer (&error));
1376 }
1377 else
1378 {
1379 fp_dbg ("Cancel completed after identify done, completing identify action");
1380 fpi_device_identify_complete (device, NULL);
1381 }
1382 2 }
1383
1384 static void
1385 4 egis_etu905_identify_cancel_next_state_cb (FpDevice *device,
1386 guchar *buffer_in,
1387 gsize length_in,
1388 GError *error)
1389 {
1390 4 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1391
1392
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 4 times.
4 if (error)
1393 fpi_ssm_mark_failed (self->identify_cancel_ssm, error);
1394 else
1395 4 fpi_ssm_next_state (self->identify_cancel_ssm);
1396 4 }
1397
1398 static void
1399 6 egis_etu905_identify_cancel_run_state (FpiSsm *ssm,
1400 FpDevice *device)
1401 {
1402 6 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1403
1404
3/4
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 6 taken 2 times.
✓ Branch 3 → 9 taken 2 times.
✗ Branch 3 → 12 not taken.
6 switch (fpi_ssm_get_cur_state (ssm))
1405 {
1406 2 case CANCEL_SENSOR_RESET:
1407 /* Reset sensor to ensure device is in a clean state.
1408 * Use NULL cancellable to ensure this command reaches the device. */
1409 2 egis_etu905_exec_cmd_full (device, cmd_sensor_reset,
1410 G_N_ELEMENTS (cmd_sensor_reset),
1411 egis_etu905_identify_cancel_next_state_cb,
1412 NULL);
1413 2 break;
1414
1415 2 case CANCEL_SEND_CANCEL:
1416 /* Send cmd_identify_cancel to trigger firmware template update.
1417 * Only send if identify was actually started (cmd_sensor_identify was sent).
1418 * Use NULL cancellable to ensure this command reaches the device. */
1419
2/2
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 1 time.
2 if (self->identify_started)
1420 {
1421 1 egis_etu905_exec_cmd_full (device, cmd_identify_cancel,
1422 G_N_ELEMENTS (cmd_identify_cancel),
1423 egis_etu905_identify_cancel_next_state_cb,
1424 NULL);
1425 }
1426 else
1427 {
1428 1 fpi_ssm_next_state (ssm);
1429 }
1430 break;
1431
1432 2 case CANCEL_SEND_CANCEL_RESULT:
1433 /* Retrieve the cancel result after template update is triggered.
1434 * Only send if identify was actually started.
1435 * Use NULL cancellable to ensure this command reaches the device. */
1436
2/2
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 1 time.
2 if (self->identify_started)
1437 {
1438 1 egis_etu905_exec_cmd_full (device, cmd_identify_cancel_result,
1439 G_N_ELEMENTS (cmd_identify_cancel_result),
1440 egis_etu905_identify_cancel_next_state_cb,
1441 NULL);
1442 }
1443 else
1444 {
1445 1 fpi_ssm_next_state (ssm);
1446 }
1447 break;
1448 }
1449 6 }
1450
1451 static void
1452 24 egis_etu905_identify_run_state (FpiSsm *ssm,
1453 FpDevice *device)
1454 {
1455 24 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1456 24 g_autofree guchar *payload = NULL;
1457 24 gsize payload_length = 0;
1458
1459
9/10
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 6 taken 3 times.
✓ Branch 3 → 13 taken 3 times.
✓ Branch 3 → 15 taken 3 times.
✓ Branch 3 → 17 taken 3 times.
✓ Branch 3 → 19 taken 2 times.
✓ Branch 3 → 21 taken 2 times.
✓ Branch 3 → 24 taken 2 times.
✓ Branch 3 → 26 taken 2 times.
✗ Branch 3 → 31 not taken.
24 switch (fpi_ssm_get_cur_state (ssm))
1460 {
1461 4 case IDENTIFY_GET_ENROLLED_IDS:
1462 /* get enrolled_ids from device for use in check stages below */
1463 4 egis_etu905_exec_cmd (device, cmd_list, G_N_ELEMENTS (cmd_list),
1464 egis_etu905_list_fill_enrolled_ids_cb);
1465 4 break;
1466
1467 3 case IDENTIFY_CHECK_ENROLLED_NUM:
1468
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 11 taken 3 times.
3 if (self->enrolled_ids->len == 0)
1469 {
1470 fpi_ssm_mark_failed (g_steal_pointer (&self->task_ssm),
1471 fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
1472 return;
1473 }
1474 3 fpi_ssm_next_state (ssm);
1475 3 break;
1476
1477 3 case IDENTIFY_SENSOR_RESET:
1478 3 egis_etu905_exec_cmd (device, cmd_sensor_reset, G_N_ELEMENTS (cmd_sensor_reset),
1479 egis_etu905_task_ssm_next_state_cb);
1480 3 break;
1481
1482 3 case IDENTIFY_SENSOR_IDENTIFY:
1483 3 self->identify_started = TRUE;
1484 3 egis_etu905_exec_cmd (device, cmd_sensor_identify, G_N_ELEMENTS (cmd_sensor_identify),
1485 egis_etu905_task_ssm_next_state_cb);
1486 3 break;
1487
1488 3 case IDENTIFY_WAIT_FINGER:
1489 3 egis_etu905_wait_finger_on_sensor (ssm, device);
1490 3 break;
1491
1492 2 case IDENTIFY_SENSOR_CHECK:
1493 2 egis_etu905_exec_cmd (device, cmd_sensor_check, G_N_ELEMENTS (cmd_sensor_check),
1494 egis_etu905_task_ssm_next_state_cb);
1495 2 break;
1496
1497 2 case IDENTIFY_CHECK:
1498 2 payload = egis_etu905_get_check_cmd (device, &payload_length);
1499 2 egis_etu905_exec_cmd (device, payload, payload_length,
1500 egis_etu905_identify_check_cb);
1501 2 break;
1502
1503 2 case IDENTIFY_COMPLETE_SENSOR_RESET:
1504 2 egis_etu905_exec_cmd_full (device, cmd_sensor_reset, G_N_ELEMENTS (cmd_sensor_reset),
1505 egis_etu905_task_ssm_next_state_cb,
1506 NULL);
1507 2 break;
1508
1509 /*
1510 * In Windows, the driver seems at this point to then immediately take
1511 * another read from the sensor; this is suspected to be an on-chip
1512 * "identify". However, because the user's finger is still on the sensor from
1513 * the identify, then it seems to always return positive. We will consider
1514 * this extra step unnecessary and just skip it in this driver. This driver
1515 * will instead handle matching of the FpPrint from the gallery in the
1516 * callback egis_etu905_identify_check_cb.
1517 *
1518 * If the operation was cancelled, we don't complete the identify action
1519 * here. Instead, we let the cancel flow (triggered by task_ssm_done)
1520 * complete first, and it will call fpi_device_identify_complete when done.
1521 * This ensures the device stays open until the cancel flow completes.
1522 */
1523 2 case IDENTIFY_COMPLETE:
1524
1/2
✓ Branch 27 → 28 taken 2 times.
✗ Branch 27 → 29 not taken.
2 if (!fpi_device_action_is_cancelled (device))
1525 2 fpi_device_identify_complete (device, NULL);
1526
1527 2 fpi_ssm_mark_completed (ssm);
1528 2 break;
1529 }
1530 }
1531
1532 static void
1533 4 egis_etu905_identify (FpDevice *device)
1534 {
1535 4 fp_dbg ("Identify");
1536 4 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1537
1538
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4 times.
4 g_assert (self->task_ssm == NULL);
1539 4 self->identify_started = FALSE;
1540 4 self->task_ssm = fpi_ssm_new (device, egis_etu905_identify_run_state, IDENTIFY_STATES);
1541 4 fpi_ssm_start (self->task_ssm, egis_etu905_task_ssm_done);
1542 4 }
1543
1544 static void
1545 3 egis_etu905_fw_version_cb (FpDevice *device,
1546 guchar *buffer_in,
1547 gsize length_in,
1548 GError *error)
1549 {
1550 3 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1551 3 FpiByteReader reader;
1552 3 const guint8 *fw_version_data = NULL;
1553 3 const guint prefix_length = G_N_ELEMENTS (egis_etu905_read_prefix) + 2 + 3 + 1;
1554 3 gsize fw_version_length;
1555 3 g_autofree gchar *fw_version = NULL;
1556
1557 3 fp_dbg ("Firmware version callback");
1558
1559
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3 times.
3 if (error)
1560 {
1561 fpi_ssm_mark_failed (self->task_ssm, error);
1562 return;
1563 }
1564
1565 /* Check that the read payload indicates "success" */
1566
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 10 taken 3 times.
3 if (!egis_etu905_validate_response_suffix (buffer_in,
1567 length_in,
1568 rsp_fw_version_suffix,
1569 G_N_ELEMENTS (rsp_fw_version_suffix)))
1570 {
1571 fpi_ssm_mark_failed (self->task_ssm,
1572 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
1573 "Device firmware response "
1574 "was not valid."));
1575 return;
1576 }
1577
1578 /*
1579 * FW Version is 12 bytes: a carriage return (0x0d) plus the version string
1580 * itself. Always skip [the read prefix] + [2 * check bytes] + [3 * 0x00] that
1581 * come with every payload Then we will also skip the carriage return and take
1582 * all but the last 2 bytes as the FW Version
1583 */
1584 3 fpi_byte_reader_init (&reader, buffer_in, length_in);
1585
1586
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 3 times.
3 if (!fpi_byte_reader_set_pos (&reader, prefix_length))
1587 {
1588 fpi_ssm_mark_failed (self->task_ssm,
1589 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
1590 "Device firmware response "
1591 "too short for prefix."));
1592 return;
1593 }
1594
1595 3 fw_version_length = fpi_byte_reader_get_remaining (&reader) - G_N_ELEMENTS (rsp_fw_version_suffix);
1596
1597
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 20 taken 3 times.
3 if (!fpi_byte_reader_get_data (&reader, fw_version_length, &fw_version_data))
1598 {
1599 fpi_ssm_mark_failed (self->task_ssm,
1600 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
1601 "Device firmware response "
1602 "too short for version string."));
1603 return;
1604 }
1605
1606 3 fw_version = g_strndup ((gchar *) fw_version_data, fw_version_length);
1607
1608 3 fp_info ("Device firmware version is %s", fw_version);
1609
1610 3 fpi_ssm_next_state (self->task_ssm);
1611 }
1612
1613 static void
1614 3 egis_etu905_cmd_init_cb (FpDevice *device,
1615 guchar *buffer_in,
1616 gsize length_in,
1617 GError *error)
1618 {
1619 3 fp_dbg ("cmd init callback");
1620 3 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1621
1622
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 3 times.
3 if (error)
1623 {
1624 fpi_ssm_mark_failed (self->task_ssm, error);
1625 return;
1626 }
1627
1628 /* Check that the read payload indicates "success" */
1629
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 11 taken 3 times.
3 if (!egis_etu905_validate_response_suffix (buffer_in,
1630 length_in,
1631 rsp_fw_version_suffix,
1632 G_N_ELEMENTS (rsp_fw_version_suffix)))
1633 {
1634 fpi_ssm_mark_failed (self->task_ssm,
1635 fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
1636 "cmd init response "
1637 "was not valid."));
1638 return;
1639 }
1640 3 fpi_ssm_next_state (self->task_ssm);
1641 }
1642
1643 static void
1644 3 egis_etu905_dev_init_done (FpiSsm *ssm,
1645 FpDevice *device,
1646 GError *error)
1647 {
1648
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 7 taken 3 times.
3 if (error)
1649 {
1650 g_usb_device_release_interface (
1651 fpi_device_get_usb_device (device), 0, 0, NULL);
1652 egis_etu905_task_ssm_done (ssm, device, error);
1653 return;
1654 }
1655
1656 3 egis_etu905_task_ssm_done (ssm, device, NULL);
1657 3 fpi_device_open_complete (device, NULL);
1658 }
1659
1660 static void
1661 6 egis_etu905_dev_init_handler (FpiSsm *ssm,
1662 FpDevice *device)
1663 {
1664
2/3
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 5 taken 3 times.
✗ Branch 3 → 7 not taken.
6 switch (fpi_ssm_get_cur_state (ssm))
1665 {
1666 3 case DEV_GET_FW_VERSION:
1667 3 egis_etu905_exec_cmd (device, cmd_fw_version, G_N_ELEMENTS (cmd_fw_version),
1668 egis_etu905_fw_version_cb);
1669 3 return;
1670
1671 3 case DEV_INIT_CONTROL:
1672 3 egis_etu905_exec_cmd (device, cmd_init, G_N_ELEMENTS (cmd_init),
1673 egis_etu905_cmd_init_cb);
1674 3 return;
1675
1676 default:
1677 g_assert_not_reached ();
1678 }
1679 }
1680
1681 static void
1682 1 egis_etu905_probe (FpDevice *device)
1683 {
1684 1 g_autoptr(GError) error = NULL;
1685
1/4
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 44 taken 1 time.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 49 not taken.
1 g_autofree gchar *serial = NULL;
1686 1 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1687 1 GUsbDevice *usb_dev;
1688
1689 1 fp_dbg ("%s enter --> ", G_STRFUNC);
1690
1691 /* Claim usb interface */
1692 1 usb_dev = fpi_device_get_usb_device (device);
1693
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 1 time.
1 if (!g_usb_device_open (usb_dev, &error))
1694 {
1695 fp_dbg ("%s g_usb_device_open failed %s", G_STRFUNC, error->message);
1696 fpi_device_probe_complete (device, NULL, NULL, g_steal_pointer (&error));
1697 return;
1698 }
1699
1700
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 14 taken 1 time.
1 if (!g_usb_device_reset (usb_dev, &error))
1701 {
1702 fp_dbg ("%s g_usb_device_reset failed %s", G_STRFUNC, error->message);
1703 g_usb_device_close (usb_dev, NULL);
1704 fpi_device_probe_complete (device, NULL, NULL, g_steal_pointer (&error));
1705 return;
1706 }
1707
1708
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 20 taken 1 time.
1 if (!g_usb_device_claim_interface (usb_dev, 0, 0, &error))
1709 {
1710 fp_dbg ("%s g_usb_device_claim_interface failed %s", G_STRFUNC, error->message);
1711 g_usb_device_close (usb_dev, NULL);
1712 fpi_device_probe_complete (device, NULL, NULL, g_steal_pointer (&error));
1713 return;
1714 }
1715
1716
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 1 time.
1 if (fpi_device_emulation_mode_enabled (device))
1717 1 serial = g_strdup ("emulated-device");
1718 else
1719 serial = g_usb_device_get_string_descriptor (usb_dev,
1720 g_usb_device_get_serial_number_index (usb_dev),
1721 &error);
1722
1723
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 33 taken 1 time.
1 if (error)
1724 {
1725 fp_dbg ("%s g_usb_device_get_string_descriptor failed %s", G_STRFUNC, error->message);
1726 g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (device)),
1727 0, 0, NULL);
1728 g_usb_device_close (usb_dev, NULL);
1729 fpi_device_probe_complete (device, NULL, NULL, g_steal_pointer (&error));
1730 return;
1731 }
1732
1733
1/2
✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 36 not taken.
1 if (fpi_device_get_driver_data (device) & EGIS_ETU905_DRIVER_MAX_ENROLL_STAGES_20)
1734 self->max_enroll_stages = 20;
1735 else
1736 1 self->max_enroll_stages = EGIS_ETU905_MAX_ENROLL_STAGES_DEFAULT;
1737
1738 1 fpi_device_set_nr_enroll_stages (device, self->max_enroll_stages);
1739
1740 1 g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (device)), 0, 0, NULL);
1741 1 g_usb_device_close (usb_dev, NULL);
1742
1743 1 fpi_device_probe_complete (device, serial, NULL, NULL);
1744 }
1745
1746 static void
1747 3 egis_etu905_open (FpDevice *device)
1748 {
1749 3 fp_dbg ("Opening device");
1750 3 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1751 3 g_autoptr(GError) error = NULL;
1752
1753
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3 times.
3 if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error))
1754 {
1755 fpi_device_open_complete (device, g_steal_pointer (&error));
1756 return;
1757 }
1758
1759
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 12 taken 3 times.
3 if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device),
1760 0, 0, &error))
1761 {
1762 fpi_device_open_complete (device, g_steal_pointer (&error));
1763 return;
1764 }
1765
1766
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 3 times.
3 g_assert (self->task_ssm == NULL);
1767 3 self->task_ssm = fpi_ssm_new (device, egis_etu905_dev_init_handler, DEV_INIT_STATES);
1768
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 3 times.
3 fpi_ssm_start (self->task_ssm, egis_etu905_dev_init_done);
1769 }
1770
1771 static void
1772 3 egis_etu905_close (FpDevice *device)
1773 {
1774 6 g_autoptr(GError) error = NULL;
1775 3 fp_dbg ("Closing device");
1776
1777 3 g_usb_device_release_interface (fpi_device_get_usb_device (device),
1778 0, 0, &error);
1779
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 3 times.
3 fpi_device_close_complete (device, g_steal_pointer (&error));
1780 3 }
1781
1782 static void
1783 egis_etu905_cancel_cb (FpDevice *device,
1784 guchar *buffer_in,
1785 gsize length_in,
1786 GError *error)
1787 {
1788 if (error)
1789 {
1790 g_warning ("Cancel command failed: %s", error->message);
1791 g_clear_error (&error);
1792 }
1793
1794 /* This callback is only used for enroll cancellation.
1795 * Identify cancellation uses egis_etu905_identify_cancel_ssm_done instead.
1796 * We must always call fpi_device_action_error here to complete the enroll
1797 * action, even if the task_ssm has already been freed. */
1798 error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED,
1799 "Operation was cancelled");
1800
1801 fpi_device_action_error (device, g_steal_pointer (&error));
1802 }
1803
1804 static void
1805 2 egis_etu905_cancel (FpDevice *device)
1806 {
1807 2 FpiDeviceAction action = fpi_device_get_current_action (device);
1808 2 FpiDeviceEgisEtu905 *self = FPI_DEVICE_EGIS_ETU905 (device);
1809
1810 2 fp_dbg ("Cancelling action %d", action);
1811
1812 /* The cancellation commands must reach the device to abort the ongoing
1813 * sensor operation. By the time we get here the device cancellable is
1814 * already cancelled (it aborted the in-flight operation transfer), so we
1815 * must not bind these commands to it or they would never be sent.
1816 * Cancellation is effectively non-cancellable; the commands are allowed to
1817 * fail (no callback), the device is reset by the next operation anyway.
1818 *
1819 * Note: Even if task_ssm is NULL (operation already completed), we still
1820 * need to execute the cancel commands to trigger firmware template update
1821 * and device cleanup. The egis_etu905_cancel_cb will check task_ssm and
1822 * avoid calling fpi_device_action_error if the action already completed. */
1823
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2 times.
2 if (action == FPI_DEVICE_ACTION_ENROLL)
1824 {
1825 egis_etu905_exec_cmd_full (device,
1826 cmd_enroll_discard,
1827 G_N_ELEMENTS (cmd_enroll_discard),
1828 egis_etu905_cancel_cb,
1829 NULL);
1830 }
1831
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 11 not taken.
2 else if (action == FPI_DEVICE_ACTION_IDENTIFY)
1832 {
1833
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 2 times.
2 g_assert (self->identify_cancel_ssm == NULL);
1834
1835 2 self->identify_cancel_ssm = fpi_ssm_new (device,
1836 egis_etu905_identify_cancel_run_state,
1837 CANCEL_STATES);
1838 2 fpi_ssm_start (self->identify_cancel_ssm, egis_etu905_identify_cancel_ssm_done);
1839 }
1840 else
1841 {
1842 g_warning ("Cancel called for unsupported action %d", action);
1843 }
1844 2 }
1845
1846 static void
1847 1 fpi_device_egis_etu905_init (FpiDeviceEgisEtu905 *self)
1848 {
1849 1 G_DEBUG_HERE ();
1850 1 }
1851
1852 static void
1853 127 fpi_device_egis_etu905_class_init (FpiDeviceEgisEtu905Class *klass)
1854 {
1855 127 FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
1856
1857 127 dev_class->id = FP_COMPONENT;
1858 127 dev_class->full_name = EGIS_ETU905_DRIVER_FULLNAME;
1859
1860 127 dev_class->type = FP_DEVICE_TYPE_USB;
1861 127 dev_class->scan_type = FP_SCAN_TYPE_PRESS;
1862 127 dev_class->id_table = egis_etu905_id_table;
1863 127 dev_class->nr_enroll_stages = EGIS_ETU905_MAX_ENROLL_STAGES_DEFAULT;
1864 127 dev_class->temp_hot_seconds = -1;
1865
1866 127 dev_class->probe = egis_etu905_probe;
1867 127 dev_class->open = egis_etu905_open;
1868 127 dev_class->close = egis_etu905_close;
1869 127 dev_class->identify = egis_etu905_identify;
1870 127 dev_class->enroll = egis_etu905_enroll;
1871 127 dev_class->delete = egis_etu905_delete;
1872 127 dev_class->clear_storage = egis_etu905_clear_storage;
1873 127 dev_class->list = egis_etu905_list;
1874
1875 127 fpi_device_class_auto_initialize_features (dev_class);
1876 127 dev_class->features |= FP_DEVICE_FEATURE_DUPLICATES_CHECK;
1877 127 }
1878