Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Elan driver for libfprint | ||
3 | * | ||
4 | * Copyright (C) 2017 Igor Filatov <ia.filatov@gmail.com> | ||
5 | * Copyright (C) 2018 Sébastien Béchet <sebastien.bechet@osinix.com > | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * The algorithm which libfprint uses to match fingerprints doesn't like small | ||
24 | * images like the ones these drivers produce. There's just not enough minutiae | ||
25 | * (recognizable print-specific points) on them for a reliable match. This means | ||
26 | * that unless another matching algo is found/implemented, these readers will | ||
27 | * not work as good with libfprint as they do with vendor drivers. | ||
28 | * | ||
29 | * To get bigger images the driver expects you to swipe the finger over the | ||
30 | * reader. This works quite well for readers with a rectangular 144x64 sensor. | ||
31 | * Worse than real swipe readers but good enough for day-to-day use. It needs | ||
32 | * a steady and relatively slow swipe. There are also square 96x96 sensors and | ||
33 | * I don't know whether they are in fact usable or not because I don't have one. | ||
34 | * I imagine they'd be less reliable because the resulting image is even | ||
35 | * smaller. If they can't be made usable with libfprint, I might end up dropping | ||
36 | * them because it's better than saying they work when they don't. | ||
37 | */ | ||
38 | |||
39 | #define FP_COMPONENT "elan" | ||
40 | |||
41 | #include "drivers_api.h" | ||
42 | #include "elan.h" | ||
43 | |||
44 | static unsigned char | ||
45 | 300476160 | elan_get_pixel (struct fpi_frame_asmbl_ctx *ctx, | |
46 | struct fpi_frame *frame, unsigned int x, | ||
47 | unsigned int y) | ||
48 | { | ||
49 | 300476160 | return frame->data[x + y * ctx->frame_width]; | |
50 | } | ||
51 | |||
52 | static struct fpi_frame_asmbl_ctx assembling_ctx = { | ||
53 | .frame_width = 0, | ||
54 | .frame_height = 0, | ||
55 | .image_width = 0, | ||
56 | .get_pixel = elan_get_pixel, | ||
57 | }; | ||
58 | |||
59 | struct _FpiDeviceElan | ||
60 | { | ||
61 | FpImageDevice parent; | ||
62 | |||
63 | /* device config */ | ||
64 | unsigned short dev_type; | ||
65 | unsigned short fw_ver; | ||
66 | void (*process_frame) (unsigned short *raw_frame, | ||
67 | GSList ** frames); | ||
68 | /* end device config */ | ||
69 | |||
70 | /* commands */ | ||
71 | const struct elan_cmd *cmd; | ||
72 | int cmd_timeout; | ||
73 | /* end commands */ | ||
74 | |||
75 | /* state */ | ||
76 | gboolean active; | ||
77 | gboolean deactivating; | ||
78 | unsigned char *last_read; | ||
79 | unsigned char calib_atts_left; | ||
80 | unsigned char calib_status; | ||
81 | unsigned short *background; | ||
82 | unsigned char frame_width; | ||
83 | unsigned char frame_height; | ||
84 | unsigned char raw_frame_height; | ||
85 | int num_frames; | ||
86 | GSList *frames; | ||
87 | /* end state */ | ||
88 | }; | ||
89 |
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 (FpiDeviceElan, fpi_device_elan, FP_TYPE_IMAGE_DEVICE); |
90 | |||
91 | static int | ||
92 | 1868747 | cmp_short (const void *a, const void *b) | |
93 | { | ||
94 | 1868747 | return (int) (*(short *) a - *(short *) b); | |
95 | } | ||
96 | |||
97 | static void | ||
98 | 10 | elan_dev_reset_state (FpiDeviceElan *elandev) | |
99 | { | ||
100 | 10 | G_DEBUG_HERE (); | |
101 | |||
102 | 10 | elandev->cmd = NULL; | |
103 | 10 | elandev->cmd_timeout = ELAN_CMD_TIMEOUT; | |
104 | |||
105 | 10 | elandev->calib_status = 0; | |
106 | |||
107 | 10 | g_free (elandev->last_read); | |
108 | 10 | elandev->last_read = NULL; | |
109 | |||
110 | 10 | g_slist_free_full (elandev->frames, g_free); | |
111 | 10 | elandev->frames = NULL; | |
112 | 10 | elandev->num_frames = 0; | |
113 | 10 | } | |
114 | |||
115 | static void | ||
116 | 31 | elan_save_frame (FpiDeviceElan *self, unsigned short *frame) | |
117 | { | ||
118 | 31 | G_DEBUG_HERE (); | |
119 | |||
120 | /* so far 3 types of readers by sensor dimensions and orientation have been | ||
121 | * seen in the wild: | ||
122 | * 1. 144x64. Raw images are in portrait orientation while readers themselves | ||
123 | * are placed (e.g. built into a touchpad) in landscape orientation. These | ||
124 | * need to be rotated before assembling. | ||
125 | * 2. 96x96 rotated. Like the first type but square. Likewise, need to be | ||
126 | * rotated before assembling. | ||
127 | * 3. 96x96 normal. Square and need NOT be rotated. So far there's only been | ||
128 | * 1 report of a 0c03 of this type. Hopefully this type can be identified | ||
129 | * by device id (and manufacturers don't just install the readers as they | ||
130 | * please). | ||
131 | * we also discard stripes of 'frame_margin' from bottom and top because | ||
132 | * assembling works bad for tall frames */ | ||
133 | |||
134 | 31 | unsigned char frame_width = self->frame_width; | |
135 | 31 | unsigned char frame_height = self->frame_height; | |
136 | 31 | unsigned char raw_height = self->raw_frame_height; | |
137 | 31 | unsigned char frame_margin = (raw_height - self->frame_height) / 2; | |
138 | 31 | int frame_idx, raw_idx; | |
139 | |||
140 |
2/2✓ Branch 0 taken 1550 times.
✓ Branch 1 taken 31 times.
|
1581 | for (int y = 0; y < frame_height; y++) |
141 |
2/2✓ Branch 0 taken 223200 times.
✓ Branch 1 taken 1550 times.
|
224750 | for (int x = 0; x < frame_width; x++) |
142 | { | ||
143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 223200 times.
|
223200 | if (self->dev_type & ELAN_NOT_ROTATED) |
144 | ✗ | raw_idx = x + (y + frame_margin) * frame_width; | |
145 | else | ||
146 | 223200 | raw_idx = frame_margin + y + x * raw_height; | |
147 | 223200 | frame_idx = x + y * frame_width; | |
148 | 223200 | frame[frame_idx] = | |
149 | 223200 | ((unsigned short *) self->last_read)[raw_idx]; | |
150 | } | ||
151 | 31 | } | |
152 | |||
153 | static void | ||
154 | 3 | elan_save_background (FpiDeviceElan *elandev) | |
155 | { | ||
156 | 3 | G_DEBUG_HERE (); | |
157 | |||
158 | 3 | g_free (elandev->background); | |
159 | 6 | elandev->background = | |
160 | 3 | g_malloc (elandev->frame_width * elandev->frame_height * | |
161 | sizeof (short)); | ||
162 | 3 | elan_save_frame (elandev, elandev->background); | |
163 | 3 | } | |
164 | |||
165 | /* save a frame as part of the fingerprint image | ||
166 | * background needs to have been captured for this routine to work | ||
167 | * Elantech recommends 2-step non-linear normalization in order to reduce | ||
168 | * 2^14 ADC resolution to 2^8 image: | ||
169 | * | ||
170 | * 1. background is subtracted (done here) | ||
171 | * | ||
172 | * 2. pixels are grouped in 3 groups by intensity and each group is mapped | ||
173 | * separately onto the normalized frame (done in elan_process_frame_*) | ||
174 | * ==== 16383 ____> ======== 255 | ||
175 | * / | ||
176 | * ----- lvl3 __/ | ||
177 | * 35% pixels | ||
178 | * | ||
179 | * ----- lvl2 --------> ======== 156 | ||
180 | * | ||
181 | * 30% pixels | ||
182 | * ----- lvl1 --------> ======== 99 | ||
183 | * | ||
184 | * 35% pixels | ||
185 | * ----- lvl0 __ | ||
186 | * \ | ||
187 | * ======== 0 \____> ======== 0 | ||
188 | * | ||
189 | * For some devices we don't do 2. but instead do a simple linear mapping | ||
190 | * because it seems to produce better results (or at least as good): | ||
191 | * ==== 16383 ___> ======== 255 | ||
192 | * / | ||
193 | * ------ max __/ | ||
194 | * | ||
195 | * | ||
196 | * ------ min __ | ||
197 | * \ | ||
198 | * ======== 0 \___> ======== 0 | ||
199 | */ | ||
200 | static int | ||
201 | 28 | elan_save_img_frame (FpiDeviceElan *elandev) | |
202 | { | ||
203 | 28 | G_DEBUG_HERE (); | |
204 | |||
205 | 28 | unsigned int frame_size = elandev->frame_width * elandev->frame_height; | |
206 | 28 | unsigned short *frame = g_malloc (frame_size * sizeof (short)); | |
207 | |||
208 | 28 | elan_save_frame (elandev, frame); | |
209 | 28 | unsigned int sum = 0; | |
210 | |||
211 |
2/2✓ Branch 1 taken 201600 times.
✓ Branch 2 taken 28 times.
|
201628 | for (int i = 0; i < frame_size; i++) |
212 | { | ||
213 |
2/2✓ Branch 0 taken 3037 times.
✓ Branch 1 taken 198563 times.
|
201600 | if (elandev->background[i] > frame[i]) |
214 | 3037 | frame[i] = 0; | |
215 | else | ||
216 | 198563 | frame[i] -= elandev->background[i]; | |
217 | 201600 | sum += frame[i]; | |
218 | } | ||
219 | |||
220 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
|
28 | if (sum == 0) |
221 | { | ||
222 | ✗ | fp_dbg | |
223 | ("frame darker than background; finger present during calibration?"); | ||
224 | ✗ | g_free (frame); | |
225 | ✗ | return -1; | |
226 | } | ||
227 | |||
228 | 28 | elandev->frames = g_slist_prepend (elandev->frames, frame); | |
229 | 28 | elandev->num_frames += 1; | |
230 | 28 | return 0; | |
231 | } | ||
232 | |||
233 | static void | ||
234 | ✗ | elan_process_frame_linear (unsigned short *raw_frame, | |
235 | GSList ** frames) | ||
236 | { | ||
237 | ✗ | unsigned int frame_size = | |
238 | ✗ | assembling_ctx.frame_width * assembling_ctx.frame_height; | |
239 | ✗ | struct fpi_frame *frame = | |
240 | ✗ | g_malloc (frame_size + sizeof (struct fpi_frame)); | |
241 | |||
242 | ✗ | G_DEBUG_HERE (); | |
243 | |||
244 | ✗ | unsigned short min = 0xffff, max = 0; | |
245 | |||
246 | ✗ | for (int i = 0; i < frame_size; i++) | |
247 | { | ||
248 | ✗ | if (raw_frame[i] < min) | |
249 | min = raw_frame[i]; | ||
250 | ✗ | if (raw_frame[i] > max) | |
251 | max = raw_frame[i]; | ||
252 | } | ||
253 | |||
254 | ✗ | g_assert (max != min); | |
255 | |||
256 | unsigned short px; | ||
257 | |||
258 | ✗ | for (int i = 0; i < frame_size; i++) | |
259 | { | ||
260 | ✗ | px = raw_frame[i]; | |
261 | ✗ | px = (px - min) * 0xff / (max - min); | |
262 | ✗ | frame->data[i] = (unsigned char) px; | |
263 | } | ||
264 | |||
265 | ✗ | *frames = g_slist_prepend (*frames, frame); | |
266 | ✗ | } | |
267 | |||
268 | static void | ||
269 | 24 | elan_process_frame_thirds (unsigned short *raw_frame, | |
270 | GSList ** frames) | ||
271 | { | ||
272 | 24 | G_DEBUG_HERE (); | |
273 | |||
274 | 24 | unsigned int frame_size = | |
275 | 24 | assembling_ctx.frame_width * assembling_ctx.frame_height; | |
276 | 24 | struct fpi_frame *frame = | |
277 | 24 | g_malloc (frame_size + sizeof (struct fpi_frame)); | |
278 | |||
279 | 24 | unsigned short lvl0, lvl1, lvl2, lvl3; | |
280 | 24 | unsigned short *sorted = g_malloc (frame_size * sizeof (short)); | |
281 | |||
282 | 24 | memcpy (sorted, raw_frame, frame_size * sizeof (short)); | |
283 | 24 | qsort (sorted, frame_size, sizeof (short), cmp_short); | |
284 | 24 | lvl0 = sorted[0]; | |
285 | 24 | lvl1 = sorted[frame_size * 3 / 10]; | |
286 | 24 | lvl2 = sorted[frame_size * 65 / 100]; | |
287 | 24 | lvl3 = sorted[frame_size - 1]; | |
288 | 24 | g_free (sorted); | |
289 | |||
290 | 24 | unsigned short px; | |
291 | |||
292 |
2/2✓ Branch 1 taken 172800 times.
✓ Branch 2 taken 24 times.
|
172824 | for (int i = 0; i < frame_size; i++) |
293 | { | ||
294 | 172800 | px = raw_frame[i]; | |
295 |
2/2✓ Branch 0 taken 51794 times.
✓ Branch 1 taken 121006 times.
|
172800 | if (lvl0 <= px && px < lvl1) |
296 | 51794 | px = (px - lvl0) * 99 / (lvl1 - lvl0); | |
297 |
2/2✓ Branch 0 taken 60488 times.
✓ Branch 1 taken 60518 times.
|
121006 | else if (lvl1 <= px && px < lvl2) |
298 | 60488 | px = 99 + ((px - lvl1) * 56 / (lvl2 - lvl1)); | |
299 | else // (lvl2 <= px && px <= lvl3) | ||
300 | 60518 | px = 155 + ((px - lvl2) * 100 / (lvl3 - lvl2)); | |
301 | 172800 | frame->data[i] = (unsigned char) px; | |
302 | } | ||
303 | |||
304 | 24 | *frames = g_slist_prepend (*frames, frame); | |
305 | 24 | } | |
306 | |||
307 | static void | ||
308 | 2 | elan_submit_image (FpImageDevice *dev) | |
309 | { | ||
310 | 2 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
311 | 2 | GSList *raw_frames; | |
312 | 2 | GSList *frames = NULL; | |
313 | 2 | FpImage *img; | |
314 | |||
315 | 2 | G_DEBUG_HERE (); | |
316 | |||
317 | 2 | raw_frames = g_slist_nth (self->frames, ELAN_SKIP_LAST_FRAMES); | |
318 | |||
319 | 2 | assembling_ctx.frame_width = self->frame_width; | |
320 | 2 | assembling_ctx.frame_height = self->frame_height; | |
321 | 2 | assembling_ctx.image_width = self->frame_width * 3 / 2; | |
322 | 2 | g_slist_foreach (raw_frames, (GFunc) self->process_frame, &frames); | |
323 | 2 | fpi_do_movement_estimation (&assembling_ctx, frames); | |
324 | 2 | img = fpi_assemble_frames (&assembling_ctx, frames); | |
325 | 2 | img->flags |= FPI_IMAGE_PARTIAL; | |
326 | |||
327 | 2 | g_slist_free_full (frames, g_free); | |
328 | |||
329 | 2 | fpi_image_device_image_captured (dev, img); | |
330 | 2 | } | |
331 | |||
332 | static void | ||
333 | 80 | elan_cmd_done (FpiSsm *ssm) | |
334 | { | ||
335 | 80 | G_DEBUG_HERE (); | |
336 | 80 | fpi_ssm_next_state (ssm); | |
337 | 80 | } | |
338 | |||
339 | static void | ||
340 | 152 | elan_cmd_cb (FpiUsbTransfer *transfer, FpDevice *dev, | |
341 | gpointer user_data, GError *error) | ||
342 | { | ||
343 | 152 | FpiSsm *ssm = transfer->ssm; | |
344 | 152 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
345 | |||
346 | 152 | G_DEBUG_HERE (); | |
347 | |||
348 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
|
152 | if (error) |
349 | { | ||
350 | /* XXX: In the cancellation case we used to not | ||
351 | * mark the SSM as failed?! */ | ||
352 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
353 | ✗ | return; | |
354 | } | ||
355 | |||
356 | /* XXX: We used to reset the device in error cases! */ | ||
357 |
2/2✓ Branch 0 taken 74 times.
✓ Branch 1 taken 78 times.
|
152 | if (transfer->endpoint & FPI_USB_ENDPOINT_IN) |
358 | { | ||
359 | /* just finished receiving */ | ||
360 | 74 | self->last_read = g_memdup2 (transfer->buffer, transfer->actual_length); | |
361 | 74 | elan_cmd_done (ssm); | |
362 | } | ||
363 | else | ||
364 | { | ||
365 | /* just finished sending */ | ||
366 | 78 | G_DEBUG_HERE (); | |
367 | 78 | elan_cmd_read (ssm, dev); | |
368 | } | ||
369 | } | ||
370 | |||
371 | static void | ||
372 | 78 | elan_cmd_read (FpiSsm *ssm, FpDevice *dev) | |
373 | { | ||
374 | 78 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
375 | 78 | FpiUsbTransfer *transfer; | |
376 | 78 | GCancellable *cancellable = NULL; | |
377 | 78 | int response_len = self->cmd->response_len; | |
378 | |||
379 | 78 | G_DEBUG_HERE (); | |
380 | |||
381 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 74 times.
|
78 | if (self->cmd->response_len == ELAN_CMD_SKIP_READ) |
382 | { | ||
383 | 4 | fp_dbg ("skipping read, not expecting anything"); | |
384 | 4 | elan_cmd_done (ssm); | |
385 | 4 | return; | |
386 | } | ||
387 | |||
388 |
1/2✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
|
74 | if (self->dev_type == ELAN_0C42) |
389 | { | ||
390 | /* ELAN_0C42 sends an extra byte in one byte responses */ | ||
391 | ✗ | if (self->cmd->response_len == 1) | |
392 | 74 | response_len = 2; | |
393 | } | ||
394 | |||
395 |
2/2✓ Branch 0 taken 31 times.
✓ Branch 1 taken 43 times.
|
74 | if (self->cmd->cmd == get_image_cmd.cmd) |
396 | /* raw data has 2-byte "pixels" and the frame is vertical */ | ||
397 | 31 | response_len = | |
398 | 31 | self->raw_frame_height * self->frame_width * 2; | |
399 | |||
400 |
2/2✓ Branch 0 taken 68 times.
✓ Branch 1 taken 6 times.
|
74 | g_clear_pointer (&self->last_read, g_free); |
401 | |||
402 | 74 | transfer = fpi_usb_transfer_new (dev); | |
403 | 74 | transfer->ssm = ssm; | |
404 | 74 | transfer->short_is_error = TRUE; | |
405 | |||
406 | 74 | fpi_usb_transfer_fill_bulk (transfer, | |
407 | 74 | self->cmd->response_in, | |
408 | response_len); | ||
409 | |||
410 |
1/2✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
|
74 | if (!self->cmd->never_cancel) |
411 | 74 | cancellable = fpi_device_get_cancellable (dev); | |
412 | |||
413 | 74 | fpi_usb_transfer_submit (transfer, self->cmd_timeout, cancellable, elan_cmd_cb, NULL); | |
414 | } | ||
415 | |||
416 | static void | ||
417 | 80 | elan_run_cmd (FpiSsm *ssm, | |
418 | FpDevice *dev, | ||
419 | const struct elan_cmd *cmd, | ||
420 | int cmd_timeout) | ||
421 | { | ||
422 | 80 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
423 | 80 | FpiUsbTransfer *transfer; | |
424 | 80 | GCancellable *cancellable = NULL; | |
425 | |||
426 | 80 | self->cmd = cmd; | |
427 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 30 times.
|
80 | if (cmd_timeout != -1) |
428 | 50 | self->cmd_timeout = cmd_timeout; | |
429 | |||
430 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
80 | if (cmd->devices != ELAN_ALL_DEV && !(cmd->devices & self->dev_type)) |
431 | { | ||
432 | 2 | fp_dbg ("skipping command 0x%x 0x%x for this device (for devices 0x%x but device is 0x%x)", | |
433 | cmd->cmd[0], cmd->cmd[1], cmd->devices, self->dev_type); | ||
434 | 2 | elan_cmd_done (ssm); | |
435 | 2 | return; | |
436 | } | ||
437 | |||
438 | 78 | transfer = fpi_usb_transfer_new (dev); | |
439 | 78 | transfer->ssm = ssm; | |
440 | 78 | transfer->short_is_error = TRUE; | |
441 | |||
442 | 78 | fpi_usb_transfer_fill_bulk_full (transfer, | |
443 | ELAN_EP_CMD_OUT, | ||
444 | 78 | (guint8 *) cmd->cmd, | |
445 | ELAN_CMD_LEN, | ||
446 | NULL); | ||
447 | |||
448 |
2/2✓ Branch 0 taken 76 times.
✓ Branch 1 taken 2 times.
|
78 | if (!self->cmd->never_cancel) |
449 | 76 | cancellable = fpi_device_get_cancellable (dev); | |
450 | |||
451 | 78 | fpi_usb_transfer_submit (transfer, | |
452 | 78 | self->cmd_timeout, | |
453 | cancellable, | ||
454 | elan_cmd_cb, | ||
455 | NULL); | ||
456 | } | ||
457 | |||
458 | enum stop_capture_states { | ||
459 | STOP_CAPTURE, | ||
460 | STOP_CAPTURE_NUM_STATES, | ||
461 | }; | ||
462 | |||
463 | static void | ||
464 | 2 | stop_capture_run_state (FpiSsm *ssm, FpDevice *dev) | |
465 | { | ||
466 | 2 | G_DEBUG_HERE (); | |
467 | |||
468 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | switch (fpi_ssm_get_cur_state (ssm)) |
469 | { | ||
470 | 2 | case STOP_CAPTURE: | |
471 | 2 | elan_run_cmd (ssm, dev, &stop_cmd, | |
472 | ELAN_CMD_TIMEOUT); | ||
473 | 2 | break; | |
474 | } | ||
475 | 2 | } | |
476 | |||
477 | static void | ||
478 | 2 | stop_capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) | |
479 | { | ||
480 | 2 | FpImageDevice *dev = FP_IMAGE_DEVICE (_dev); | |
481 | 2 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
482 | |||
483 | 2 | G_DEBUG_HERE (); | |
484 | |||
485 | |||
486 | /* The device is inactive at this point. */ | ||
487 | 2 | self->active = FALSE; | |
488 | |||
489 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (self->deactivating) |
490 | { | ||
491 | /* Simply complete the pending deactivation. */ | ||
492 | ✗ | self->deactivating = FALSE; | |
493 | ✗ | fpi_image_device_deactivate_complete (dev, error); | |
494 | ✗ | return; | |
495 | } | ||
496 | |||
497 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (!error) |
498 | 2 | fpi_image_device_report_finger_status (dev, FALSE); | |
499 | else | ||
500 | /* NOTE: We cannot get a cancellation error here. */ | ||
501 | ✗ | fpi_image_device_session_error (dev, error); | |
502 | } | ||
503 | |||
504 | static void | ||
505 | 2 | elan_stop_capture (FpiDeviceElan *self) | |
506 | { | ||
507 | 2 | G_DEBUG_HERE (); | |
508 | |||
509 | 2 | elan_dev_reset_state (self); | |
510 | |||
511 | 2 | FpiSsm *ssm = | |
512 | 2 | fpi_ssm_new (FP_DEVICE (self), stop_capture_run_state, STOP_CAPTURE_NUM_STATES); | |
513 | |||
514 | 2 | fpi_ssm_start (ssm, stop_capture_complete); | |
515 | 2 | } | |
516 | |||
517 | enum capture_states { | ||
518 | CAPTURE_LED_ON, | ||
519 | CAPTURE_WAIT_FINGER, | ||
520 | CAPTURE_READ_DATA, | ||
521 | CAPTURE_CHECK_ENOUGH_FRAMES, | ||
522 | CAPTURE_NUM_STATES, | ||
523 | }; | ||
524 | |||
525 | static void | ||
526 | 90 | capture_run_state (FpiSsm *ssm, FpDevice *dev) | |
527 | { | ||
528 | 90 | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
529 | 90 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
530 | 90 | int r; | |
531 | |||
532 |
4/5✓ Branch 1 taken 2 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 30 times.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
|
90 | switch (fpi_ssm_get_cur_state (ssm)) |
533 | { | ||
534 | 2 | case CAPTURE_LED_ON: | |
535 | 2 | elan_run_cmd (ssm, dev, &led_on_cmd, ELAN_CMD_TIMEOUT); | |
536 | 2 | break; | |
537 | |||
538 | 30 | case CAPTURE_WAIT_FINGER: | |
539 | 30 | elan_run_cmd (ssm, dev, &pre_scan_cmd, -1); | |
540 | 30 | break; | |
541 | |||
542 | 30 | case CAPTURE_READ_DATA: | |
543 | /* 0x55 - finger present | ||
544 | * 0xff - device not calibrated (probably) */ | ||
545 |
3/4✓ Branch 0 taken 28 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
|
30 | if (self->last_read && self->last_read[0] == 0x55) |
546 | { | ||
547 | 28 | fpi_image_device_report_finger_status (idev, TRUE); | |
548 | 28 | elan_run_cmd (ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT); | |
549 | } | ||
550 | else | ||
551 | { | ||
552 | /* XXX: The timeout is emulated incorrectly, resulting in a zero byte read. */ | ||
553 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0) |
554 | 2 | fpi_ssm_mark_completed (ssm); | |
555 | else | ||
556 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
557 | } | ||
558 | break; | ||
559 | |||
560 | 28 | case CAPTURE_CHECK_ENOUGH_FRAMES: | |
561 | 28 | r = elan_save_img_frame (self); | |
562 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
|
28 | if (r < 0) |
563 | { | ||
564 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); | |
565 | } | ||
566 |
1/2✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
|
28 | else if (self->num_frames < ELAN_MAX_FRAMES) |
567 | { | ||
568 | /* quickly stop if finger is removed */ | ||
569 | 28 | self->cmd_timeout = ELAN_FINGER_TIMEOUT; | |
570 | 28 | fpi_ssm_jump_to_state (ssm, CAPTURE_WAIT_FINGER); | |
571 | } | ||
572 | else | ||
573 | { | ||
574 | ✗ | fpi_ssm_next_state (ssm); | |
575 | } | ||
576 | break; | ||
577 | } | ||
578 | 90 | } | |
579 | |||
580 | static void | ||
581 | 2 | capture_complete (FpiSsm *ssm, FpDevice *_dev, GError *error) | |
582 | { | ||
583 | 2 | FpImageDevice *dev = FP_IMAGE_DEVICE (_dev); | |
584 | 2 | FpiDeviceElan *self = FPI_DEVICE_ELAN (_dev); | |
585 | |||
586 | 2 | G_DEBUG_HERE (); | |
587 | |||
588 | /* either max frames captured or timed out waiting for the next frame */ | ||
589 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
2 | if (!error || |
590 | ✗ | (g_error_matches (error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT) && | |
591 | ✗ | fpi_ssm_get_cur_state (ssm) == CAPTURE_WAIT_FINGER)) | |
592 | { | ||
593 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (self->num_frames >= ELAN_MIN_FRAMES) |
594 | { | ||
595 | 2 | elan_submit_image (dev); | |
596 | } | ||
597 | else | ||
598 | { | ||
599 | ✗ | fp_dbg ("swipe too short: want >= %d frames, got %d", | |
600 | ELAN_MIN_FRAMES, self->num_frames); | ||
601 | ✗ | fpi_image_device_retry_scan (dev, FP_DEVICE_RETRY_TOO_SHORT); | |
602 | } | ||
603 | 2 | g_clear_error (&error); | |
604 | } | ||
605 | else | ||
606 | { | ||
607 | ✗ | fpi_image_device_session_error (dev, error); | |
608 | } | ||
609 | |||
610 | /* Note: We always stop capturing even if that may not be needed always. | ||
611 | * Doing this between captures appears to make it at least less likely for | ||
612 | * devices to end up in a bad state. | ||
613 | */ | ||
614 | 2 | elan_stop_capture (self); | |
615 | 2 | } | |
616 | |||
617 | static void | ||
618 | 2 | elan_capture (FpiDeviceElan *self) | |
619 | { | ||
620 | 2 | G_DEBUG_HERE (); | |
621 | |||
622 | 2 | elan_dev_reset_state (self); | |
623 | 2 | FpiSsm *ssm = | |
624 | 2 | fpi_ssm_new (FP_DEVICE (self), capture_run_state, CAPTURE_NUM_STATES); | |
625 | |||
626 | 2 | fpi_ssm_start (ssm, capture_complete); | |
627 | 2 | } | |
628 | |||
629 | /* this function needs to have elandev->background and elandev->last_read to be | ||
630 | * the calibration mean */ | ||
631 | static int | ||
632 | 3 | elan_need_calibration (FpiDeviceElan *elandev) | |
633 | { | ||
634 | 3 | G_DEBUG_HERE (); | |
635 | |||
636 | 3 | unsigned short calib_mean = | |
637 | 3 | elandev->last_read[0] * 0xff + elandev->last_read[1]; | |
638 | 3 | unsigned int bg_mean = 0, delta; | |
639 | 3 | unsigned int frame_size = elandev->frame_width * elandev->frame_height; | |
640 | |||
641 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | g_assert (frame_size != 0); |
642 | |||
643 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (elandev->dev_type == ELAN_0C42) |
644 | { | ||
645 | ✗ | if (calib_mean > 5500 || | |
646 | calib_mean < 2500) | ||
647 | { | ||
648 | ✗ | fp_dbg ("Forcing needed recalibration"); | |
649 | ✗ | return 1; | |
650 | } | ||
651 | } | ||
652 | |||
653 |
2/2✓ Branch 0 taken 21600 times.
✓ Branch 1 taken 3 times.
|
21603 | for (int i = 0; i < frame_size; i++) |
654 | 21600 | bg_mean += elandev->background[i]; | |
655 | 3 | bg_mean /= frame_size; | |
656 | |||
657 | 6 | delta = | |
658 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | bg_mean > calib_mean ? bg_mean - calib_mean : calib_mean - bg_mean; |
659 | |||
660 | 3 | fp_dbg ("calibration mean: %d, bg mean: %d, delta: %d", calib_mean, | |
661 | bg_mean, delta); | ||
662 | |||
663 | 3 | return delta > ELAN_CALIBRATION_MAX_DELTA ? 1 : 0; | |
664 | } | ||
665 | |||
666 | enum calibrate_states { | ||
667 | CALIBRATE_GET_BACKGROUND, | ||
668 | CALIBRATE_SAVE_BACKGROUND, | ||
669 | CALIBRATE_GET_MEAN, | ||
670 | CALIBRATE_CHECK_NEEDED, | ||
671 | CALIBRATE_GET_STATUS, | ||
672 | CALIBRATE_CHECK_STATUS, | ||
673 | CALIBRATE_REPEAT_STATUS, | ||
674 | CALIBRATE_NUM_STATES, | ||
675 | }; | ||
676 | |||
677 | static gboolean | ||
678 | 3 | elan_supports_calibration (FpiDeviceElan *elandev) | |
679 | { | ||
680 | 3 | if (elandev->dev_type == ELAN_0C42) | |
681 | return TRUE; | ||
682 | |||
683 | 3 | return elandev->fw_ver >= ELAN_MIN_CALIBRATION_FW; | |
684 | } | ||
685 | |||
686 | static void | ||
687 | 29 | calibrate_run_state (FpiSsm *ssm, FpDevice *dev) | |
688 | { | ||
689 | 29 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
690 | |||
691 | 29 | G_DEBUG_HERE (); | |
692 | |||
693 |
7/8✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
|
29 | switch (fpi_ssm_get_cur_state (ssm)) |
694 | { | ||
695 | 3 | case CALIBRATE_GET_BACKGROUND: | |
696 | 3 | elan_run_cmd (ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT); | |
697 | 3 | break; | |
698 | |||
699 | 3 | case CALIBRATE_SAVE_BACKGROUND: | |
700 | 3 | elan_save_background (self); | |
701 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | if (!elan_supports_calibration (self)) |
702 | { | ||
703 | ✗ | fp_dbg ("FW does not support calibration"); | |
704 | ✗ | fpi_ssm_mark_completed (ssm); | |
705 | } | ||
706 | else | ||
707 | { | ||
708 | 3 | fpi_ssm_next_state (ssm); | |
709 | } | ||
710 | break; | ||
711 | |||
712 | 3 | case CALIBRATE_GET_MEAN: | |
713 | 3 | elan_run_cmd (ssm, dev, &get_calib_mean_cmd, ELAN_CMD_TIMEOUT); | |
714 | 3 | break; | |
715 | |||
716 | 3 | case CALIBRATE_CHECK_NEEDED: | |
717 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
|
3 | if (elan_need_calibration (self)) |
718 | { | ||
719 | 1 | self->calib_status = 0; | |
720 | 1 | fpi_ssm_next_state (ssm); | |
721 | } | ||
722 | else | ||
723 | { | ||
724 | 2 | fpi_ssm_mark_completed (ssm); | |
725 | } | ||
726 | break; | ||
727 | |||
728 | 6 | case CALIBRATE_GET_STATUS: | |
729 | 6 | self->calib_atts_left -= 1; | |
730 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (self->calib_atts_left) |
731 | { | ||
732 | 6 | elan_run_cmd (ssm, dev, &get_calib_status_cmd, | |
733 | ELAN_CMD_TIMEOUT); | ||
734 | } | ||
735 | else | ||
736 | { | ||
737 | ✗ | fp_dbg ("calibration failed"); | |
738 | ✗ | fpi_ssm_mark_failed (ssm, | |
739 | fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, | ||
740 | "Calibration failed!")); | ||
741 | } | ||
742 | break; | ||
743 | |||
744 | 6 | case CALIBRATE_CHECK_STATUS: | |
745 | /* 0x01 - retry, 0x03 - ok | ||
746 | * It appears that when reading the response soon after 0x4023 the device | ||
747 | * can return 0x03, and only after some time (up to 100 ms) the response | ||
748 | * changes to 0x01. It stays that way for some time and then changes back | ||
749 | * to 0x03. Because of this we don't just expect 0x03, we want to see 0x01 | ||
750 | * first. This is to make sure that a full calibration loop has completed */ | ||
751 | 6 | fp_dbg ("calibration status: 0x%02x", self->last_read[0]); | |
752 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
|
6 | if (self->calib_status == 0x01 && |
753 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
|
5 | self->last_read[0] == 0x03) |
754 | { | ||
755 | 1 | self->calib_status = 0x03; | |
756 | 1 | fpi_ssm_jump_to_state (ssm, CALIBRATE_GET_BACKGROUND); | |
757 | } | ||
758 | else | ||
759 | { | ||
760 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
|
5 | if (self->calib_status == 0x00 && |
761 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | self->last_read[0] == 0x01) |
762 | 1 | self->calib_status = 0x01; | |
763 | 5 | fpi_ssm_next_state_delayed (ssm, 50); | |
764 | } | ||
765 | break; | ||
766 | |||
767 | 5 | case CALIBRATE_REPEAT_STATUS: | |
768 | 5 | fpi_ssm_jump_to_state (ssm, CALIBRATE_GET_STATUS); | |
769 | 5 | break; | |
770 | } | ||
771 | 29 | } | |
772 | |||
773 | static void | ||
774 | 2 | calibrate_complete (FpiSsm *ssm, FpDevice *dev, GError *error) | |
775 | { | ||
776 | 2 | G_DEBUG_HERE (); | |
777 | |||
778 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (error) |
779 | { | ||
780 | ✗ | fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error); | |
781 | ✗ | elan_stop_capture (FPI_DEVICE_ELAN (dev)); | |
782 | } | ||
783 | else | ||
784 | { | ||
785 | 2 | elan_capture (FPI_DEVICE_ELAN (dev)); | |
786 | } | ||
787 | 2 | } | |
788 | |||
789 | static void | ||
790 | 2 | elan_calibrate (FpiDeviceElan *self) | |
791 | { | ||
792 | 2 | G_DEBUG_HERE (); | |
793 | |||
794 | 2 | elan_dev_reset_state (self); | |
795 | |||
796 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | g_return_if_fail (!self->active); |
797 | 2 | self->active = TRUE; | |
798 | 2 | self->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS; | |
799 | |||
800 | 2 | FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (self), calibrate_run_state, | |
801 | CALIBRATE_NUM_STATES); | ||
802 | |||
803 | 2 | fpi_ssm_start (ssm, calibrate_complete); | |
804 | } | ||
805 | |||
806 | enum activate_states { | ||
807 | ACTIVATE_GET_FW_VER, | ||
808 | ACTIVATE_SET_FW_VER, | ||
809 | ACTIVATE_GET_SENSOR_DIM, | ||
810 | ACTIVATE_SET_SENSOR_DIM, | ||
811 | ACTIVATE_CMD_1, | ||
812 | ACTIVATE_NUM_STATES, | ||
813 | }; | ||
814 | |||
815 | static void | ||
816 | 10 | activate_run_state (FpiSsm *ssm, FpDevice *dev) | |
817 | { | ||
818 | 10 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
819 | |||
820 | 10 | G_DEBUG_HERE (); | |
821 | |||
822 |
5/6✓ 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 not taken.
|
10 | switch (fpi_ssm_get_cur_state (ssm)) |
823 | { | ||
824 | 2 | case ACTIVATE_GET_FW_VER: | |
825 | 2 | elan_run_cmd (ssm, dev, &get_fw_ver_cmd, ELAN_CMD_TIMEOUT); | |
826 | 2 | break; | |
827 | |||
828 | 2 | case ACTIVATE_SET_FW_VER: | |
829 | 2 | self->fw_ver = | |
830 | 2 | (self->last_read[0] << 8 | self->last_read[1]); | |
831 | 2 | fp_dbg ("FW ver 0x%04hx", self->fw_ver); | |
832 | 2 | fpi_ssm_next_state (ssm); | |
833 | 2 | break; | |
834 | |||
835 | 2 | case ACTIVATE_GET_SENSOR_DIM: | |
836 | 2 | elan_run_cmd (ssm, dev, &get_sensor_dim_cmd, ELAN_CMD_TIMEOUT); | |
837 | 2 | break; | |
838 | |||
839 | 2 | case ACTIVATE_SET_SENSOR_DIM: | |
840 | /* see elan_save_frame for details */ | ||
841 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (self->dev_type & ELAN_NOT_ROTATED) |
842 | { | ||
843 | ✗ | self->frame_width = self->last_read[0]; | |
844 | ✗ | self->frame_height = self->raw_frame_height = | |
845 | self->last_read[2]; | ||
846 | } | ||
847 | else | ||
848 | { | ||
849 | 2 | self->frame_width = self->last_read[2]; | |
850 | 2 | self->frame_height = self->raw_frame_height = | |
851 | 2 | self->last_read[0]; | |
852 | } | ||
853 | /* Work-around sensors returning the sizes as zero-based index | ||
854 | * rather than the number of pixels. */ | ||
855 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if ((self->frame_width % 2 == 1) && |
856 | (self->frame_height % 2 == 1)) | ||
857 | { | ||
858 | ✗ | self->frame_width++; | |
859 | ✗ | self->frame_height++; | |
860 | ✗ | self->raw_frame_height = self->frame_height; | |
861 | } | ||
862 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (self->frame_height > ELAN_MAX_FRAME_HEIGHT) |
863 | 2 | self->frame_height = ELAN_MAX_FRAME_HEIGHT; | |
864 | 2 | fp_dbg ("sensor dimensions, WxH: %dx%d", self->frame_width, | |
865 | self->raw_frame_height); | ||
866 | 2 | fpi_ssm_next_state (ssm); | |
867 | 2 | break; | |
868 | |||
869 | 2 | case ACTIVATE_CMD_1: | |
870 | /* TODO: find out what this does, if we need it */ | ||
871 | 2 | elan_run_cmd (ssm, dev, &activate_cmd_1, ELAN_CMD_TIMEOUT); | |
872 | 2 | break; | |
873 | } | ||
874 | 10 | } | |
875 | |||
876 | static void | ||
877 | 2 | activate_complete (FpiSsm *ssm, FpDevice *dev, GError *error) | |
878 | { | ||
879 | 2 | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
880 | |||
881 | 2 | G_DEBUG_HERE (); | |
882 | |||
883 | 2 | fpi_image_device_activate_complete (idev, error); | |
884 | |||
885 | 2 | } | |
886 | |||
887 | static void | ||
888 | 2 | elan_activate (FpImageDevice *dev) | |
889 | { | ||
890 | 2 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
891 | |||
892 | 2 | G_DEBUG_HERE (); | |
893 | 2 | elan_dev_reset_state (self); | |
894 | |||
895 | 2 | FpiSsm *ssm = | |
896 | 2 | fpi_ssm_new (FP_DEVICE (dev), activate_run_state, | |
897 | ACTIVATE_NUM_STATES); | ||
898 | |||
899 | 2 | fpi_ssm_start (ssm, activate_complete); | |
900 | 2 | } | |
901 | |||
902 | static void | ||
903 | 2 | dev_init (FpImageDevice *dev) | |
904 | { | ||
905 | 2 | GError *error = NULL; | |
906 | 2 | FpiDeviceElan *self; | |
907 | |||
908 | 2 | G_DEBUG_HERE (); | |
909 | |||
910 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
2 | if (!g_usb_device_claim_interface (fpi_device_get_usb_device (FP_DEVICE (dev)), 0, 0, &error)) |
911 | { | ||
912 | ✗ | fpi_image_device_open_complete (dev, error); | |
913 | ✗ | return; | |
914 | } | ||
915 | |||
916 | 2 | self = FPI_DEVICE_ELAN (dev); | |
917 | |||
918 | /* common params */ | ||
919 | 2 | self->dev_type = fpi_device_get_driver_data (FP_DEVICE (dev)); | |
920 | 2 | self->background = NULL; | |
921 | 2 | self->process_frame = elan_process_frame_thirds; | |
922 | |||
923 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | switch (self->dev_type) |
924 | { | ||
925 | ✗ | case ELAN_0907: | |
926 | ✗ | self->process_frame = elan_process_frame_linear; | |
927 | ✗ | break; | |
928 | } | ||
929 | |||
930 | 2 | fpi_image_device_open_complete (dev, NULL); | |
931 | } | ||
932 | |||
933 | static void | ||
934 | 2 | dev_deinit (FpImageDevice *dev) | |
935 | { | ||
936 | 2 | GError *error = NULL; | |
937 | 2 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
938 | |||
939 | 2 | G_DEBUG_HERE (); | |
940 | |||
941 | 2 | elan_dev_reset_state (self); | |
942 | 2 | g_free (self->background); | |
943 | 2 | g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (dev)), | |
944 | 0, 0, &error); | ||
945 | 2 | fpi_image_device_close_complete (dev, error); | |
946 | 2 | } | |
947 | |||
948 | static void | ||
949 | 2 | dev_activate (FpImageDevice *dev) | |
950 | { | ||
951 | 2 | G_DEBUG_HERE (); | |
952 | 2 | elan_activate (dev); | |
953 | 2 | } | |
954 | |||
955 | static void | ||
956 | 16 | dev_change_state (FpImageDevice *dev, FpiImageDeviceState state) | |
957 | { | ||
958 | 16 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
959 | |||
960 | 16 | G_DEBUG_HERE (); | |
961 | |||
962 | /* Note: We always calibrate even if that may not be needed always. | ||
963 | * Doing this for each capture appears to make it at least less likely for | ||
964 | * devices to end up in a bad state. | ||
965 | */ | ||
966 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14 times.
|
16 | if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) |
967 | 2 | elan_calibrate (self); | |
968 | 16 | } | |
969 | |||
970 | static void | ||
971 | 2 | dev_deactivate (FpImageDevice *dev) | |
972 | { | ||
973 | 2 | FpiDeviceElan *self = FPI_DEVICE_ELAN (dev); | |
974 | |||
975 | 2 | G_DEBUG_HERE (); | |
976 | |||
977 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (!self->active) |
978 | /* The device is inactive already, complete the operation immediately. */ | ||
979 | 2 | fpi_image_device_deactivate_complete (dev, NULL); | |
980 | else | ||
981 | /* The device is not yet inactive, flag that we are deactivating (and | ||
982 | * need to signal back deactivation). | ||
983 | * Note that any running capture will be cancelled already if needed. */ | ||
984 | ✗ | self->deactivating = TRUE; | |
985 | 2 | } | |
986 | |||
987 | static void | ||
988 | 2 | fpi_device_elan_init (FpiDeviceElan *self) | |
989 | { | ||
990 | 2 | } | |
991 | static void | ||
992 | 120 | fpi_device_elan_class_init (FpiDeviceElanClass *klass) | |
993 | { | ||
994 | 120 | FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); | |
995 | 120 | FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass); | |
996 | |||
997 | 120 | dev_class->id = "elan"; | |
998 | 120 | dev_class->full_name = "ElanTech Fingerprint Sensor"; | |
999 | 120 | dev_class->type = FP_DEVICE_TYPE_USB; | |
1000 | 120 | dev_class->id_table = elan_id_table; | |
1001 | 120 | dev_class->scan_type = FP_SCAN_TYPE_SWIPE; | |
1002 | |||
1003 | 120 | img_class->img_open = dev_init; | |
1004 | 120 | img_class->img_close = dev_deinit; | |
1005 | 120 | img_class->activate = dev_activate; | |
1006 | 120 | img_class->deactivate = dev_deactivate; | |
1007 | 120 | img_class->change_state = dev_change_state; | |
1008 | |||
1009 | 120 | img_class->bz3_threshold = 24; | |
1010 | } | ||
1011 |