Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Validity VFS0050 driver for libfprint | ||
3 | * Copyright (C) 2015-2016 Konstantin Semenov <zemen17@gmail.com> | ||
4 | * | ||
5 | * This library is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; either | ||
8 | * version 2.1 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This library is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this library; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | #define FP_COMPONENT "vfs0050" | ||
21 | |||
22 | #include "drivers_api.h" | ||
23 | #include "vfs0050.h" | ||
24 | |||
25 |
4/5✓ Branch 0 taken 120 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 120 times.
✗ Branch 4 not taken.
|
768 | G_DEFINE_TYPE (FpDeviceVfs0050, fpi_device_vfs0050, FP_TYPE_IMAGE_DEVICE) |
26 | |||
27 | /* USB functions */ | ||
28 | |||
29 | /* Callback for async_write */ | ||
30 | static void | ||
31 | 14 | async_write_callback (FpiUsbTransfer *transfer, FpDevice *device, | |
32 | gpointer user_data, GError *error) | ||
33 | { | ||
34 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | if (error) |
35 | { | ||
36 | ✗ | fp_err ("USB write transfer: %s", error->message); | |
37 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
38 | ✗ | return; | |
39 | } | ||
40 | |||
41 | 14 | fpi_ssm_next_state (transfer->ssm); | |
42 | } | ||
43 | |||
44 | /* Send data to EP1, the only out endpoint */ | ||
45 | FP_GNUC_ACCESS (read_only, 3, 4) | ||
46 | static void | ||
47 | 14 | async_write (FpiSsm *ssm, | |
48 | FpDevice *dev, | ||
49 | void *data, | ||
50 | int len) | ||
51 | { | ||
52 | 14 | FpiUsbTransfer *transfer; | |
53 | |||
54 | 14 | transfer = fpi_usb_transfer_new (FP_DEVICE (dev)); | |
55 | 14 | fpi_usb_transfer_fill_bulk_full (transfer, 0x01, data, len, NULL); | |
56 | 14 | transfer->ssm = ssm; | |
57 | 14 | transfer->short_is_error = TRUE; | |
58 | 14 | fpi_usb_transfer_submit (transfer, VFS_USB_TIMEOUT, NULL, | |
59 | async_write_callback, NULL); | ||
60 | 14 | } | |
61 | |||
62 | /* Callback for async_read */ | ||
63 | static void | ||
64 | 18 | async_read_callback (FpiUsbTransfer *transfer, FpDevice *device, | |
65 | gpointer user_data, GError *error) | ||
66 | { | ||
67 | 18 | int ep = transfer->endpoint; | |
68 | |||
69 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
18 | if (error) |
70 | { | ||
71 | ✗ | fp_err ("USB read transfer on endpoint %d: %s", ep - 0x80, | |
72 | error->message); | ||
73 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
74 | ✗ | return; | |
75 | } | ||
76 | |||
77 | 18 | fpi_ssm_next_state (transfer->ssm); | |
78 | } | ||
79 | |||
80 | /* Receive data from the given ep and either discard or fill the given buffer */ | ||
81 | static void | ||
82 | 18 | async_read (FpiSsm *ssm, | |
83 | FpDevice *dev, | ||
84 | int ep, | ||
85 | void *data, | ||
86 | int len) | ||
87 | { | ||
88 | 18 | FpiUsbTransfer *transfer; | |
89 | 18 | GDestroyNotify free_func = NULL; | |
90 | |||
91 | 18 | ep |= FPI_USB_ENDPOINT_IN; | |
92 | |||
93 | 18 | transfer = fpi_usb_transfer_new (FP_DEVICE (dev)); | |
94 | 18 | transfer->ssm = ssm; | |
95 | 18 | transfer->short_is_error = TRUE; | |
96 | |||
97 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 4 times.
|
18 | if (data == NULL) |
98 | { | ||
99 | 14 | data = g_malloc0 (len); | |
100 | 14 | free_func = g_free; | |
101 | } | ||
102 | |||
103 | /* 0x83 is the only interrupt endpoint */ | ||
104 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
|
18 | if (ep == EP3_IN) |
105 | 4 | fpi_usb_transfer_fill_interrupt_full (transfer, ep, data, len, free_func); | |
106 | else | ||
107 | 14 | fpi_usb_transfer_fill_bulk_full (transfer, ep, data, len, free_func); | |
108 | |||
109 | 18 | fpi_usb_transfer_submit (transfer, VFS_USB_TIMEOUT, NULL, | |
110 | async_read_callback, NULL); | ||
111 | 18 | } | |
112 | |||
113 | /* Callback for async_abort */ | ||
114 | static void | ||
115 | 16 | async_abort_callback (FpiUsbTransfer *transfer, FpDevice *device, | |
116 | gpointer user_data, GError *error) | ||
117 | { | ||
118 | 16 | int ep = transfer->endpoint; | |
119 | |||
120 | /* In normal case endpoint is empty */ | ||
121 |
2/4✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
32 | if (g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT) || |
122 |
1/2✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
|
32 | (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0 && transfer->actual_length == 0)) |
123 | { | ||
124 | 16 | g_clear_error (&error); | |
125 | 16 | fpi_ssm_next_state (transfer->ssm); | |
126 | 16 | return; | |
127 | } | ||
128 | |||
129 | ✗ | if (error) | |
130 | { | ||
131 | ✗ | fp_err ("USB write transfer: %s", error->message); | |
132 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
133 | ✗ | return; | |
134 | } | ||
135 | |||
136 | /* Don't stop process, only print warning */ | ||
137 | ✗ | fp_warn ("Endpoint %d had extra %zd bytes readable", ep - 0x80, | |
138 | transfer->actual_length); | ||
139 | |||
140 | ✗ | fpi_ssm_jump_to_state (transfer->ssm, | |
141 | fpi_ssm_get_cur_state (transfer->ssm)); | ||
142 | } | ||
143 | |||
144 | /* Receive data from the given ep; continues to the next state once no | ||
145 | * more data is available. Otherwise the current state is repeated. */ | ||
146 | static void | ||
147 | 16 | async_abort (FpDevice *dev, FpiSsm *ssm, int ep) | |
148 | { | ||
149 | 16 | FpiUsbTransfer *transfer; | |
150 | |||
151 | 16 | ep |= FPI_USB_ENDPOINT_IN; | |
152 | |||
153 | 16 | transfer = fpi_usb_transfer_new (dev); | |
154 | |||
155 | /* 0x83 is the only interrupt endpoint */ | ||
156 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
|
16 | if (ep == EP3_IN) |
157 | 6 | fpi_usb_transfer_fill_interrupt (transfer, ep, VFS_USB_BUFFER_SIZE); | |
158 | else | ||
159 | 10 | fpi_usb_transfer_fill_bulk (transfer, ep, VFS_USB_BUFFER_SIZE); | |
160 | |||
161 | 16 | transfer->ssm = ssm; | |
162 | |||
163 | 16 | fpi_usb_transfer_submit (transfer, VFS_USB_ABORT_TIMEOUT, NULL, | |
164 | async_abort_callback, NULL); | ||
165 | 16 | } | |
166 | |||
167 | /* Image processing functions */ | ||
168 | |||
169 | /* Pixel getter for fpi_assemble_lines */ | ||
170 | static unsigned char | ||
171 | 230200 | vfs0050_get_pixel (struct fpi_line_asmbl_ctx *ctx, | |
172 | GSList * line, unsigned int x) | ||
173 | { | ||
174 | 230200 | return ((struct vfs_line *) line->data)->data[x]; | |
175 | } | ||
176 | |||
177 | /* Deviation getter for fpi_assemble_lines */ | ||
178 | static int | ||
179 | 147500 | vfs0050_get_difference (struct fpi_line_asmbl_ctx *ctx, | |
180 | GSList * line_list_1, GSList * line_list_2) | ||
181 | { | ||
182 | 147500 | struct vfs_line *line1 = line_list_1->data; | |
183 | 147500 | struct vfs_line *line2 = line_list_2->data; | |
184 | 147500 | const int shift = (VFS_IMAGE_WIDTH - VFS_NEXT_LINE_WIDTH) / 2 - 1; | |
185 | 147500 | int res = 0; | |
186 | |||
187 |
2/2✓ Branch 0 taken 4720000 times.
✓ Branch 1 taken 147500 times.
|
4867500 | for (int i = 0; i < VFS_NEXT_LINE_WIDTH; ++i) |
188 | { | ||
189 | 4720000 | int x = | |
190 | 4720000 | (int) line1->next_line_part[i] - (int) line2->data[shift + i]; | |
191 | 4720000 | res += x * x; | |
192 | } | ||
193 | 147500 | return res; | |
194 | } | ||
195 | |||
196 | #define VFS_NOISE_THRESHOLD 40 | ||
197 | |||
198 | /* Checks whether line is noise or not using hardware parameters */ | ||
199 | static char | ||
200 | 1 | is_noise (struct vfs_line *line) | |
201 | { | ||
202 | 1 | int val1 = line->noise_hash_1; | |
203 | 1 | int val2 = line->noise_hash_2; | |
204 | |||
205 | 1 | if (val1 > VFS_NOISE_THRESHOLD && | |
206 | val1 < 256 - VFS_NOISE_THRESHOLD && | ||
207 | ✗ | val2 > VFS_NOISE_THRESHOLD && val2 < 256 - VFS_NOISE_THRESHOLD) | |
208 | ✗ | return 1; | |
209 | return 0; | ||
210 | } | ||
211 | |||
212 | /* Parameters for fpi_assemble_lines */ | ||
213 | static struct fpi_line_asmbl_ctx assembling_ctx = { | ||
214 | .line_width = VFS_IMAGE_WIDTH, | ||
215 | .max_height = VFS_MAX_HEIGHT, | ||
216 | .resolution = 10, | ||
217 | .median_filter_size = 25, | ||
218 | .max_search_offset = 100, | ||
219 | .get_deviation = vfs0050_get_difference, | ||
220 | .get_pixel = vfs0050_get_pixel, | ||
221 | }; | ||
222 | |||
223 | /* Processes image before submitting */ | ||
224 | static FpImage * | ||
225 | 1 | prepare_image (FpDeviceVfs0050 *vdev) | |
226 | { | ||
227 | 1 | int height = vdev->bytes / VFS_LINE_SIZE; | |
228 | |||
229 | /* Noise cleaning. IMHO, it works pretty well | ||
230 | I've not detected cases when it doesn't work or cuts a part of the finger | ||
231 | Noise arises at the end of scan when some water remains on the scanner */ | ||
232 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | while (height > 0) |
233 | { | ||
234 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!is_noise (vdev->lines_buffer + height - 1)) |
235 | break; | ||
236 | ✗ | --height; | |
237 | } | ||
238 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (height > VFS_MAX_HEIGHT) |
239 | height = VFS_MAX_HEIGHT; | ||
240 | |||
241 | /* If image is not good enough */ | ||
242 | ✗ | if (height < VFS_IMAGE_WIDTH) | |
243 | return NULL; | ||
244 | |||
245 | /* Building GSList */ | ||
246 | 1 | GSList *lines = NULL; | |
247 | |||
248 |
2/2✓ Branch 0 taken 3000 times.
✓ Branch 1 taken 1 times.
|
3001 | for (int i = height - 1; i >= 0; --i) |
249 | 3000 | lines = g_slist_prepend (lines, vdev->lines_buffer + i); | |
250 | |||
251 | /* Perform line assembling */ | ||
252 | 1 | FpImage *img = fpi_assemble_lines (&assembling_ctx, lines, height); | |
253 | |||
254 | 1 | g_slist_free (lines); | |
255 | 1 | return img; | |
256 | } | ||
257 | |||
258 | /* Processes and submits image after fingerprint received */ | ||
259 | static void | ||
260 | 1 | submit_image (FpDeviceVfs0050 *self) | |
261 | { | ||
262 | 1 | FpImageDevice *idev = FP_IMAGE_DEVICE (self); | |
263 | |||
264 | /* We were not asked to submit image actually */ | ||
265 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (!self->active) |
266 | return; | ||
267 | |||
268 | 1 | FpImage *img = prepare_image (self); | |
269 | |||
270 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!img) |
271 | ✗ | fpi_image_device_retry_scan (idev, FP_DEVICE_RETRY_TOO_SHORT); | |
272 | else | ||
273 | 1 | fpi_image_device_image_captured (idev, img); | |
274 | |||
275 | /* Finger not on the scanner */ | ||
276 | 1 | fpi_image_device_report_finger_status (idev, FALSE); | |
277 | } | ||
278 | |||
279 | /* Proto functions */ | ||
280 | |||
281 | /* SSM loop for clear_ep2 */ | ||
282 | static void | ||
283 | 18 | clear_ep2_ssm (FpiSsm *ssm, FpDevice *dev) | |
284 | { | ||
285 | 18 | char command04 = 0x04; | |
286 | |||
287 |
3/4✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
|
18 | switch (fpi_ssm_get_cur_state (ssm)) |
288 | { | ||
289 | 6 | case SUBSM1_COMMAND_04: | |
290 | 6 | async_write (ssm, dev, &command04, sizeof (command04)); | |
291 | 6 | break; | |
292 | |||
293 | 6 | case SUBSM1_RETURN_CODE: | |
294 | 6 | async_read (ssm, dev, 1, NULL, 2); | |
295 | 6 | break; | |
296 | |||
297 | 6 | case SUBSM1_ABORT_2: | |
298 | 6 | async_abort (dev, ssm, 2); | |
299 | 6 | break; | |
300 | |||
301 | ✗ | default: | |
302 | ✗ | fp_err ("Unknown SUBSM1 state"); | |
303 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
304 | } | ||
305 | 18 | } | |
306 | |||
307 | /* Send command to clear EP2 */ | ||
308 | static void | ||
309 | 6 | clear_ep2 (FpDevice *dev, | |
310 | FpiSsm *ssm) | ||
311 | { | ||
312 | 6 | FpiSsm *subsm = fpi_ssm_new (dev, clear_ep2_ssm, SUBSM1_STATES); | |
313 | |||
314 | 6 | fpi_ssm_start_subsm (ssm, subsm); | |
315 | 6 | } | |
316 | |||
317 | static void | ||
318 | 28 | send_control_packet_ssm (FpiSsm *ssm, FpDevice *dev) | |
319 | { | ||
320 | 28 | FpDeviceVfs0050 *self = FPI_DEVICE_VFS0050 (dev); | |
321 | |||
322 |
7/8✓ 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 not taken.
|
28 | switch (fpi_ssm_get_cur_state (ssm)) |
323 | { | ||
324 | 4 | case SUBSM2_SEND_CONTROL: | |
325 | 4 | async_write (ssm, dev, self->control_packet, | |
326 | VFS_CONTROL_PACKET_SIZE); | ||
327 | 4 | break; | |
328 | |||
329 | 4 | case SUBSM2_RETURN_CODE: | |
330 | 4 | async_read (ssm, dev, 1, NULL, 2); | |
331 | 4 | break; | |
332 | |||
333 | 4 | case SUBSM2_SEND_COMMIT: | |
334 | /* next_receive_* packets could be sent only in pair */ | ||
335 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (self->control_packet == next_receive_1) |
336 | { | ||
337 | ✗ | self->control_packet = next_receive_2; | |
338 | ✗ | fpi_ssm_jump_to_state (ssm, SUBSM2_SEND_CONTROL); | |
339 | ✗ | break; | |
340 | } | ||
341 | /* commit_out in Windows differs in each commit, but I send the same each time */ | ||
342 | 4 | async_write (ssm, dev, commit_out, sizeof (commit_out)); | |
343 | 4 | break; | |
344 | |||
345 | 4 | case SUBSM2_COMMIT_RESPONSE: | |
346 | 4 | async_read (ssm, dev, 1, NULL, VFS_COMMIT_RESPONSE_SIZE); | |
347 | 4 | break; | |
348 | |||
349 | 4 | case SUBSM2_READ_EMPTY_INTERRUPT: | |
350 | /* I don't know how to check result, it could be different | ||
351 | * NOTE: I guess this comment relates to the above read. */ | ||
352 | 4 | async_read (ssm, dev, 3, self->interrupt, VFS_INTERRUPT_SIZE); | |
353 | 4 | break; | |
354 | |||
355 | 4 | case SUBSM2_ABORT_3: | |
356 | /* Check that interrupt is empty */ | ||
357 | 4 | if (memcmp | |
358 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | (self->interrupt, empty_interrupt, VFS_INTERRUPT_SIZE)) |
359 | { | ||
360 | ✗ | fp_err ("Unknown SUBSM2 state"); | |
361 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
362 | ✗ | break; | |
363 | } | ||
364 | 4 | async_abort (dev, ssm, 3); | |
365 | 4 | break; | |
366 | |||
367 | 4 | case SUBSM2_CLEAR_EP2: | |
368 | /* After turn_on Windows doesn't clear EP2 */ | ||
369 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | if (self->control_packet != turn_on) |
370 | 3 | clear_ep2 (dev, ssm); | |
371 | else | ||
372 | 1 | fpi_ssm_next_state (ssm); | |
373 | break; | ||
374 | |||
375 | ✗ | default: | |
376 | ✗ | fp_err ("Unknown SUBSM2 state"); | |
377 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
378 | } | ||
379 | 28 | } | |
380 | |||
381 | /* Send device state control packet */ | ||
382 | static void | ||
383 | 4 | send_control_packet (FpiSsm *ssm, | |
384 | FpDevice *dev) | ||
385 | { | ||
386 | 4 | FpiSsm *subsm = | |
387 | 4 | fpi_ssm_new (dev, send_control_packet_ssm, SUBSM2_STATES); | |
388 | |||
389 | 4 | fpi_ssm_start_subsm (ssm, subsm); | |
390 | 4 | } | |
391 | |||
392 | /* Clears all fprint data */ | ||
393 | static void | ||
394 | 3 | clear_data (FpDeviceVfs0050 *vdev) | |
395 | { | ||
396 | 3 | g_free (vdev->lines_buffer); | |
397 | 3 | vdev->lines_buffer = NULL; | |
398 | 3 | vdev->memory = vdev->bytes = 0; | |
399 | } | ||
400 | |||
401 | /* After receiving interrupt from EP3 */ | ||
402 | static void | ||
403 | 1 | interrupt_callback (FpiUsbTransfer *transfer, FpDevice *device, | |
404 | gpointer user_data, GError *error) | ||
405 | { | ||
406 | 1 | FpDeviceVfs0050 *self = FPI_DEVICE_VFS0050 (device); | |
407 | 1 | unsigned char *interrupt = transfer->buffer; | |
408 | |||
409 | /* we expect a cancellation error when the device is deactivating | ||
410 | * go into the SSM_CLEAR_EP2 state in that case. */ | ||
411 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
1 | if (!self->active && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
412 | { | ||
413 | ✗ | g_error_free (error); | |
414 | ✗ | fpi_ssm_jump_to_state (transfer->ssm, SSM_CLEAR_EP2); | |
415 | ✗ | return; | |
416 | } | ||
417 | |||
418 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (error) |
419 | { | ||
420 | ✗ | fp_err ("USB read interrupt transfer: %s", | |
421 | error->message); | ||
422 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
423 | ✗ | return; | |
424 | } | ||
425 | |||
426 | /* Standard interrupts */ | ||
427 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (memcmp (interrupt, interrupt1, VFS_INTERRUPT_SIZE) == 0 || |
428 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | memcmp (interrupt, interrupt2, VFS_INTERRUPT_SIZE) == 0 || |
429 | ✗ | memcmp (interrupt, interrupt3, VFS_INTERRUPT_SIZE) == 0) | |
430 | { | ||
431 | /* Go to the next ssm stage */ | ||
432 | 1 | fpi_ssm_next_state (transfer->ssm); | |
433 | 1 | return; | |
434 | } | ||
435 | |||
436 | /* When finger is on the scanner before turn_on */ | ||
437 | ✗ | if (interrupt[0] == 0x01) | |
438 | { | ||
439 | ✗ | fp_warn ("Finger is already on the scanner"); | |
440 | |||
441 | /* Go to the next ssm stage */ | ||
442 | ✗ | fpi_ssm_next_state (transfer->ssm); | |
443 | ✗ | return; | |
444 | } | ||
445 | |||
446 | /* Unknown interrupt; abort the session */ | ||
447 | ✗ | fp_err ("Unknown interrupt '%02x:%02x:%02x:%02x:%02x'!", | |
448 | interrupt[0] & 0xff, interrupt[1] & 0xff, interrupt[2] & 0xff, | ||
449 | interrupt[3] & 0xff, interrupt[4] & 0xff); | ||
450 | |||
451 | /* Abort ssm */ | ||
452 | ✗ | fpi_ssm_mark_failed (transfer->ssm, | |
453 | fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | ||
454 | } | ||
455 | |||
456 | static void | ||
457 | 25 | receive_callback (FpiUsbTransfer *transfer, FpDevice *device, | |
458 | gpointer user_data, GError *error) | ||
459 | { | ||
460 | 25 | FpDeviceVfs0050 *self = FPI_DEVICE_VFS0050 (device); | |
461 | |||
462 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
25 | if (error && !g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT)) |
463 | { | ||
464 | ✗ | fp_err ("USB read transfer: %s", error->message); | |
465 | |||
466 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
467 | ✗ | return; | |
468 | } | ||
469 | ✗ | if (error) | |
470 | ✗ | g_error_free (error); | |
471 | |||
472 | /* Capture is done when there is no more data to transfer or device timed out */ | ||
473 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 24 times.
|
25 | if (transfer->actual_length <= 0) |
474 | { | ||
475 | 1 | fpi_ssm_next_state (transfer->ssm); | |
476 | } | ||
477 | else | ||
478 | { | ||
479 | 24 | self->bytes += transfer->actual_length; | |
480 | |||
481 | /* Try reading more data */ | ||
482 | 24 | fpi_ssm_jump_to_state (transfer->ssm, | |
483 | fpi_ssm_get_cur_state (transfer->ssm)); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | /* Main SSM loop */ | ||
488 | static void | ||
489 | 44 | activate_ssm (FpiSsm *ssm, FpDevice *dev) | |
490 | { | ||
491 | 44 | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
492 | 44 | FpDeviceVfs0050 *self = FPI_DEVICE_VFS0050 (dev); | |
493 | |||
494 |
11/13✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 25 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
|
44 | switch (fpi_ssm_get_cur_state (ssm)) |
495 | { | ||
496 | 2 | case SSM_INITIAL_ABORT_1: | |
497 | 2 | async_abort (dev, ssm, 1); | |
498 | 2 | break; | |
499 | |||
500 | 2 | case SSM_INITIAL_ABORT_2: | |
501 | 2 | async_abort (dev, ssm, 2); | |
502 | 2 | break; | |
503 | |||
504 | 2 | case SSM_INITIAL_ABORT_3: | |
505 | 2 | async_abort (dev, ssm, 3); | |
506 | 2 | break; | |
507 | |||
508 | 3 | case SSM_CLEAR_EP2: | |
509 | 3 | clear_ep2 (dev, ssm); | |
510 | 3 | break; | |
511 | |||
512 | 3 | case SSM_TURN_OFF: | |
513 | /* Set control_packet argument */ | ||
514 | 3 | self->control_packet = turn_off; | |
515 | |||
516 | 3 | send_control_packet (ssm, dev); | |
517 | 3 | break; | |
518 | |||
519 | 3 | case SSM_TURN_ON: | |
520 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (!self->active) |
521 | { | ||
522 | /* The only correct exit */ | ||
523 | 2 | fpi_ssm_mark_completed (ssm); | |
524 | |||
525 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (self->need_report) |
526 | { | ||
527 | 1 | fpi_image_device_deactivate_complete (idev, | |
528 | NULL); | ||
529 | 1 | self->need_report = 0; | |
530 | } | ||
531 | break; | ||
532 | } | ||
533 | /* Set control_packet argument */ | ||
534 | 1 | self->control_packet = turn_on; | |
535 | |||
536 | 1 | send_control_packet (ssm, dev); | |
537 | 1 | break; | |
538 | |||
539 | 1 | case SSM_ASK_INTERRUPT: { | |
540 | 1 | FpiUsbTransfer *transfer; | |
541 | /* Activated, light must be blinking now */ | ||
542 | |||
543 | /* If we first time here, report that activate completed */ | ||
544 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (self->need_report) |
545 | { | ||
546 | 1 | fpi_image_device_activate_complete (idev, NULL); | |
547 | 1 | self->need_report = 0; | |
548 | } | ||
549 | |||
550 | /* Asynchronously enquire an interrupt */ | ||
551 | 1 | transfer = fpi_usb_transfer_new (dev); | |
552 | 1 | transfer->ssm = ssm; | |
553 | 1 | transfer->short_is_error = TRUE; | |
554 | 1 | fpi_usb_transfer_fill_interrupt (transfer, 0x83, VFS_INTERRUPT_SIZE); | |
555 | 1 | fpi_usb_transfer_submit (transfer, | |
556 | 0, | ||
557 | fpi_device_get_cancellable (dev), | ||
558 | interrupt_callback, NULL); | ||
559 | |||
560 | /* I've put it here to be sure that data is cleared */ | ||
561 | 1 | clear_data (self); | |
562 | |||
563 | 1 | fpi_ssm_next_state (ssm); | |
564 | 1 | break; | |
565 | } | ||
566 | |||
567 | case SSM_WAIT_INTERRUPT: | ||
568 | /* TODO: This state is unused at this point. When we | ||
569 | * are in this state, then a user cancellation will | ||
570 | * cause deactivation. In that case, the USB transfer | ||
571 | * is cancelled and the device is set to not be active. | ||
572 | * We then go into SSM_CLEAR_EP2 based on the | ||
573 | * cancellation. */ | ||
574 | break; | ||
575 | |||
576 | 25 | case SSM_RECEIVE_FINGER: { | |
577 | 25 | FpiUsbTransfer *transfer; | |
578 | |||
579 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 24 times.
|
25 | if (self->memory == 0) |
580 | { | ||
581 | /* Initialize fingerprint buffer */ | ||
582 | 1 | g_free (self->lines_buffer); | |
583 | 1 | self->memory = VFS_USB_BUFFER_SIZE; | |
584 | 1 | self->lines_buffer = g_malloc0 (self->memory); | |
585 | 1 | self->bytes = 0; | |
586 | |||
587 | /* Finger is on the scanner */ | ||
588 | 1 | fpi_image_device_report_finger_status (idev, TRUE); | |
589 | } | ||
590 | |||
591 | /* Increase buffer size while it's insufficient */ | ||
592 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 25 times.
|
48 | while (self->memory < self->bytes + VFS_USB_BUFFER_SIZE) |
593 | { | ||
594 | 23 | int pre_memory = self->memory; | |
595 | 23 | self->memory += VFS_USB_BUFFER_SIZE; | |
596 | 46 | self->lines_buffer = | |
597 | 23 | (struct vfs_line *) g_realloc (self->lines_buffer, | |
598 | self->memory); | ||
599 | 23 | memset ((guint8 *) self->lines_buffer + pre_memory, 0, | |
600 | VFS_USB_BUFFER_SIZE); | ||
601 | } | ||
602 | |||
603 | /* Receive chunk of data */ | ||
604 | 25 | transfer = fpi_usb_transfer_new (dev); | |
605 | 25 | fpi_usb_transfer_fill_bulk_full (transfer, 0x82, | |
606 | 25 | (guint8 *) self->lines_buffer + self->bytes, | |
607 | VFS_USB_BUFFER_SIZE, NULL); | ||
608 | 25 | transfer->ssm = ssm; | |
609 | 25 | fpi_usb_transfer_submit (transfer, VFS_USB_TIMEOUT, NULL, | |
610 | receive_callback, NULL); | ||
611 | 25 | break; | |
612 | } | ||
613 | |||
614 | 1 | case SSM_SUBMIT_IMAGE: | |
615 | 1 | submit_image (self); | |
616 | 1 | clear_data (self); | |
617 | |||
618 | /* Wait for probable vdev->active changing */ | ||
619 | 1 | fpi_ssm_next_state_delayed (ssm, VFS_SSM_TIMEOUT); | |
620 | 1 | break; | |
621 | |||
622 | 1 | case SSM_NEXT_RECEIVE: | |
623 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (!self->active) |
624 | { | ||
625 | /* It's the last scan */ | ||
626 | 1 | fpi_ssm_jump_to_state (ssm, SSM_CLEAR_EP2); | |
627 | 1 | break; | |
628 | } | ||
629 | |||
630 | /* Set control_packet argument */ | ||
631 | ✗ | self->control_packet = next_receive_1; | |
632 | |||
633 | ✗ | send_control_packet (ssm, dev); | |
634 | ✗ | break; | |
635 | |||
636 | ✗ | case SSM_WAIT_ANOTHER_SCAN: | |
637 | /* Orange light is on now */ | ||
638 | ✗ | fpi_ssm_jump_to_state_delayed (ssm, SSM_TURN_ON, VFS_SSM_ORANGE_TIMEOUT); | |
639 | ✗ | break; | |
640 | |||
641 | ✗ | default: | |
642 | ✗ | fp_err ("Unknown state"); | |
643 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
644 | } | ||
645 | 44 | } | |
646 | |||
647 | /* Driver functions */ | ||
648 | |||
649 | /* Callback for dev_activate ssm */ | ||
650 | static void | ||
651 | 1 | dev_activate_callback (FpiSsm *ssm, FpDevice *dev, GError *error) | |
652 | { | ||
653 | 1 | FpDeviceVfs0050 *self = FPI_DEVICE_VFS0050 (dev); | |
654 | |||
655 | 1 | self->ssm_active = 0; | |
656 | |||
657 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (error) |
658 | { | ||
659 | ✗ | g_warning ("Unhandled device activation error: %s", error->message); | |
660 | ✗ | g_error_free (error); | |
661 | } | ||
662 | |||
663 | 1 | } | |
664 | |||
665 | /* Activate device */ | ||
666 | static void | ||
667 | 1 | dev_activate (FpImageDevice *idev) | |
668 | { | ||
669 | 1 | FpDeviceVfs0050 *self = FPI_DEVICE_VFS0050 (idev); | |
670 | |||
671 | /* Initialize flags */ | ||
672 | 1 | self->active = 1; | |
673 | 1 | self->need_report = 1; | |
674 | 1 | self->ssm_active = 1; | |
675 | |||
676 | 1 | FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (idev), activate_ssm, SSM_STATES); | |
677 | |||
678 | 1 | fpi_ssm_start (ssm, dev_activate_callback); | |
679 | 1 | } | |
680 | |||
681 | /* Deactivate device */ | ||
682 | static void | ||
683 | 1 | dev_deactivate (FpImageDevice *idev) | |
684 | { | ||
685 | 1 | FpDeviceVfs0050 *self = FPI_DEVICE_VFS0050 (idev); | |
686 | |||
687 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!self->ssm_active) |
688 | { | ||
689 | ✗ | fpi_image_device_deactivate_complete (idev, NULL); | |
690 | ✗ | return; | |
691 | } | ||
692 | |||
693 | /* Initialize flags */ | ||
694 | 1 | self->active = 0; | |
695 | 1 | self->need_report = 1; | |
696 | } | ||
697 | |||
698 | /* Callback for dev_open ssm */ | ||
699 | static void | ||
700 | 1 | dev_open_callback (FpiSsm *ssm, FpDevice *dev, GError *error) | |
701 | { | ||
702 | /* Notify open complete */ | ||
703 | 1 | fpi_image_device_open_complete (FP_IMAGE_DEVICE (dev), error); | |
704 | 1 | } | |
705 | |||
706 | /* Open device */ | ||
707 | static void | ||
708 | 1 | dev_open (FpImageDevice *idev) | |
709 | { | ||
710 | 1 | GError *error = NULL; | |
711 | |||
712 | /* Claim usb interface */ | ||
713 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (!g_usb_device_claim_interface (fpi_device_get_usb_device (FP_DEVICE (idev)), 0, 0, &error)) |
714 | { | ||
715 | ✗ | fpi_image_device_open_complete (idev, error); | |
716 | ✗ | return; | |
717 | } | ||
718 | |||
719 | /* Clearing previous device state */ | ||
720 | 1 | FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (idev), activate_ssm, SSM_STATES); | |
721 | |||
722 | 1 | fpi_ssm_start (ssm, dev_open_callback); | |
723 | } | ||
724 | |||
725 | /* Close device */ | ||
726 | static void | ||
727 | 1 | dev_close (FpImageDevice *idev) | |
728 | { | ||
729 | 1 | GError *error = NULL; | |
730 | 1 | FpDeviceVfs0050 *self = FPI_DEVICE_VFS0050 (idev); | |
731 | |||
732 | 1 | clear_data (self); | |
733 | |||
734 | /* Release usb interface */ | ||
735 | 1 | g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (idev)), | |
736 | 0, 0, &error); | ||
737 | |||
738 | /* Notify close complete */ | ||
739 | 1 | fpi_image_device_close_complete (idev, error); | |
740 | 1 | } | |
741 | |||
742 | /* Usb id table of device */ | ||
743 | static const FpIdEntry id_table[] = { | ||
744 | {.vid = 0x138a, .pid = 0x0050, }, | ||
745 | {.vid = 0, .pid = 0, .driver_data = 0}, | ||
746 | }; | ||
747 | |||
748 | static void | ||
749 | 1 | fpi_device_vfs0050_init (FpDeviceVfs0050 *self) | |
750 | { | ||
751 | 1 | } | |
752 | static void | ||
753 | 120 | fpi_device_vfs0050_class_init (FpDeviceVfs0050Class *klass) | |
754 | { | ||
755 | 120 | FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); | |
756 | 120 | FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass); | |
757 | |||
758 | 120 | dev_class->id = "vfs0050"; | |
759 | 120 | dev_class->full_name = "Validity VFS0050"; | |
760 | 120 | dev_class->type = FP_DEVICE_TYPE_USB; | |
761 | 120 | dev_class->id_table = id_table; | |
762 | 120 | dev_class->scan_type = FP_SCAN_TYPE_SWIPE; | |
763 | |||
764 | 120 | img_class->img_open = dev_open; | |
765 | 120 | img_class->img_close = dev_close; | |
766 | 120 | img_class->activate = dev_activate; | |
767 | 120 | img_class->deactivate = dev_deactivate; | |
768 | |||
769 | 120 | img_class->bz3_threshold = 24; | |
770 | |||
771 | 120 | img_class->img_width = VFS_IMAGE_WIDTH; | |
772 | 120 | img_class->img_height = -1; | |
773 | } | ||
774 |