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