GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 82.1% 582 / 0 / 709
Functions: 97.8% 44 / 0 / 45
Branches: 51.2% 174 / 0 / 340

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