Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Elan SPI driver for libfprint | ||
3 | * | ||
4 | * Copyright (C) 2021 Matthew Mirvish <matthew@mm12.xyz> | ||
5 | * | ||
6 | * This library is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU Lesser General Public | ||
8 | * License as published by the Free Software Foundation; either | ||
9 | * version 2.1 of the License, or (at your option) any later version. | ||
10 | * | ||
11 | * This library is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * Lesser General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU Lesser General Public | ||
17 | * License along with this library; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
19 | */ | ||
20 | |||
21 | #define FP_COMPONENT "elanspi" | ||
22 | |||
23 | #include "drivers_api.h" | ||
24 | #include "elanspi.h" | ||
25 | |||
26 | #include <linux/hidraw.h> | ||
27 | #include <sys/ioctl.h> | ||
28 | #include <sys/fcntl.h> | ||
29 | #include <sys/types.h> | ||
30 | #include <sys/stat.h> | ||
31 | #include <linux/types.h> | ||
32 | #include <errno.h> | ||
33 | |||
34 | struct _FpiDeviceElanSpi | ||
35 | { | ||
36 | FpImageDevice parent; | ||
37 | |||
38 | /* sensor info */ | ||
39 | guint8 sensor_width, sensor_height, sensor_ic_version, sensor_id; | ||
40 | gboolean sensor_otp; | ||
41 | guint8 sensor_vcm_mode; | ||
42 | |||
43 | /* processed frame info */ | ||
44 | guint8 frame_width, frame_height; | ||
45 | |||
46 | /* init info */ | ||
47 | guint8 sensor_raw_version, sensor_reg_17; | ||
48 | guint8 sensor_reg_vref1, sensor_reg_28, sensor_reg_27, sensor_reg_dac2; | ||
49 | |||
50 | /* calibration info */ | ||
51 | union | ||
52 | { | ||
53 | struct | ||
54 | { | ||
55 | guint8 dac_value; | ||
56 | guint8 line_ptr; | ||
57 | guint8 dacfine_retry; | ||
58 | gint64 otp_timeout; | ||
59 | } old_data; | ||
60 | struct | ||
61 | { | ||
62 | guint16 gdac_value; | ||
63 | guint16 gdac_step; | ||
64 | guint16 best_gdac; | ||
65 | guint16 best_meandiff; | ||
66 | } hv_data; | ||
67 | }; | ||
68 | |||
69 | /* generic temp info for async reading */ | ||
70 | guint8 sensor_status; | ||
71 | gint64 capture_timeout; | ||
72 | |||
73 | /* background / calibration parameters */ | ||
74 | guint16 *bg_image; | ||
75 | guint16 *last_image; | ||
76 | guint16 *prev_frame_image; | ||
77 | |||
78 | gint fp_empty_counter; | ||
79 | GSList *fp_frame_list; | ||
80 | |||
81 | /* wait ctx */ | ||
82 | gint finger_wait_debounce; | ||
83 | |||
84 | gboolean deactivating, capturing; | ||
85 | |||
86 | /* active SPI status info */ | ||
87 | int spi_fd; | ||
88 | }; | ||
89 | |||
90 | G_DECLARE_FINAL_TYPE (FpiDeviceElanSpi, fpi_device_elanspi, FPI, DEVICE_ELANSPI, FpImageDevice); | ||
91 |
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 (FpiDeviceElanSpi, fpi_device_elanspi, FP_TYPE_IMAGE_DEVICE); |
92 | |||
93 | static void | ||
94 | 1 | elanspi_do_hwreset (FpiDeviceElanSpi *self, GError **err) | |
95 | { | ||
96 | /* Skip in emulation mode, since we don't mock hid devices */ | ||
97 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0) |
98 | 1 | return; | |
99 | |||
100 | /* | ||
101 | * TODO: Make this also work with the non-HID cases | ||
102 | */ | ||
103 | |||
104 | ✗ | int fd = open ((char *) fpi_device_get_udev_data (FP_DEVICE (self), FPI_DEVICE_UDEV_SUBTYPE_HIDRAW), O_RDWR); | |
105 | |||
106 | ✗ | if (fd < 0) | |
107 | { | ||
108 | ✗ | g_set_error (err, G_IO_ERROR, g_io_error_from_errno (errno), "unable to open hid"); | |
109 | ✗ | return; | |
110 | } | ||
111 | |||
112 | ✗ | guint8 buf[5] = { | |
113 | 0xe, 0, 0, 0, 0 | ||
114 | }; | ||
115 | |||
116 | ✗ | if (ioctl (fd, HIDIOCSFEATURE (5), &buf) != 5) | |
117 | { | ||
118 | ✗ | g_set_error (err, G_IO_ERROR, g_io_error_from_errno (errno), "unable to reset via hid"); | |
119 | ✗ | goto out; | |
120 | } | ||
121 | |||
122 | ✗ | out: | |
123 | ✗ | close (fd); | |
124 | } | ||
125 | |||
126 | /* | ||
127 | * Three main processes involved in driving these sensors: | ||
128 | * - initialization (device type detection) | ||
129 | * - calibration | ||
130 | * - image capture (single) | ||
131 | * - image capture (stitched) | ||
132 | */ | ||
133 | |||
134 | enum elanspi_init_state { | ||
135 | ELANSPI_INIT_READ_STATUS1, | ||
136 | ELANSPI_INIT_HWSWRESET, /* fused b.c. hw reset is currently sync */ | ||
137 | ELANSPI_INIT_SWRESETDELAY1, | ||
138 | ELANSPI_INIT_READ_HEIGHT, | ||
139 | ELANSPI_INIT_READ_WIDTH, | ||
140 | ELANSPI_INIT_READ_REG17, /* both of these states finish setting up sensor settings */ | ||
141 | ELANSPI_INIT_READ_VERSION, /* can jump straight to calibrate */ | ||
142 | ELANSPI_INIT_SWRESET2, | ||
143 | ELANSPI_INIT_SWRESETDELAY2, | ||
144 | ELANSPI_INIT_OTP_READ_VREF1, | ||
145 | ELANSPI_INIT_OTP_WRITE_VREF1, | ||
146 | ELANSPI_INIT_OTP_WRITE_0x28, | ||
147 | ELANSPI_INIT_OTP_LOOP_READ_0x28, /* may loop */ | ||
148 | ELANSPI_INIT_OTP_LOOP_READ_0x27, | ||
149 | ELANSPI_INIT_OTP_LOOP_UPDATEDAC_READ_DAC2, | ||
150 | ELANSPI_INIT_OTP_LOOP_UPDATEDAC_WRITE_DAC2, | ||
151 | ELANSPI_INIT_OTP_LOOP_UPDATEDAC_WRITE_10, | ||
152 | /* exit loop */ | ||
153 | ELANSPI_INIT_OTP_WRITE_0xb, | ||
154 | ELANSPI_INIT_OTP_WRITE_0xc, | ||
155 | /* do calibration (mutexc) */ | ||
156 | ELANSPI_INIT_CALIBRATE, | ||
157 | ELANSPI_INIT_BG_CAPTURE, | ||
158 | ELANSPI_INIT_BG_SAVE, | ||
159 | ELANSPI_INIT_NSTATES | ||
160 | }; | ||
161 | |||
162 | enum elanspi_calibrate_old_state { | ||
163 | ELANSPI_CALIBOLD_UNPROTECT, | ||
164 | ELANSPI_CALIBOLD_WRITE_STARTCALIB, | ||
165 | ELANSPI_CALIBOLD_STARTCALIBDELAY, | ||
166 | ELANSPI_CALIBOLD_SEND_REGTABLE, | ||
167 | /* calibrate dac base value */ | ||
168 | ELANSPI_CALIBOLD_DACBASE_CAPTURE, | ||
169 | ELANSPI_CALIBOLD_DACBASE_WRITE_DAC1, | ||
170 | /* check for finger */ | ||
171 | ELANSPI_CALIBOLD_CHECKFIN_CAPTURE, | ||
172 | /* increase gain */ | ||
173 | ELANSPI_CALIBOLD_WRITE_GAIN, | ||
174 | /* calibrate dac stage2 */ | ||
175 | ELANSPI_CALIBOLD_DACFINE_CAPTURE, | ||
176 | ELANSPI_CALIBOLD_DACFINE_WRITE_DAC1, | ||
177 | ELANSPI_CALIBOLD_DACFINE_LOOP, | ||
178 | /* exit ok (cleanup by protecting) */ | ||
179 | ELANSPI_CALIBOLD_PROTECT, | ||
180 | ELANSPI_CALIBOLD_NSTATES | ||
181 | }; | ||
182 | |||
183 | enum elanspi_capture_old_state { | ||
184 | ELANSPI_CAPTOLD_WRITE_CAPTURE, | ||
185 | ELANSPI_CAPTOLD_CHECK_LINEREADY, | ||
186 | ELANSPI_CAPTOLD_RECV_LINE, | ||
187 | |||
188 | ELANSPI_CAPTOLD_NSTATES | ||
189 | }; | ||
190 | |||
191 | enum elanspi_calibrate_hv_state { | ||
192 | ELANSPI_CALIBHV_SELECT_PAGE0_0, | ||
193 | ELANSPI_CALIBHV_WRITE_STARTCALIB, | ||
194 | ELANSPI_CALIBHV_UNPROTECT, | ||
195 | ELANSPI_CALIBHV_SEND_REGTABLE0, | ||
196 | ELANSPI_CALIBHV_SELECT_PAGE1, | ||
197 | ELANSPI_CALIBHV_SEND_REGTABLE1, | ||
198 | ELANSPI_CALIBHV_SELECT_PAGE0_1, | ||
199 | ELANSPI_CALIBHV_WRITE_GDAC_H, | ||
200 | ELANSPI_CALIBHV_WRITE_GDAC_L, | ||
201 | ELANSPI_CALIBHV_CAPTURE, | ||
202 | ELANSPI_CALIBHV_PROCESS, | ||
203 | ELANSPI_CALIBHV_WRITE_BEST_GDAC_H, | ||
204 | ELANSPI_CALIBHV_WRITE_BEST_GDAC_L, | ||
205 | /* cleanup by protecting */ | ||
206 | ELANSPI_CALIBHV_PROTECT, | ||
207 | ELANSPI_CALIBHV_NSTATES | ||
208 | }; | ||
209 | |||
210 | enum elanspi_capture_hv_state { | ||
211 | ELANSPI_CAPTHV_WRITE_CAPTURE, | ||
212 | ELANSPI_CAPTHV_CHECK_READY, | ||
213 | ELANSPI_CAPTHV_RECV_IMAGE, | ||
214 | ELANSPI_CAPTHV_NSTATES | ||
215 | }; | ||
216 | |||
217 | enum elanspi_write_regtable_state { | ||
218 | ELANSPI_WRTABLE_WRITE, | ||
219 | ELANSPI_WRTABLE_ITERATE, | ||
220 | ELANSPI_WRTABLE_NSTATES | ||
221 | }; | ||
222 | |||
223 | enum elanspi_fp_capture_state { | ||
224 | ELANSPI_FPCAPT_INIT, | ||
225 | /* wait for finger */ | ||
226 | ELANSPI_FPCAPT_WAITDOWN_CAPTURE, | ||
227 | ELANSPI_FPCAPT_WAITDOWN_PROCESS, | ||
228 | /* capture full image */ | ||
229 | ELANSPI_FPCAPT_FP_CAPTURE, | ||
230 | ELANSPI_FPCAPT_FP_PROCESS, | ||
231 | /* wait for no finger */ | ||
232 | ELANSPI_FPCAPT_WAITUP_CAPTURE, | ||
233 | ELANSPI_FPCAPT_WAITUP_PROCESS, | ||
234 | ELANSPI_FPCAPT_NSTATES | ||
235 | }; | ||
236 | |||
237 | /* helpers */ | ||
238 | |||
239 | static FpiSpiTransfer * | ||
240 | 2 | elanspi_do_swreset (FpiDeviceElanSpi *self) | |
241 | { | ||
242 | 2 | FpiSpiTransfer * xfer = fpi_spi_transfer_new (FP_DEVICE (self), self->spi_fd); | |
243 | |||
244 | 2 | fpi_spi_transfer_write (xfer, 1); | |
245 | 2 | xfer->buffer_wr[0] = 0x31; | |
246 | 2 | return xfer; | |
247 | } | ||
248 | static FpiSpiTransfer * | ||
249 | 1 | elanspi_do_startcalib (FpiDeviceElanSpi *self) | |
250 | { | ||
251 | 1 | FpiSpiTransfer * xfer = fpi_spi_transfer_new (FP_DEVICE (self), self->spi_fd); | |
252 | |||
253 | 1 | fpi_spi_transfer_write (xfer, 1); | |
254 | 1 | xfer->buffer_wr[0] = 0x4; | |
255 | 1 | return xfer; | |
256 | } | ||
257 | static FpiSpiTransfer * | ||
258 | 77 | elanspi_do_capture (FpiDeviceElanSpi *self) | |
259 | { | ||
260 | 77 | FpiSpiTransfer * xfer = fpi_spi_transfer_new (FP_DEVICE (self), self->spi_fd); | |
261 | |||
262 | 77 | fpi_spi_transfer_write (xfer, 1); | |
263 | 77 | xfer->buffer_wr[0] = 0x1; | |
264 | 77 | return xfer; | |
265 | } | ||
266 | static FpiSpiTransfer * | ||
267 | ✗ | elanspi_do_selectpage (FpiDeviceElanSpi *self, guint8 page) | |
268 | { | ||
269 | ✗ | FpiSpiTransfer * xfer = fpi_spi_transfer_new (FP_DEVICE (self), self->spi_fd); | |
270 | |||
271 | ✗ | fpi_spi_transfer_write (xfer, 2); | |
272 | ✗ | xfer->buffer_wr[0] = 0x7; | |
273 | ✗ | xfer->buffer_wr[1] = page; | |
274 | ✗ | return xfer; | |
275 | } | ||
276 | |||
277 | static FpiSpiTransfer * | ||
278 | 7995 | elanspi_single_read_cmd (FpiDeviceElanSpi *self, guint8 cmd_id, guint8 *data_out) | |
279 | { | ||
280 | 7995 | FpiSpiTransfer * xfer = fpi_spi_transfer_new (FP_DEVICE (self), self->spi_fd); | |
281 | |||
282 | 7995 | fpi_spi_transfer_write (xfer, 2); | |
283 | 7995 | xfer->buffer_wr[0] = cmd_id; | |
284 | 7995 | xfer->buffer_wr[1] = 0xff; | |
285 | 7995 | fpi_spi_transfer_read_full (xfer, data_out, 1, NULL); | |
286 | 7995 | return xfer; | |
287 | } | ||
288 | |||
289 | static FpiSpiTransfer * | ||
290 | 7992 | elanspi_read_status (FpiDeviceElanSpi *self, guint8 *data_out) | |
291 | { | ||
292 | 7992 | return elanspi_single_read_cmd (self, 0x3, data_out); | |
293 | } | ||
294 | static FpiSpiTransfer * | ||
295 | 1 | elanspi_read_width (FpiDeviceElanSpi *self, guint8 *data_out) | |
296 | { | ||
297 | 1 | return elanspi_single_read_cmd (self, 0x9, data_out); | |
298 | } | ||
299 | static FpiSpiTransfer * | ||
300 | 1 | elanspi_read_height (FpiDeviceElanSpi *self, guint8 *data_out) | |
301 | { | ||
302 | 1 | return elanspi_single_read_cmd (self, 0x8, data_out); | |
303 | } | ||
304 | static FpiSpiTransfer * | ||
305 | 1 | elanspi_read_version (FpiDeviceElanSpi *self, guint8 *data_out) | |
306 | { | ||
307 | 1 | return elanspi_single_read_cmd (self, 0xa, data_out); | |
308 | } | ||
309 | |||
310 | static FpiSpiTransfer * | ||
311 | 4 | elanspi_read_register (FpiDeviceElanSpi *self, guint8 reg_id, guint8 *data_out) | |
312 | { | ||
313 | 4 | FpiSpiTransfer * xfer = fpi_spi_transfer_new (FP_DEVICE (self), self->spi_fd); | |
314 | |||
315 | 4 | fpi_spi_transfer_write (xfer, 1); | |
316 | 4 | xfer->buffer_wr[0] = reg_id | 0x40; | |
317 | 4 | fpi_spi_transfer_read_full (xfer, data_out, 1, NULL); | |
318 | 4 | return xfer; | |
319 | } | ||
320 | |||
321 | static FpiSpiTransfer * | ||
322 | 33 | elanspi_write_register (FpiDeviceElanSpi *self, guint8 reg_id, guint8 data_in) | |
323 | { | ||
324 | 33 | FpiSpiTransfer * xfer = fpi_spi_transfer_new (FP_DEVICE (self), self->spi_fd); | |
325 | |||
326 | 33 | fpi_spi_transfer_write (xfer, 2); | |
327 | 33 | xfer->buffer_wr[0] = reg_id | 0x80; | |
328 | 33 | xfer->buffer_wr[1] = data_in; | |
329 | 33 | return xfer; | |
330 | } | ||
331 | |||
332 | static void | ||
333 | 1 | elanspi_determine_sensor (FpiDeviceElanSpi *self, GError **err) | |
334 | { | ||
335 | 1 | guint8 raw_height = self->sensor_height; | |
336 | 1 | guint8 raw_width = self->sensor_width; | |
337 | |||
338 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (((raw_height == 0xa1) && (raw_width == 0xa1)) || |
339 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | ((raw_height == 0xd1) && (raw_width == 0x51)) || |
340 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | ((raw_height == 0xc1) && (raw_width == 0x39))) |
341 | { | ||
342 | ✗ | self->sensor_ic_version = 0; /* Version 0 */ | |
343 | ✗ | self->sensor_width = raw_width - 1; | |
344 | ✗ | self->sensor_height = raw_height - 1; | |
345 | } | ||
346 | else | ||
347 | { | ||
348 | /* If the sensor is exactly 96x96 (0x60 x 0x60), the version is the high bit of register 17 */ | ||
349 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (raw_width == 0x60 && raw_height == 0x60) |
350 | { | ||
351 | 1 | self->sensor_ic_version = (self->sensor_reg_17 & 0x80) ? 1 : 0; | |
352 | } | ||
353 | else | ||
354 | { | ||
355 | ✗ | if (((raw_height != 0xa0) || (raw_width != 0x50)) && | |
356 | ✗ | ((raw_height != 0x90) || (raw_width != 0x40)) && | |
357 | ✗ | ((raw_height != 0x78) || (raw_width != 0x78))) | |
358 | { | ||
359 | ✗ | if (((raw_height != 0x40) || (raw_width != 0x58)) && | |
360 | ✗ | ((raw_height != 0x50) || (raw_width != 0x50))) | |
361 | { | ||
362 | /* Old sensor hack?? */ | ||
363 | ✗ | self->sensor_width = 0x78; | |
364 | ✗ | self->sensor_height = 0x78; | |
365 | ✗ | self->sensor_ic_version = 0; | |
366 | } | ||
367 | else | ||
368 | { | ||
369 | /* Otherwise, read the version 'normally' */ | ||
370 | ✗ | self->sensor_ic_version = (self->sensor_raw_version & 0x70) >> 4; | |
371 | } | ||
372 | } | ||
373 | else | ||
374 | { | ||
375 | ✗ | self->sensor_ic_version = 1; | |
376 | } | ||
377 | } | ||
378 | } | ||
379 | |||
380 | 1 | fp_dbg ("<init/detect> after hardcoded lookup; %dx%d, version %d", self->sensor_width, self->sensor_height, self->sensor_ic_version); | |
381 | |||
382 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | for (const struct elanspi_sensor_entry *entry = elanspi_sensor_table; entry->name; entry += 1) |
383 | { | ||
384 |
5/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
7 | if (entry->ic_version == self->sensor_ic_version && entry->width == self->sensor_width && entry->height == self->sensor_height) |
385 | { | ||
386 | 1 | self->sensor_id = entry->sensor_id; | |
387 | 1 | self->sensor_otp = entry->is_otp_model; | |
388 | |||
389 | 1 | fp_dbg ("<init/detect> found sensor ID %d => [%s] (%d x %d)", self->sensor_id, entry->name, self->sensor_width, self->sensor_height); | |
390 | 1 | break; | |
391 | } | ||
392 | } | ||
393 | |||
394 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (self->sensor_id == 0xff) |
395 | { | ||
396 | ✗ | *err = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED, "unknown sensor (%dx%d, v%d)", self->sensor_width, self->sensor_height, self->sensor_ic_version); | |
397 | ✗ | return; | |
398 | } | ||
399 | |||
400 | /* setup frame size */ | ||
401 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (fpi_device_get_driver_data (FP_DEVICE (self)) & ELANSPI_HV_FLIPPED) |
402 | { | ||
403 | ✗ | self->frame_width = self->sensor_height; | |
404 | ✗ | self->frame_height = self->sensor_width > ELANSPI_MAX_FRAME_HEIGHT ? ELANSPI_MAX_FRAME_HEIGHT : self->sensor_width; | |
405 | } | ||
406 | else | ||
407 | { | ||
408 | 1 | self->frame_width = self->sensor_width; | |
409 | 1 | self->frame_height = self->sensor_height > ELANSPI_MAX_FRAME_HEIGHT ? ELANSPI_MAX_FRAME_HEIGHT : self->sensor_height; | |
410 | } | ||
411 | } | ||
412 | |||
413 | static void | ||
414 | 7392 | elanspi_capture_old_line_handler (FpiSpiTransfer *transfer, FpDevice *dev, gpointer unused_data, GError *error) | |
415 | { | ||
416 | 7392 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
417 | |||
418 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7392 times.
|
7392 | if (error) |
419 | { | ||
420 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
421 | ✗ | return; | |
422 | } | ||
423 | |||
424 | /* copy buffer from line into last_image */ | ||
425 |
2/2✓ Branch 0 taken 709632 times.
✓ Branch 1 taken 7392 times.
|
717024 | for (int col = 0; col < self->sensor_width; col += 1) |
426 | { | ||
427 | 709632 | guint8 low = transfer->buffer_rd[col * 2 + 1]; | |
428 | 709632 | guint8 high = transfer->buffer_rd[col * 2]; | |
429 | |||
430 | 709632 | self->last_image[self->sensor_width * self->old_data.line_ptr + col] = low + high * 0x100; | |
431 | } | ||
432 | |||
433 | /* increment line ptr */ | ||
434 | 7392 | self->old_data.line_ptr += 1; | |
435 | /* if there is still data, continue from check lineready */ | ||
436 |
2/2✓ Branch 0 taken 7315 times.
✓ Branch 1 taken 77 times.
|
7392 | if (self->old_data.line_ptr < self->sensor_height) |
437 | { | ||
438 | 7315 | fpi_ssm_jump_to_state (transfer->ssm, ELANSPI_CAPTOLD_CHECK_LINEREADY); | |
439 | } | ||
440 | else | ||
441 | { | ||
442 | /* check for termination */ | ||
443 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 77 times.
|
77 | if (fpi_device_get_current_action (dev) == FPI_DEVICE_ACTION_NONE) |
444 | { | ||
445 | ✗ | fpi_ssm_mark_completed (transfer->ssm); | |
446 | ✗ | return; | |
447 | } | ||
448 | /* check for cancellation */ | ||
449 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 77 times.
|
77 | if (fpi_device_action_is_cancelled (dev)) |
450 | { | ||
451 | ✗ | g_cancellable_set_error_if_cancelled (fpi_device_get_cancellable (dev), &error); | |
452 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
453 | ✗ | return; | |
454 | } | ||
455 | /* otherwise finish succesfully */ | ||
456 | 77 | fpi_ssm_mark_completed (transfer->ssm); | |
457 | } | ||
458 | } | ||
459 | |||
460 | static void | ||
461 | 16059 | elanspi_capture_old_handler (FpiSsm *ssm, FpDevice *dev) | |
462 | { | ||
463 | 16059 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
464 | 16059 | FpiSpiTransfer *xfer = NULL; | |
465 | |||
466 |
3/4✓ Branch 1 taken 77 times.
✓ Branch 2 taken 7991 times.
✓ Branch 3 taken 7991 times.
✗ Branch 4 not taken.
|
16059 | switch (fpi_ssm_get_cur_state (ssm)) |
467 | { | ||
468 | 77 | case ELANSPI_CAPTOLD_WRITE_CAPTURE: | |
469 | /* reset capture state */ | ||
470 | 77 | self->old_data.line_ptr = 0; | |
471 | 77 | self->capture_timeout = g_get_monotonic_time () + ELANSPI_OLD_CAPTURE_TIMEOUT_USEC; | |
472 | 77 | xfer = elanspi_do_capture (self); | |
473 | 77 | xfer->ssm = ssm; | |
474 | 77 | fpi_spi_transfer_submit (xfer, NULL, fpi_ssm_spi_transfer_cb, NULL); | |
475 | 77 | return; | |
476 | |||
477 | 7991 | case ELANSPI_CAPTOLD_CHECK_LINEREADY: | |
478 | 7991 | xfer = elanspi_read_status (self, &self->sensor_status); | |
479 | 7991 | xfer->ssm = ssm; | |
480 | 7991 | fpi_spi_transfer_submit (xfer, NULL, fpi_ssm_spi_transfer_cb, NULL); | |
481 | 7991 | return; | |
482 | |||
483 | 7991 | case ELANSPI_CAPTOLD_RECV_LINE: | |
484 | /* is the sensor ready? */ | ||
485 |
2/2✓ Branch 0 taken 599 times.
✓ Branch 1 taken 7392 times.
|
7991 | if (!(self->sensor_status & 4)) |
486 | { | ||
487 | /* has the timeout expired? -- disabled in testing since valgrind is very slow */ | ||
488 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 599 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
599 | if (g_get_monotonic_time () > self->capture_timeout && g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") != 0) |
489 | { | ||
490 | /* end with a timeout */ | ||
491 | ✗ | fpi_ssm_mark_failed (ssm, g_error_new (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "timed out waiting for new line")); | |
492 | ✗ | return; | |
493 | } | ||
494 | /* check again */ | ||
495 | 599 | fpi_ssm_jump_to_state (ssm, ELANSPI_CAPTOLD_CHECK_LINEREADY); | |
496 | 599 | return; | |
497 | } | ||
498 | /* otherwise, perform a read */ | ||
499 | 7392 | xfer = fpi_spi_transfer_new (dev, self->spi_fd); | |
500 | 7392 | xfer->ssm = ssm; | |
501 | 7392 | fpi_spi_transfer_write (xfer, 2); | |
502 | 7392 | xfer->buffer_wr[0] = 0x10; /* receieve line */ | |
503 | 7392 | fpi_spi_transfer_read (xfer, self->sensor_width * 2); | |
504 | 7392 | fpi_spi_transfer_submit (xfer, NULL, elanspi_capture_old_line_handler, NULL); | |
505 | 7392 | return; | |
506 | } | ||
507 | } | ||
508 | |||
509 | static void | ||
510 | 50 | elanspi_send_regtable_handler (FpiSsm *ssm, FpDevice *dev) | |
511 | { | ||
512 | 50 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
513 | 50 | FpiSpiTransfer *xfer = NULL; | |
514 | 50 | const struct elanspi_reg_entry *entry = fpi_ssm_get_data (ssm); | |
515 | |||
516 |
2/3✓ Branch 1 taken 25 times.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
|
50 | switch (fpi_ssm_get_cur_state (ssm)) |
517 | { | ||
518 | 25 | case ELANSPI_WRTABLE_WRITE: | |
519 | 25 | xfer = elanspi_write_register (self, entry->addr, entry->value); | |
520 | 25 | xfer->ssm = ssm; | |
521 | 25 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
522 | 25 | return; | |
523 | |||
524 | 25 | case ELANSPI_WRTABLE_ITERATE: | |
525 | 25 | entry += 1; | |
526 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1 times.
|
25 | if (entry->addr != 0xff) |
527 | { | ||
528 | 24 | fpi_ssm_set_data (ssm, (gpointer) entry, NULL); | |
529 | 24 | fpi_ssm_jump_to_state (ssm, ELANSPI_WRTABLE_WRITE); | |
530 | 24 | return; | |
531 | } | ||
532 | 1 | fpi_ssm_mark_completed (ssm); | |
533 | 1 | return; | |
534 | } | ||
535 | } | ||
536 | |||
537 | static FpiSsm * | ||
538 | 1 | elanspi_write_regtable (FpiDeviceElanSpi *self, const struct elanspi_regtable * table) | |
539 | { | ||
540 | /* find regtable pointer */ | ||
541 | 1 | const struct elanspi_reg_entry * starting_entry = table->other; | |
542 | |||
543 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | for (int i = 0; table->entries[i].table; i += 1) |
544 | { | ||
545 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (table->entries[i].sid == self->sensor_id) |
546 | { | ||
547 | starting_entry = table->entries[i].table; | ||
548 | break; | ||
549 | } | ||
550 | } | ||
551 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (starting_entry == NULL) |
552 | { | ||
553 | ✗ | fp_err ("<regtable> unknown regtable for sensor %d", self->sensor_id); | |
554 | ✗ | return NULL; | |
555 | } | ||
556 | |||
557 | 1 | FpiSsm * ssm = fpi_ssm_new (FP_DEVICE (self), elanspi_send_regtable_handler, ELANSPI_WRTABLE_NSTATES); | |
558 | |||
559 | 1 | fpi_ssm_set_data (ssm, (gpointer) starting_entry, NULL); | |
560 | 1 | return ssm; | |
561 | } | ||
562 | |||
563 | static int | ||
564 | 3 | elanspi_mean_image (FpiDeviceElanSpi *self, const guint16 *img) | |
565 | { | ||
566 | 3 | int total = 0; | |
567 | |||
568 |
6/10✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 9216 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 9216 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 9216 times.
✓ Branch 9 taken 1 times.
|
27651 | for (int i = 0; i < self->sensor_width * self->sensor_height; i += 1) |
569 | 27648 | total += img[i]; | |
570 | 3 | return total / (self->sensor_width * self->sensor_height); | |
571 | } | ||
572 | |||
573 | static void | ||
574 | 11 | elanspi_calibrate_old_handler (FpiSsm *ssm, FpDevice *dev) | |
575 | { | ||
576 | 11 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
577 | 11 | FpiSpiTransfer *xfer = NULL; | |
578 | 11 | GError *err = NULL; | |
579 | 11 | FpiSsm *chld = NULL; | |
580 | 11 | int mean_value = 0; | |
581 | |||
582 |
9/11✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
|
11 | switch (fpi_ssm_get_cur_state (ssm)) |
583 | { | ||
584 | 1 | case ELANSPI_CALIBOLD_UNPROTECT: | |
585 | 1 | xfer = elanspi_write_register (self, 0x00, 0x5a); | |
586 | 1 | xfer->ssm = ssm; | |
587 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
588 | 1 | return; | |
589 | |||
590 | 1 | case ELANSPI_CALIBOLD_WRITE_STARTCALIB: | |
591 | 1 | xfer = elanspi_do_startcalib (self); | |
592 | 1 | xfer->ssm = ssm; | |
593 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
594 | 1 | return; | |
595 | |||
596 | 1 | case ELANSPI_CALIBOLD_STARTCALIBDELAY: | |
597 | 1 | fpi_ssm_next_state_delayed (ssm, 1); | |
598 | 1 | return; | |
599 | |||
600 | 1 | case ELANSPI_CALIBOLD_SEND_REGTABLE: | |
601 | 1 | chld = elanspi_write_regtable (self, &elanspi_calibration_table_old); | |
602 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (chld == NULL) |
603 | { | ||
604 | ✗ | err = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED, "unknown calibration table for sensor"); | |
605 | ✗ | fpi_ssm_mark_failed (ssm, err); | |
606 | ✗ | return; | |
607 | } | ||
608 | 1 | fpi_ssm_start_subsm (ssm, chld); | |
609 | 1 | return; | |
610 | |||
611 | 3 | case ELANSPI_CALIBOLD_DACBASE_CAPTURE: | |
612 | case ELANSPI_CALIBOLD_CHECKFIN_CAPTURE: | ||
613 | case ELANSPI_CALIBOLD_DACFINE_CAPTURE: | ||
614 | 3 | chld = fpi_ssm_new (dev, elanspi_capture_old_handler, ELANSPI_CAPTOLD_NSTATES); | |
615 | 3 | fpi_ssm_silence_debug (chld); | |
616 | 3 | fpi_ssm_start_subsm (ssm, chld); | |
617 | 3 | return; | |
618 | |||
619 | 1 | case ELANSPI_CALIBOLD_DACBASE_WRITE_DAC1: | |
620 | /* compute dac */ | ||
621 | 1 | self->old_data.dac_value = ((elanspi_mean_image (self, self->last_image) & 0xffff) + 0x80) >> 8; | |
622 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (0x3f < self->old_data.dac_value) |
623 | ✗ | self->old_data.dac_value = 0x3f; | |
624 | 1 | fp_dbg ("<calibold> dac init is 0x%02x", self->old_data.dac_value); | |
625 | /* write it */ | ||
626 | 1 | xfer = elanspi_write_register (self, 0x6, self->old_data.dac_value - 0x40); | |
627 | 1 | xfer->ssm = ssm; | |
628 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
629 | 1 | return; | |
630 | |||
631 | 1 | case ELANSPI_CALIBOLD_WRITE_GAIN: | |
632 | /* check if finger was present */ | ||
633 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
2 | if (elanspi_mean_image (self, self->last_image) >= ELANSPI_MAX_OLD_STAGE1_CALIBRATION_MEAN) |
634 | { | ||
635 | ✗ | err = fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER, "finger on sensor during calibration"); | |
636 | ✗ | fpi_ssm_mark_failed (ssm, err); | |
637 | ✗ | return; | |
638 | } | ||
639 | /* if ok, increase gain */ | ||
640 | 1 | xfer = elanspi_write_register (self, 0x5, 0x6f); | |
641 | 1 | xfer->ssm = ssm; | |
642 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
643 | /* initialize retry counter */ | ||
644 | 1 | self->old_data.dacfine_retry = 0; | |
645 | 1 | return; | |
646 | |||
647 | 1 | case ELANSPI_CALIBOLD_DACFINE_WRITE_DAC1: | |
648 | 1 | mean_value = elanspi_mean_image (self, self->last_image); | |
649 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (mean_value >= ELANSPI_MIN_OLD_STAGE2_CALBIRATION_MEAN && mean_value <= ELANSPI_MAX_OLD_STAGE2_CALBIRATION_MEAN) |
650 | { | ||
651 | /* finished calibration, goto bg */ | ||
652 | 1 | fpi_ssm_jump_to_state (ssm, ELANSPI_CALIBOLD_PROTECT); | |
653 | 1 | return; | |
654 | } | ||
655 | |||
656 | ✗ | if (mean_value < (ELANSPI_MIN_OLD_STAGE2_CALBIRATION_MEAN + (ELANSPI_MAX_OLD_STAGE2_CALBIRATION_MEAN - ELANSPI_MIN_OLD_STAGE2_CALBIRATION_MEAN) / 2)) | |
657 | ✗ | self->old_data.dac_value -= 1; | |
658 | else | ||
659 | ✗ | self->old_data.dac_value += 1; | |
660 | |||
661 | /* write it */ | ||
662 | ✗ | xfer = elanspi_write_register (self, 0x6, self->old_data.dac_value - 0x40); | |
663 | ✗ | xfer->ssm = ssm; | |
664 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
665 | ✗ | return; | |
666 | |||
667 | ✗ | case ELANSPI_CALIBOLD_DACFINE_LOOP: | |
668 | /* check the retry counter */ | ||
669 | ✗ | self->old_data.dacfine_retry += 1; | |
670 | ✗ | if (self->old_data.dacfine_retry >= 2) | |
671 | { | ||
672 | /* bail with calibration error */ | ||
673 | ✗ | err = fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER, "finger on sensor during calibration"); | |
674 | ✗ | fpi_ssm_mark_failed (ssm, err); | |
675 | ✗ | return; | |
676 | } | ||
677 | ✗ | fp_dbg ("<calibold> repeating calibration for the %dth time", self->old_data.dacfine_retry); | |
678 | /* otherwise, take another image */ | ||
679 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_CALIBOLD_DACFINE_CAPTURE); | |
680 | ✗ | return; | |
681 | |||
682 | 1 | case ELANSPI_CALIBOLD_PROTECT: | |
683 | 1 | fp_dbg ("<calibold> calibration ok, saving bg image"); | |
684 | 1 | xfer = elanspi_write_register (self, 0x00, 0x00); | |
685 | 1 | xfer->ssm = ssm; | |
686 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
687 | 1 | return; | |
688 | } | ||
689 | } | ||
690 | |||
691 | static void | ||
692 | ✗ | elanspi_capture_hv_image_handler (FpiSpiTransfer *transfer, FpDevice *dev, gpointer unused_data, GError *error) | |
693 | { | ||
694 | ✗ | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
695 | |||
696 | ✗ | if (error) | |
697 | { | ||
698 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
699 | ✗ | return; | |
700 | } | ||
701 | |||
702 | int i, outptr; | ||
703 | guint16 value = 0; | ||
704 | |||
705 | ✗ | for (i = 0, outptr = 0; i < transfer->length_rd && outptr < (self->sensor_height * self->sensor_width * 2); i += 1) | |
706 | { | ||
707 | ✗ | if (transfer->buffer_rd[i] != 0xff) | |
708 | { | ||
709 | ✗ | if (outptr % 2) | |
710 | { | ||
711 | ✗ | value <<= 8; | |
712 | ✗ | value |= transfer->buffer_rd[i]; | |
713 | ✗ | self->last_image[outptr / 2] = value; | |
714 | } | ||
715 | else | ||
716 | { | ||
717 | ✗ | value = transfer->buffer_rd[i]; | |
718 | } | ||
719 | ✗ | outptr += 1; | |
720 | } | ||
721 | } | ||
722 | |||
723 | ✗ | if (outptr != (self->sensor_height * self->sensor_width * 2)) | |
724 | { | ||
725 | ✗ | fp_warn ("<capture/hv> did not receive full image"); | |
726 | /* mark ssm failed */ | ||
727 | ✗ | error = fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, "hv image receieve did not fill buffer"); | |
728 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
729 | ✗ | return; | |
730 | } | ||
731 | |||
732 | ✗ | fpi_ssm_mark_completed (transfer->ssm); | |
733 | } | ||
734 | |||
735 | |||
736 | static void | ||
737 | ✗ | elanspi_capture_hv_handler (FpiSsm *ssm, FpDevice *dev) | |
738 | { | ||
739 | ✗ | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
740 | ✗ | FpiSpiTransfer *xfer = NULL; | |
741 | |||
742 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
743 | { | ||
744 | ✗ | case ELANSPI_CAPTHV_WRITE_CAPTURE: | |
745 | /* reset capture state */ | ||
746 | ✗ | self->old_data.line_ptr = 0; | |
747 | ✗ | self->capture_timeout = g_get_monotonic_time () + ELANSPI_HV_CAPTURE_TIMEOUT_USEC; | |
748 | ✗ | xfer = elanspi_do_capture (self); | |
749 | ✗ | xfer->ssm = ssm; | |
750 | /* these are specifically cancellable because they don't leave the device at some aribtrary line offset, since | ||
751 | * these devices only send entire images */ | ||
752 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
753 | ✗ | return; | |
754 | |||
755 | ✗ | case ELANSPI_CAPTHV_CHECK_READY: | |
756 | ✗ | xfer = elanspi_read_status (self, &self->sensor_status); | |
757 | ✗ | xfer->ssm = ssm; | |
758 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
759 | ✗ | return; | |
760 | |||
761 | ✗ | case ELANSPI_CAPTHV_RECV_IMAGE: | |
762 | /* is the sensor ready? */ | ||
763 | ✗ | if (!(self->sensor_status & 4)) | |
764 | { | ||
765 | /* has the timeout expired? */ | ||
766 | ✗ | if (g_get_monotonic_time () > self->capture_timeout) | |
767 | { | ||
768 | /* end with a timeout */ | ||
769 | ✗ | fpi_ssm_mark_failed (ssm, g_error_new (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "timed out waiting for image")); | |
770 | ✗ | return; | |
771 | } | ||
772 | /* check again */ | ||
773 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_CAPTHV_CHECK_READY); | |
774 | ✗ | return; | |
775 | } | ||
776 | /* otherwise, read the image | ||
777 | * the hv sensors seem to use 128 bytes of padding(?) this is only tested on the 0xe sensors */ | ||
778 | ✗ | xfer = fpi_spi_transfer_new (dev, self->spi_fd); | |
779 | ✗ | xfer->ssm = ssm; | |
780 | ✗ | fpi_spi_transfer_write (xfer, 2); | |
781 | ✗ | xfer->buffer_wr[0] = 0x10; /* receieve line */ | |
782 | ✗ | fpi_spi_transfer_read (xfer, self->sensor_height * (self->sensor_width * 2 + 48)); | |
783 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), elanspi_capture_hv_image_handler, NULL); | |
784 | ✗ | return; | |
785 | } | ||
786 | } | ||
787 | |||
788 | static void | ||
789 | ✗ | elanspi_calibrate_hv_handler (FpiSsm *ssm, FpDevice *dev) | |
790 | { | ||
791 | ✗ | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
792 | ✗ | FpiSpiTransfer *xfer = NULL; | |
793 | ✗ | GError *err = NULL; | |
794 | ✗ | FpiSsm *chld = NULL; | |
795 | ✗ | int mean_diff = 0; | |
796 | |||
797 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
798 | { | ||
799 | ✗ | case ELANSPI_CALIBHV_SELECT_PAGE0_0: | |
800 | /* initialize gdac */ | ||
801 | ✗ | self->hv_data.gdac_value = 0x100; | |
802 | ✗ | self->hv_data.gdac_step = 0x100; | |
803 | ✗ | self->hv_data.best_gdac = 0x0; | |
804 | ✗ | self->hv_data.best_meandiff = 0xffff; | |
805 | |||
806 | ✗ | case ELANSPI_CALIBHV_SELECT_PAGE0_1: | |
807 | ✗ | xfer = elanspi_do_selectpage (self, 0); | |
808 | ✗ | xfer->ssm = ssm; | |
809 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
810 | ✗ | return; | |
811 | |||
812 | ✗ | case ELANSPI_CALIBHV_WRITE_STARTCALIB: | |
813 | ✗ | xfer = elanspi_do_startcalib (self); | |
814 | ✗ | xfer->ssm = ssm; | |
815 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
816 | ✗ | return; | |
817 | |||
818 | ✗ | case ELANSPI_CALIBHV_UNPROTECT: | |
819 | ✗ | xfer = elanspi_write_register (self, 0x00, 0x5a); | |
820 | ✗ | xfer->ssm = ssm; | |
821 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
822 | ✗ | return; | |
823 | |||
824 | ✗ | case ELANSPI_CALIBHV_SEND_REGTABLE0: | |
825 | ✗ | chld = elanspi_write_regtable (self, &elanspi_calibration_table_new_page0); | |
826 | ✗ | if (chld == NULL) | |
827 | { | ||
828 | ✗ | err = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED, "unknown calibration table for sensor"); | |
829 | ✗ | fpi_ssm_mark_failed (ssm, err); | |
830 | ✗ | return; | |
831 | } | ||
832 | ✗ | fpi_ssm_start_subsm (ssm, chld); | |
833 | ✗ | return; | |
834 | |||
835 | ✗ | case ELANSPI_CALIBHV_SELECT_PAGE1: | |
836 | ✗ | xfer = elanspi_do_selectpage (self, 1); | |
837 | ✗ | xfer->ssm = ssm; | |
838 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
839 | ✗ | return; | |
840 | |||
841 | ✗ | case ELANSPI_CALIBHV_SEND_REGTABLE1: | |
842 | ✗ | chld = elanspi_write_regtable (self, &elanspi_calibration_table_new_page1); | |
843 | ✗ | if (chld == NULL) | |
844 | { | ||
845 | ✗ | err = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED, "unknown calibration table for sensor"); | |
846 | ✗ | fpi_ssm_mark_failed (ssm, err); | |
847 | ✗ | return; | |
848 | } | ||
849 | ✗ | fpi_ssm_start_subsm (ssm, chld); | |
850 | ✗ | return; | |
851 | |||
852 | ✗ | case ELANSPI_CALIBHV_WRITE_GDAC_H: | |
853 | case ELANSPI_CALIBHV_WRITE_BEST_GDAC_H: | ||
854 | ✗ | if (fpi_ssm_get_cur_state (ssm) == ELANSPI_CALIBHV_WRITE_BEST_GDAC_H) | |
855 | ✗ | self->hv_data.gdac_value = self->hv_data.best_gdac; | |
856 | ✗ | xfer = elanspi_write_register (self, 0x06, (self->hv_data.gdac_value >> 2) & 0xff); | |
857 | ✗ | xfer->ssm = ssm; | |
858 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
859 | ✗ | return; | |
860 | |||
861 | ✗ | case ELANSPI_CALIBHV_WRITE_GDAC_L: | |
862 | case ELANSPI_CALIBHV_WRITE_BEST_GDAC_L: | ||
863 | ✗ | xfer = elanspi_write_register (self, 0x07, self->hv_data.gdac_value & 3); | |
864 | ✗ | xfer->ssm = ssm; | |
865 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
866 | ✗ | return; | |
867 | |||
868 | ✗ | case ELANSPI_CALIBHV_CAPTURE: | |
869 | ✗ | chld = fpi_ssm_new (dev, elanspi_capture_hv_handler, ELANSPI_CAPTHV_NSTATES); | |
870 | ✗ | fpi_ssm_silence_debug (chld); | |
871 | ✗ | fpi_ssm_start_subsm (ssm, chld); | |
872 | ✗ | return; | |
873 | |||
874 | ✗ | case ELANSPI_CALIBHV_PROCESS: | |
875 | /* compute mean */ | ||
876 | ✗ | mean_diff = abs (elanspi_mean_image (self, self->last_image) - ELANSPI_HV_CALIBRATION_TARGET_MEAN); | |
877 | ✗ | if (mean_diff < 100) | |
878 | { | ||
879 | ✗ | fp_dbg ("<calibhv> calibration ok (mdiff < 100 w/ gdac=%04x)", self->hv_data.gdac_value); | |
880 | /* exit early, jump right to protect */ | ||
881 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_CALIBHV_PROTECT); | |
882 | ✗ | return; | |
883 | } | ||
884 | ✗ | if (mean_diff < self->hv_data.best_meandiff) | |
885 | { | ||
886 | ✗ | self->hv_data.best_meandiff = mean_diff; | |
887 | ✗ | self->hv_data.best_gdac = self->hv_data.gdac_value; | |
888 | } | ||
889 | /* shrink step */ | ||
890 | ✗ | self->hv_data.gdac_step /= 2; | |
891 | ✗ | if (self->hv_data.gdac_step == 0) | |
892 | { | ||
893 | ✗ | fp_dbg ("<calibhv> calibration ok (step = 0 w/ best_gdac=%04x)", self->hv_data.best_gdac); | |
894 | /* exit, using best value */ | ||
895 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_CALIBHV_WRITE_BEST_GDAC_H); | |
896 | ✗ | return; | |
897 | } | ||
898 | /* update gdac */ | ||
899 | ✗ | if (elanspi_mean_image (self, self->last_image) < ELANSPI_HV_CALIBRATION_TARGET_MEAN) | |
900 | ✗ | self->hv_data.gdac_value -= self->hv_data.gdac_step; | |
901 | else | ||
902 | ✗ | self->hv_data.gdac_value += self->hv_data.gdac_step; | |
903 | /* advance back to capture */ | ||
904 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_CALIBHV_WRITE_GDAC_H); | |
905 | ✗ | return; | |
906 | |||
907 | ✗ | case ELANSPI_CALIBHV_PROTECT: | |
908 | ✗ | fp_dbg ("<calibhv> calibration ok, saving bg image"); | |
909 | ✗ | xfer = elanspi_write_register (self, 0x00, 0x00); | |
910 | ✗ | xfer->ssm = ssm; | |
911 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
912 | ✗ | return; | |
913 | |||
914 | } | ||
915 | } | ||
916 | |||
917 | static void | ||
918 | 20 | elanspi_init_ssm_handler (FpiSsm *ssm, FpDevice *dev) | |
919 | { | ||
920 | 20 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
921 | 20 | FpiSpiTransfer *xfer = NULL; | |
922 | 20 | GError *err = NULL; | |
923 | 20 | FpiSsm *chld = NULL; | |
924 | |||
925 |
19/22✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
✓ Branch 18 taken 1 times.
✓ Branch 19 taken 1 times.
✓ Branch 20 taken 1 times.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
|
20 | switch (fpi_ssm_get_cur_state (ssm)) |
926 | { | ||
927 | 1 | case ELANSPI_INIT_READ_STATUS1: | |
928 | 1 | xfer = elanspi_read_status (self, &self->sensor_status); | |
929 | 1 | xfer->ssm = ssm; | |
930 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
931 | 21 | return; | |
932 | |||
933 | 1 | case ELANSPI_INIT_HWSWRESET: | |
934 | 1 | fp_dbg ("<init> got status %02x", self->sensor_status); | |
935 | 1 | elanspi_do_hwreset (self, &err); | |
936 | 1 | fp_dbg ("<init> sync hw reset"); | |
937 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (err) |
938 | { | ||
939 | ✗ | fp_err ("<init> sync hw reset failed"); | |
940 | ✗ | fpi_ssm_mark_failed (ssm, err); | |
941 | ✗ | return; | |
942 | } | ||
943 | 1 | do_sw_reset: | |
944 | 2 | xfer = elanspi_do_swreset (self); | |
945 | 2 | xfer->ssm = ssm; | |
946 | 2 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
947 | 2 | return; | |
948 | |||
949 | 2 | case ELANSPI_INIT_SWRESETDELAY1: | |
950 | case ELANSPI_INIT_SWRESETDELAY2: | ||
951 | 2 | fpi_ssm_next_state_delayed (ssm, 4); | |
952 | 2 | return; | |
953 | |||
954 | 1 | case ELANSPI_INIT_READ_HEIGHT: | |
955 | 1 | fp_dbg ("<init> sw reset ok"); | |
956 | 1 | xfer = elanspi_read_height (self, &self->sensor_height); | |
957 | 1 | xfer->ssm = ssm; | |
958 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
959 | 1 | return; | |
960 | |||
961 | 1 | case ELANSPI_INIT_READ_WIDTH: | |
962 | 1 | self->sensor_height += 1; | |
963 | 1 | fp_dbg ("<init> raw height = %d", self->sensor_height); | |
964 | 1 | xfer = elanspi_read_width (self, &self->sensor_width); | |
965 | 1 | xfer->ssm = ssm; | |
966 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
967 | 1 | return; | |
968 | |||
969 | 1 | case ELANSPI_INIT_READ_REG17: | |
970 | 1 | self->sensor_width += 1; | |
971 | 1 | fp_dbg ("<init> raw width = %d", self->sensor_width); | |
972 | 1 | xfer = elanspi_read_register (self, 0x17, &self->sensor_reg_17); | |
973 | 1 | xfer->ssm = ssm; | |
974 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
975 | 1 | return; | |
976 | |||
977 | 1 | case ELANSPI_INIT_READ_VERSION: | |
978 | 1 | fp_dbg ("<init> raw reg17 = %d", self->sensor_reg_17); | |
979 | 1 | xfer = elanspi_read_version (self, &self->sensor_raw_version); | |
980 | 1 | xfer->ssm = ssm; | |
981 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
982 | 1 | return; | |
983 | |||
984 | 1 | case ELANSPI_INIT_SWRESET2: | |
985 | 1 | fp_dbg ("<init> raw version = %02x", self->sensor_raw_version); | |
986 | 1 | elanspi_determine_sensor (self, &err); | |
987 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (err) |
988 | { | ||
989 | ✗ | fp_err ("<init> sensor detection error"); | |
990 | ✗ | fpi_ssm_mark_failed (ssm, err); | |
991 | ✗ | return; | |
992 | } | ||
993 | /* allocate memory */ | ||
994 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_clear_pointer (&self->bg_image, g_free); |
995 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_clear_pointer (&self->last_image, g_free); |
996 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | g_clear_pointer (&self->prev_frame_image, g_free); |
997 | 1 | self->last_image = g_malloc0 (self->sensor_width * self->sensor_height * 2); | |
998 | 1 | self->bg_image = g_malloc0 (self->sensor_width * self->sensor_height * 2); | |
999 | 1 | self->prev_frame_image = g_malloc0 (self->sensor_width * self->sensor_height * 2); | |
1000 | /* reset again */ | ||
1001 | 1 | goto do_sw_reset; | |
1002 | |||
1003 | 1 | case ELANSPI_INIT_OTP_READ_VREF1: | |
1004 | /* is this sensor otp? */ | ||
1005 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!self->sensor_otp) |
1006 | { | ||
1007 | /* go to calibration */ | ||
1008 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_INIT_CALIBRATE); | |
1009 | ✗ | return; | |
1010 | } | ||
1011 | /* otherwise, begin otp */ | ||
1012 | 1 | self->old_data.otp_timeout = g_get_monotonic_time () + ELANSPI_OTP_TIMEOUT_USEC; | |
1013 | 1 | xfer = elanspi_read_register (self, 0x3d, &self->sensor_reg_vref1); | |
1014 | 1 | xfer->ssm = ssm; | |
1015 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1016 | 1 | return; | |
1017 | |||
1018 | 1 | case ELANSPI_INIT_OTP_WRITE_VREF1: | |
1019 | /* mask out low bits */ | ||
1020 | 1 | self->sensor_reg_vref1 &= 0x3f; | |
1021 | 1 | xfer = elanspi_write_register (self, 0x3d, self->sensor_reg_vref1); | |
1022 | 1 | xfer->ssm = ssm; | |
1023 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1024 | 1 | return; | |
1025 | |||
1026 | 1 | case ELANSPI_INIT_OTP_WRITE_0x28: | |
1027 | 1 | xfer = elanspi_write_register (self, 0x28, 0x78); | |
1028 | 1 | xfer->ssm = ssm; | |
1029 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1030 | 1 | return; | |
1031 | |||
1032 | /* begin loop */ | ||
1033 | 1 | case ELANSPI_INIT_OTP_LOOP_READ_0x28: | |
1034 | /* begin read of 0x28 */ | ||
1035 | 1 | xfer = elanspi_read_register (self, 0x28, &self->sensor_reg_28); | |
1036 | 1 | xfer->ssm = ssm; | |
1037 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1038 | 1 | return; | |
1039 | |||
1040 | 1 | case ELANSPI_INIT_OTP_LOOP_READ_0x27: | |
1041 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (self->sensor_reg_28 & 0x40) |
1042 | { | ||
1043 | /* try again */ | ||
1044 | ✗ | fp_dbg ("<init/otp> looping"); | |
1045 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_INIT_OTP_LOOP_READ_0x28); | |
1046 | ✗ | return; | |
1047 | } | ||
1048 | /* otherwise, read reg 27 */ | ||
1049 | 1 | xfer = elanspi_read_register (self, 0x27, &self->sensor_reg_27); | |
1050 | 1 | xfer->ssm = ssm; | |
1051 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1052 | 1 | return; | |
1053 | |||
1054 | 1 | case ELANSPI_INIT_OTP_LOOP_UPDATEDAC_READ_DAC2: | |
1055 | /* if high bit set, exit with mode 2 */ | ||
1056 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (self->sensor_reg_27 & 0x80) |
1057 | { | ||
1058 | 1 | self->sensor_vcm_mode = 2; | |
1059 | 1 | fpi_ssm_jump_to_state (ssm, ELANSPI_INIT_OTP_WRITE_0xb); | |
1060 | 1 | return; | |
1061 | } | ||
1062 | /* if low two bits are not set, loop */ | ||
1063 | ✗ | if ((self->sensor_reg_27 & 6) != 6) | |
1064 | { | ||
1065 | /* have we hit the timeout */ | ||
1066 | ✗ | if (g_get_monotonic_time () > self->old_data.otp_timeout) | |
1067 | { | ||
1068 | ✗ | fp_warn ("<init/otp> timed out waiting for vcom detection"); | |
1069 | ✗ | self->sensor_vcm_mode = 2; | |
1070 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_INIT_OTP_WRITE_0xb); | |
1071 | ✗ | return; | |
1072 | } | ||
1073 | /* try again */ | ||
1074 | ✗ | fp_dbg ("<init/otp> looping"); | |
1075 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_INIT_OTP_LOOP_READ_0x28); | |
1076 | ✗ | return; | |
1077 | } | ||
1078 | /* otherwise, set vcm mode from low bit and read dac2 */ | ||
1079 | ✗ | self->sensor_vcm_mode = (self->sensor_reg_27 & 1) + 1; | |
1080 | ✗ | xfer = elanspi_read_register (self, 0x7, &self->sensor_reg_dac2); | |
1081 | ✗ | xfer->ssm = ssm; | |
1082 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1083 | ✗ | return; | |
1084 | |||
1085 | ✗ | case ELANSPI_INIT_OTP_LOOP_UPDATEDAC_WRITE_DAC2: | |
1086 | /* set high bit and rewrite */ | ||
1087 | ✗ | self->sensor_reg_dac2 |= 0x80; | |
1088 | ✗ | xfer = elanspi_write_register (self, 0x7, self->sensor_reg_dac2); | |
1089 | ✗ | xfer->ssm = ssm; | |
1090 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1091 | ✗ | return; | |
1092 | |||
1093 | ✗ | case ELANSPI_INIT_OTP_LOOP_UPDATEDAC_WRITE_10: | |
1094 | ✗ | xfer = elanspi_write_register (self, 0xa, 0x97); | |
1095 | ✗ | xfer->ssm = ssm; | |
1096 | ✗ | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1097 | ✗ | return; | |
1098 | |||
1099 | /* end loop, joins to here on early exits */ | ||
1100 | 1 | case ELANSPI_INIT_OTP_WRITE_0xb: | |
1101 | 1 | fp_dbg ("<init/otp> got vcm mode = %d", self->sensor_vcm_mode); | |
1102 | /* if mode is 0, skip to calibration */ | ||
1103 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (self->sensor_vcm_mode == 0) |
1104 | { | ||
1105 | ✗ | fpi_ssm_jump_to_state (ssm, ELANSPI_INIT_CALIBRATE); | |
1106 | ✗ | return; | |
1107 | } | ||
1108 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | xfer = elanspi_write_register (self, 0xb, self->sensor_vcm_mode == 2 ? 0x72 : 0x71); |
1109 | 1 | xfer->ssm = ssm; | |
1110 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1111 | 1 | return; | |
1112 | |||
1113 | 1 | case ELANSPI_INIT_OTP_WRITE_0xc: | |
1114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | xfer = elanspi_write_register (self, 0xc, self->sensor_vcm_mode == 2 ? 0x62 : 0x49); |
1115 | 1 | xfer->ssm = ssm; | |
1116 | 1 | fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), fpi_ssm_spi_transfer_cb, NULL); | |
1117 | 1 | return; | |
1118 | |||
1119 | 1 | case ELANSPI_INIT_CALIBRATE: | |
1120 | 1 | fp_dbg ("<init/calibrate> starting calibrate"); | |
1121 | /* if sensor is hv */ | ||
1122 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (self->sensor_id == 0xe) |
1123 | ✗ | chld = fpi_ssm_new_full (dev, elanspi_calibrate_hv_handler, ELANSPI_CALIBHV_NSTATES, ELANSPI_CALIBHV_PROTECT, "HV calibrate"); | |
1124 | else | ||
1125 | 1 | chld = fpi_ssm_new_full (dev, elanspi_calibrate_old_handler, ELANSPI_CALIBOLD_NSTATES, ELANSPI_CALIBOLD_PROTECT, "old calibrate"); | |
1126 | 1 | fpi_ssm_silence_debug (chld); | |
1127 | 1 | fpi_ssm_start_subsm (ssm, chld); | |
1128 | 1 | return; | |
1129 | |||
1130 | 1 | case ELANSPI_INIT_BG_CAPTURE: | |
1131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (self->sensor_id == 0xe) |
1132 | ✗ | chld = fpi_ssm_new (dev, elanspi_capture_hv_handler, ELANSPI_CAPTHV_NSTATES); | |
1133 | else | ||
1134 | 1 | chld = fpi_ssm_new (dev, elanspi_capture_old_handler, ELANSPI_CAPTOLD_NSTATES); | |
1135 | 1 | fpi_ssm_silence_debug (chld); | |
1136 | 1 | fpi_ssm_start_subsm (ssm, chld); | |
1137 | 1 | return; | |
1138 | |||
1139 | 1 | case ELANSPI_INIT_BG_SAVE: | |
1140 | 1 | memcpy (self->bg_image, self->last_image, self->sensor_height * self->sensor_width * 2); | |
1141 | 1 | fpi_ssm_mark_completed (ssm); | |
1142 | 1 | return; | |
1143 | } | ||
1144 | } | ||
1145 | |||
1146 | enum elanspi_guess_result { | ||
1147 | ELANSPI_GUESS_FINGERPRINT, | ||
1148 | ELANSPI_GUESS_EMPTY, | ||
1149 | ELANSPI_GUESS_UNKNOWN | ||
1150 | }; | ||
1151 | |||
1152 | /* in place correct image, returning number of invalid pixels */ | ||
1153 | static gint | ||
1154 | 96 | elanspi_correct_with_bg (FpiDeviceElanSpi *self, guint16 *raw_image) | |
1155 | { | ||
1156 | 96 | gint count = 0; | |
1157 | |||
1158 |
2/2✓ Branch 0 taken 884736 times.
✓ Branch 1 taken 96 times.
|
884832 | for (int i = 0; i < self->sensor_width * self->sensor_height; i += 1) |
1159 | { | ||
1160 |
2/2✓ Branch 0 taken 104162 times.
✓ Branch 1 taken 780574 times.
|
884736 | if (raw_image[i] < self->bg_image[i]) |
1161 | { | ||
1162 | 104162 | count += 1; | |
1163 | 104162 | raw_image[i] = 0; | |
1164 | } | ||
1165 | else | ||
1166 | { | ||
1167 | 780574 | raw_image[i] -= self->bg_image[i]; | |
1168 | } | ||
1169 | } | ||
1170 | |||
1171 | 96 | return count; | |
1172 | } | ||
1173 | |||
1174 | static guint16 | ||
1175 | 792576 | elanspi_lookup_pixel_with_rotation (FpiDeviceElanSpi *self, const guint16 *data_in, int y, int x) | |
1176 | { | ||
1177 | 792576 | int rotation = fpi_device_get_driver_data (FP_DEVICE (self)) & 3; | |
1178 | 792576 | gint x1 = x, y1 = y; | |
1179 | |||
1180 |
1/2✓ Branch 0 taken 792576 times.
✗ Branch 1 not taken.
|
792576 | if (rotation == ELANSPI_180_ROTATE) |
1181 | { | ||
1182 | 792576 | x1 = (self->sensor_width - x - 1); | |
1183 | 792576 | y1 = (self->sensor_height - y - 1); | |
1184 | } | ||
1185 | ✗ | else if (rotation == ELANSPI_90LEFT_ROTATE) | |
1186 | { | ||
1187 | ✗ | x1 = y; | |
1188 | ✗ | y1 = (self->sensor_width - x - 1); | |
1189 | } | ||
1190 | ✗ | else if (rotation == ELANSPI_90RIGHT_ROTATE) | |
1191 | { | ||
1192 | ✗ | x1 = (self->sensor_height - y - 1); | |
1193 | ✗ | y1 = x; | |
1194 | } | ||
1195 | 792576 | return data_in[y1 * self->sensor_width + x1]; | |
1196 | } | ||
1197 | |||
1198 | static enum elanspi_guess_result | ||
1199 | 73 | elanspi_guess_image (FpiDeviceElanSpi *self, guint16 *raw_image) | |
1200 | { | ||
1201 | 73 | g_autofree guint16 * image_copy = g_malloc0 (self->sensor_height * self->sensor_width * 2); | |
1202 | 73 | guint8 frame_width, frame_height; | |
1203 | |||
1204 | /* make clang happy about div0 */ | ||
1205 | 73 | frame_width = self->frame_width; | |
1206 | 73 | frame_height = self->frame_height; | |
1207 |
1/2✓ Branch 0 taken 73 times.
✗ Branch 1 not taken.
|
73 | g_assert (frame_width && frame_height); |
1208 | |||
1209 | 73 | memcpy (image_copy, raw_image, self->sensor_height * self->sensor_width * 2); | |
1210 | |||
1211 | 73 | gint invalid_percent = (100 * elanspi_correct_with_bg (self, image_copy)) / (self->sensor_height * self->sensor_width); | |
1212 | 73 | gint is_fp = 0, is_empty = 0; | |
1213 | |||
1214 | 73 | gint64 mean = 0; | |
1215 | 73 | gint64 sq_stddev = 0; | |
1216 | |||
1217 |
2/2✓ Branch 0 taken 3139 times.
✓ Branch 1 taken 73 times.
|
3212 | for (int j = 0; j < frame_height; j += 1) |
1218 |
2/2✓ Branch 0 taken 301344 times.
✓ Branch 1 taken 3139 times.
|
304483 | for (int i = 0; i < frame_width; i += 1) |
1219 | 301344 | mean += (gint64) elanspi_lookup_pixel_with_rotation (self, image_copy, j, i); | |
1220 | |||
1221 | 73 | mean /= (frame_width * frame_height); | |
1222 | |||
1223 |
2/2✓ Branch 0 taken 3139 times.
✓ Branch 1 taken 73 times.
|
3212 | for (int j = 0; j < frame_height; j += 1) |
1224 |
2/2✓ Branch 0 taken 301344 times.
✓ Branch 1 taken 3139 times.
|
304483 | for (int i = 0; i < frame_width; i += 1) |
1225 | { | ||
1226 | 301344 | gint64 k = (gint64) elanspi_lookup_pixel_with_rotation (self, image_copy, j, i) - mean; | |
1227 | 301344 | sq_stddev += k * k; | |
1228 | } | ||
1229 | |||
1230 | 73 | sq_stddev /= (frame_width * frame_height); | |
1231 | |||
1232 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 44 times.
|
73 | if (invalid_percent < ELANSPI_MAX_REAL_INVALID_PERCENT) |
1233 | is_fp += 1; | ||
1234 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 2 times.
|
29 | if (invalid_percent > ELANSPI_MIN_EMPTY_INVALID_PERCENT) |
1235 | 27 | is_empty += 1; | |
1236 | |||
1237 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 33 times.
|
73 | if (sq_stddev > ELANSPI_MIN_REAL_STDDEV) |
1238 | 40 | is_fp += 1; | |
1239 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 46 times.
|
73 | if (sq_stddev < ELANSPI_MAX_EMPTY_STDDEV) |
1240 | 27 | is_empty += 1; | |
1241 | |||
1242 | 73 | fp_dbg ("<guess> stddev=%" G_GUINT64_FORMAT "d, ip=%d, is_fp=%d, is_empty=%d", sq_stddev, invalid_percent, is_fp, is_empty); | |
1243 | |||
1244 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 46 times.
|
73 | if (is_fp > is_empty) |
1245 | return ELANSPI_GUESS_FINGERPRINT; | ||
1246 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
|
27 | else if (is_empty > is_fp) |
1247 | return ELANSPI_GUESS_EMPTY; | ||
1248 | else | ||
1249 | ✗ | return ELANSPI_GUESS_UNKNOWN; | |
1250 | } | ||
1251 | |||
1252 | /* returns TRUE when the waiting is complete */ | ||
1253 | static gboolean | ||
1254 | 49 | elanspi_check_waitupdown_done (FpiDeviceElanSpi *self, enum elanspi_guess_result target) | |
1255 | { | ||
1256 | 49 | enum elanspi_guess_result guess = elanspi_guess_image (self, self->last_image); | |
1257 | |||
1258 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
|
49 | if (guess == ELANSPI_GUESS_UNKNOWN) |
1259 | return FALSE; | ||
1260 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 45 times.
|
49 | if (guess == target) |
1261 | { | ||
1262 | 4 | self->finger_wait_debounce += 1; | |
1263 | 4 | return self->finger_wait_debounce == ELANSPI_MIN_FRAMES_DEBOUNCE; | |
1264 | } | ||
1265 | else | ||
1266 | { | ||
1267 | 45 | self->finger_wait_debounce = 0; | |
1268 | 45 | return FALSE; | |
1269 | } | ||
1270 | } | ||
1271 | |||
1272 | static int | ||
1273 | 951265 | cmp_u16 (const void *a, const void *b) | |
1274 | { | ||
1275 | 951265 | return (int) (*(guint16 *) a - *(guint16 *) b); | |
1276 | } | ||
1277 | |||
1278 | static void | ||
1279 | 23 | elanspi_process_frame (FpiDeviceElanSpi *self, const guint16 *data_in, guint8 *data_out) | |
1280 | 23 | { | |
1281 | 23 | size_t frame_size = self->frame_width * self->frame_height; | |
1282 | 23 | guint16 data_in_sorted[frame_size]; | |
1283 | |||
1284 |
2/2✓ Branch 0 taken 989 times.
✓ Branch 1 taken 23 times.
|
1012 | for (int i = 0, offset = 0; i < self->frame_height; i += 1) |
1285 |
2/2✓ Branch 0 taken 94944 times.
✓ Branch 1 taken 989 times.
|
95933 | for (int j = 0; j < self->frame_width; j += 1) |
1286 | 94944 | data_in_sorted[offset++] = elanspi_lookup_pixel_with_rotation (self, data_in, i, j); | |
1287 | |||
1288 | 23 | qsort (data_in_sorted, frame_size, 2, cmp_u16); | |
1289 | 23 | guint16 lvl0 = data_in_sorted[0]; | |
1290 | 23 | guint16 lvl1 = data_in_sorted[frame_size * 3 / 10]; | |
1291 | 23 | guint16 lvl2 = data_in_sorted[frame_size * 65 / 100]; | |
1292 | 23 | guint16 lvl3 = data_in_sorted[frame_size - 1]; | |
1293 | |||
1294 | 23 | lvl1 = MAX (lvl1, lvl0 + 1); | |
1295 | 23 | lvl2 = MAX (lvl2, lvl1 + 1); | |
1296 | 23 | lvl3 = MAX (lvl3, lvl2 + 1); | |
1297 | |||
1298 |
2/2✓ Branch 0 taken 989 times.
✓ Branch 1 taken 23 times.
|
1012 | for (int i = 0; i < self->frame_height; i += 1) |
1299 | { | ||
1300 |
2/2✓ Branch 0 taken 94944 times.
✓ Branch 1 taken 989 times.
|
95933 | for (int j = 0; j < self->frame_width; j += 1) |
1301 | { | ||
1302 | 94944 | guint16 px = elanspi_lookup_pixel_with_rotation (self, data_in, i, j); | |
1303 |
1/2✓ Branch 0 taken 94944 times.
✗ Branch 1 not taken.
|
94944 | if (px < lvl0) |
1304 | { | ||
1305 | px = 0; | ||
1306 | } | ||
1307 |
1/2✓ Branch 0 taken 94944 times.
✗ Branch 1 not taken.
|
94944 | else if (px > lvl3) |
1308 | { | ||
1309 | px = 255; | ||
1310 | } | ||
1311 | else | ||
1312 | { | ||
1313 |
2/2✓ Branch 0 taken 28460 times.
✓ Branch 1 taken 66484 times.
|
94944 | if (lvl0 <= px && px < lvl1) |
1314 | 28460 | px = (px - lvl0) * 99 / (lvl1 - lvl0); | |
1315 |
2/2✓ Branch 0 taken 33232 times.
✓ Branch 1 taken 33252 times.
|
66484 | else if (lvl1 <= px && px < lvl2) |
1316 | 33232 | px = 99 + ((px - lvl1) * 56 / (lvl2 - lvl1)); | |
1317 | else /* (lvl2 <= px && px <= lvl3) */ | ||
1318 | 33252 | px = 155 + ((px - lvl2) * 100 / (lvl3 - lvl2)); | |
1319 | } | ||
1320 | 94944 | *data_out = px; | |
1321 | 94944 | data_out += 1; | |
1322 | } | ||
1323 | } | ||
1324 | 23 | } | |
1325 | |||
1326 | static unsigned char | ||
1327 | 152172653 | elanspi_fp_assembling_get_pixel (struct fpi_frame_asmbl_ctx *ctx, struct fpi_frame *frame, unsigned int x, unsigned int y) | |
1328 | { | ||
1329 | 152172653 | return frame->data[y * ctx->frame_width + x]; | |
1330 | } | ||
1331 | |||
1332 | static void | ||
1333 | 1 | elanspi_fp_frame_stitch_and_submit (FpiDeviceElanSpi *self) | |
1334 | { | ||
1335 | 2 | g_autoptr(FpImage) img = NULL; | |
1336 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | g_autoptr(FpImage) scaled = NULL; |
1337 | 1 | struct fpi_frame_asmbl_ctx assembling_ctx = { | |
1338 | 1 | .image_width = (self->frame_width * 3) / 2, | |
1339 | |||
1340 | 1 | .frame_width = self->frame_width, | |
1341 | 1 | .frame_height = self->frame_height, | |
1342 | |||
1343 | .get_pixel = elanspi_fp_assembling_get_pixel, | ||
1344 | }; | ||
1345 | |||
1346 | /* stitch image */ | ||
1347 | 1 | GSList *frame_start = g_slist_nth (self->fp_frame_list, ELANSPI_SWIPE_FRAMES_DISCARD); | |
1348 | |||
1349 | 1 | fpi_do_movement_estimation (&assembling_ctx, frame_start); | |
1350 | 1 | img = fpi_assemble_frames (&assembling_ctx, frame_start); | |
1351 | 1 | scaled = fpi_image_resize (img, 2, 2); | |
1352 | |||
1353 | 1 | scaled->flags |= FPI_IMAGE_PARTIAL | FPI_IMAGE_COLORS_INVERTED; | |
1354 | |||
1355 | /* submit image */ | ||
1356 | 1 | fpi_image_device_image_captured (FP_IMAGE_DEVICE (self), g_steal_pointer (&scaled)); | |
1357 | |||
1358 | /* clean out frame data */ | ||
1359 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | g_slist_free_full (g_steal_pointer (&self->fp_frame_list), g_free); |
1360 | 1 | } | |
1361 | |||
1362 | static gint64 | ||
1363 | 22 | elanspi_get_frame_diff_stddev_sq (FpiDeviceElanSpi *self, guint16 *frame1, guint16 *frame2) | |
1364 | { | ||
1365 | 22 | gint64 mean = 0; | |
1366 | 22 | gint64 sq_stddev = 0; | |
1367 | |||
1368 |
2/2✓ Branch 0 taken 202752 times.
✓ Branch 1 taken 22 times.
|
202774 | for (int j = 0; j < (self->sensor_height * self->sensor_width); j += 1) |
1369 | 202752 | mean += abs ((int) frame1[j] - (int) frame2[j]); | |
1370 | |||
1371 |
2/4✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
|
22 | g_assert (self->sensor_height && self->sensor_width); /* make clang happy about div0 */ |
1372 | 22 | mean /= (self->sensor_height * self->sensor_width); | |
1373 | |||
1374 |
2/2✓ Branch 0 taken 202752 times.
✓ Branch 1 taken 22 times.
|
202774 | for (int j = 0; j < (self->sensor_height * self->sensor_width); j += 1) |
1375 | { | ||
1376 | 202752 | gint64 k = abs ((int) frame1[j] - (int) frame2[j]) - mean; | |
1377 | 202752 | sq_stddev += k * k; | |
1378 | } | ||
1379 | |||
1380 | 22 | sq_stddev /= (self->sensor_height * self->sensor_width); | |
1381 | |||
1382 | 22 | return sq_stddev; | |
1383 | } | ||
1384 | |||
1385 | static void | ||
1386 | 24 | elanspi_fp_frame_handler (FpiSsm *ssm, FpiDeviceElanSpi *self) | |
1387 | { | ||
1388 | 48 | g_autofree struct fpi_frame *this_frame = NULL; | |
1389 | |||
1390 |
1/4✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
|
24 | switch (elanspi_guess_image (self, self->last_image)) |
1391 | { | ||
1392 | ✗ | case ELANSPI_GUESS_UNKNOWN: | |
1393 | ✗ | fp_dbg ("<fp_frame> unknown, ignore..."); | |
1394 | ✗ | break; | |
1395 | |||
1396 | ✗ | case ELANSPI_GUESS_EMPTY: | |
1397 | ✗ | self->fp_empty_counter += 1; | |
1398 | ✗ | fp_dbg ("<fp_frame> got empty"); | |
1399 | ✗ | if (self->fp_empty_counter > 1) | |
1400 | { | ||
1401 | ✗ | fp_dbg ("<fp_frame> have enough debounce"); | |
1402 | ✗ | if (g_slist_length (self->fp_frame_list) >= ELANSPI_MIN_FRAMES_SWIPE) | |
1403 | { | ||
1404 | ✗ | fp_dbg ("<fp_frame> have enough frames, submitting"); | |
1405 | ✗ | elanspi_fp_frame_stitch_and_submit (self); | |
1406 | } | ||
1407 | else | ||
1408 | { | ||
1409 | ✗ | fp_dbg ("<fp_frame> not enough frames, reporting short swipe"); | |
1410 | ✗ | fpi_image_device_retry_scan (FP_IMAGE_DEVICE (self), FP_DEVICE_RETRY_TOO_SHORT); | |
1411 | } | ||
1412 | ✗ | goto finish_capture; | |
1413 | } | ||
1414 | break; | ||
1415 | |||
1416 | 24 | case ELANSPI_GUESS_FINGERPRINT: | |
1417 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
24 | if (self->fp_empty_counter && self->fp_frame_list) |
1418 | { | ||
1419 | ✗ | if (self->fp_empty_counter < 1) | |
1420 | { | ||
1421 | ✗ | fp_dbg ("<fp_frame> possible bounced fp"); | |
1422 | ✗ | break; | |
1423 | } | ||
1424 | else | ||
1425 | { | ||
1426 | ✗ | fp_dbg ("<fp_frame> too many empties, clearing list"); | |
1427 | ✗ | g_slist_free_full (g_steal_pointer (&self->fp_frame_list), g_free); | |
1428 | ✗ | self->fp_empty_counter = 0; | |
1429 | } | ||
1430 | } | ||
1431 | |||
1432 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 23 times.
|
24 | if (g_slist_length (self->fp_frame_list) > ELANSPI_MAX_FRAMES_SWIPE) |
1433 | { | ||
1434 | 1 | fp_dbg ("<fp_frame> have enough frames, exiting now"); | |
1435 | 1 | elanspi_fp_frame_stitch_and_submit (self); | |
1436 | 1 | goto finish_capture; | |
1437 | } | ||
1438 | |||
1439 | /* append image */ | ||
1440 | 23 | this_frame = g_malloc0 (self->sensor_height * self->sensor_width + sizeof (struct fpi_frame)); | |
1441 | 23 | elanspi_correct_with_bg (self, self->last_image); | |
1442 | 23 | elanspi_process_frame (self, self->last_image, this_frame->data); | |
1443 | |||
1444 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 1 times.
|
23 | if (self->fp_frame_list) |
1445 | { | ||
1446 | 22 | gint difference = elanspi_get_frame_diff_stddev_sq (self, self->last_image, self->prev_frame_image); | |
1447 | 22 | fp_dbg ("<fp_frame> diff = %d", difference); | |
1448 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
|
22 | if (difference < ELANSPI_MIN_FRAME_TO_FRAME_DIFF) |
1449 | { | ||
1450 | 1 | fp_dbg ("<fp_frame> ignoring b.c. difference is too small"); | |
1451 | 1 | break; | |
1452 | } | ||
1453 | } | ||
1454 | 22 | self->fp_frame_list = g_slist_prepend (self->fp_frame_list, g_steal_pointer (&this_frame)); | |
1455 | 22 | memcpy (self->prev_frame_image, self->last_image, self->sensor_height * self->sensor_width * 2); | |
1456 | 22 | break; | |
1457 | } | ||
1458 | |||
1459 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
|
23 | if (self->sensor_id == 0xe) |
1460 | ✗ | fpi_ssm_jump_to_state_delayed (ssm, ELANSPI_FPCAPT_FP_CAPTURE, ELANSPI_HV_SENSOR_FRAME_DELAY); | |
1461 | else | ||
1462 | 23 | fpi_ssm_jump_to_state (ssm, ELANSPI_FPCAPT_FP_CAPTURE); | |
1463 | |||
1464 | return; | ||
1465 | |||
1466 | 1 | finish_capture: | |
1467 | /* prepare for wait up */ | ||
1468 | 1 | self->finger_wait_debounce = 0; | |
1469 | 1 | fpi_ssm_jump_to_state (ssm, ELANSPI_FPCAPT_WAITUP_CAPTURE); | |
1470 | 1 | return; | |
1471 | |||
1472 | } | ||
1473 | |||
1474 | static void | ||
1475 | 147 | elanspi_fp_capture_ssm_handler (FpiSsm *ssm, FpDevice *dev) | |
1476 | { | ||
1477 | 147 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
1478 | 147 | FpiSsm *chld = NULL; | |
1479 | |||
1480 |
5/6✓ Branch 1 taken 1 times.
✓ Branch 2 taken 73 times.
✓ Branch 3 taken 27 times.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 22 times.
✗ Branch 6 not taken.
|
147 | switch (fpi_ssm_get_cur_state (ssm)) |
1481 | { | ||
1482 | 1 | case ELANSPI_FPCAPT_INIT: | |
1483 | 1 | self->finger_wait_debounce = 0; | |
1484 | |||
1485 | 1 | fpi_ssm_next_state (ssm); | |
1486 | 1 | return; | |
1487 | |||
1488 | 73 | case ELANSPI_FPCAPT_WAITDOWN_CAPTURE: | |
1489 | case ELANSPI_FPCAPT_WAITUP_CAPTURE: | ||
1490 | case ELANSPI_FPCAPT_FP_CAPTURE: | ||
1491 | /* check if we are deactivating */ | ||
1492 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
|
73 | if (self->deactivating) |
1493 | { | ||
1494 | ✗ | fp_dbg ("<capture> got deactivate; exiting"); | |
1495 | |||
1496 | ✗ | self->deactivating = FALSE; | |
1497 | ✗ | fpi_ssm_mark_completed (ssm); | |
1498 | |||
1499 | /* mark deactivate done */ | ||
1500 | ✗ | fpi_image_device_deactivate_complete (FP_IMAGE_DEVICE (dev), NULL); | |
1501 | |||
1502 | ✗ | return; | |
1503 | } | ||
1504 | /* if sensor is hv */ | ||
1505 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
|
73 | if (self->sensor_id == 0xe) |
1506 | ✗ | chld = fpi_ssm_new (dev, elanspi_capture_hv_handler, ELANSPI_CAPTHV_NSTATES); | |
1507 | else | ||
1508 | 73 | chld = fpi_ssm_new (dev, elanspi_capture_old_handler, ELANSPI_CAPTOLD_NSTATES); | |
1509 | 73 | fpi_ssm_silence_debug (chld); | |
1510 | 73 | fpi_ssm_start_subsm (ssm, chld); | |
1511 | 73 | return; | |
1512 | |||
1513 | 27 | case ELANSPI_FPCAPT_WAITDOWN_PROCESS: | |
1514 |
2/2✓ Branch 1 taken 26 times.
✓ Branch 2 taken 1 times.
|
27 | if (!elanspi_check_waitupdown_done (self, ELANSPI_GUESS_FINGERPRINT)) |
1515 | { | ||
1516 | /* take another image */ | ||
1517 | 26 | fpi_ssm_jump_to_state (ssm, ELANSPI_FPCAPT_WAITDOWN_CAPTURE); | |
1518 | 26 | return; | |
1519 | } | ||
1520 | |||
1521 | /* prepare to take actual image */ | ||
1522 | 1 | self->finger_wait_debounce = 0; | |
1523 | 1 | g_slist_free_full (g_steal_pointer (&self->fp_frame_list), g_free); | |
1524 | 1 | self->fp_empty_counter = 0; | |
1525 | |||
1526 | /* report finger status */ | ||
1527 | 1 | fpi_image_device_report_finger_status (FP_IMAGE_DEVICE (self), TRUE); | |
1528 | |||
1529 | /* jump */ | ||
1530 | 1 | fpi_ssm_jump_to_state (ssm, ELANSPI_FPCAPT_FP_CAPTURE); | |
1531 | 1 | return; | |
1532 | |||
1533 | 24 | case ELANSPI_FPCAPT_FP_PROCESS: | |
1534 | 24 | elanspi_fp_frame_handler (ssm, self); | |
1535 | 24 | return; | |
1536 | |||
1537 | 22 | case ELANSPI_FPCAPT_WAITUP_PROCESS: | |
1538 |
2/2✓ Branch 1 taken 21 times.
✓ Branch 2 taken 1 times.
|
22 | if (!elanspi_check_waitupdown_done (self, ELANSPI_GUESS_EMPTY)) |
1539 | { | ||
1540 | /* take another image */ | ||
1541 | 21 | fpi_ssm_jump_to_state (ssm, ELANSPI_FPCAPT_WAITUP_CAPTURE); | |
1542 | 21 | return; | |
1543 | } | ||
1544 | |||
1545 | /* Immediately set capturing to FALSE so that when report_finger_status tries to re-start | ||
1546 | * capturing in enroll we don't hit the assert since the old SSM is about to stop. */ | ||
1547 | 1 | self->capturing = FALSE; | |
1548 | 1 | fpi_image_device_report_finger_status (FP_IMAGE_DEVICE (self), FALSE); | |
1549 | |||
1550 | /* finish */ | ||
1551 | 1 | fpi_ssm_mark_completed (ssm); | |
1552 | 1 | return; | |
1553 | } | ||
1554 | } | ||
1555 | |||
1556 | static void | ||
1557 | 1 | elanspi_open (FpImageDevice *dev) | |
1558 | { | ||
1559 | 1 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
1560 | 1 | GError *err = NULL; | |
1561 | |||
1562 | 1 | G_DEBUG_HERE (); | |
1563 | |||
1564 | 1 | int spi_fd = open (fpi_device_get_udev_data (FP_DEVICE (dev), FPI_DEVICE_UDEV_SUBTYPE_SPIDEV), O_RDWR); | |
1565 | |||
1566 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (spi_fd < 0) |
1567 | { | ||
1568 | ✗ | g_set_error (&err, G_IO_ERROR, g_io_error_from_errno (errno), "unable to open spi"); | |
1569 | ✗ | fpi_image_device_open_complete (dev, err); | |
1570 | ✗ | return; | |
1571 | } | ||
1572 | |||
1573 | 1 | self->spi_fd = spi_fd; | |
1574 | |||
1575 | 1 | fpi_image_device_open_complete (dev, NULL); | |
1576 | } | ||
1577 | |||
1578 | static void | ||
1579 | 1 | elanspi_close (FpImageDevice *dev) | |
1580 | { | ||
1581 | 1 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
1582 | |||
1583 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (self->spi_fd >= 0) |
1584 | { | ||
1585 | 1 | close (self->spi_fd); | |
1586 | 1 | self->spi_fd = -1; | |
1587 | } | ||
1588 | 1 | fpi_image_device_close_complete (dev, NULL); | |
1589 | 1 | } | |
1590 | |||
1591 | static void | ||
1592 | 1 | elanspi_init_finish (FpiSsm *ssm, FpDevice *dev, GError *error) | |
1593 | { | ||
1594 | 1 | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
1595 | |||
1596 | 1 | G_DEBUG_HERE (); | |
1597 | 1 | fpi_image_device_activate_complete (idev, error); | |
1598 | 1 | } | |
1599 | |||
1600 | static void | ||
1601 | 1 | elanspi_activate (FpImageDevice *dev) | |
1602 | { | ||
1603 | 1 | FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (dev), elanspi_init_ssm_handler, ELANSPI_INIT_NSTATES); | |
1604 | |||
1605 | 1 | fpi_ssm_start (ssm, elanspi_init_finish); | |
1606 | 1 | } | |
1607 | |||
1608 | static void | ||
1609 | 1 | elanspi_deactivate (FpImageDevice *dev) | |
1610 | { | ||
1611 | 1 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
1612 | |||
1613 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (self->capturing) |
1614 | { | ||
1615 | ✗ | self->deactivating = TRUE; | |
1616 | ✗ | fp_dbg ("<deactivate> waiting capture to stop"); | |
1617 | } | ||
1618 | else | ||
1619 | { | ||
1620 | 1 | fpi_image_device_deactivate_complete (dev, NULL); | |
1621 | } | ||
1622 | 1 | } | |
1623 | |||
1624 | static void | ||
1625 | 1 | elanspi_fp_capture_finish (FpiSsm *ssm, FpDevice *dev, GError *error) | |
1626 | { | ||
1627 | 1 | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
1628 | 1 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
1629 | |||
1630 | 1 | self->capturing = FALSE; | |
1631 | |||
1632 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (self->deactivating) |
1633 | { | ||
1634 | /* finish deactivate */ | ||
1635 | ✗ | if (error) | |
1636 | ✗ | g_error_free (error); | |
1637 | ✗ | self->deactivating = FALSE; | |
1638 | ✗ | fpi_image_device_deactivate_complete (idev, NULL); | |
1639 | ✗ | return; | |
1640 | } | ||
1641 | |||
1642 | /* if there was an error, report it */ | ||
1643 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (error) |
1644 | ✗ | fpi_image_device_session_error (idev, error); | |
1645 | } | ||
1646 | |||
1647 | static void | ||
1648 | 8 | elanspi_change_state (FpImageDevice *dev, FpiImageDeviceState state) | |
1649 | { | ||
1650 | 8 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev); | |
1651 | |||
1652 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
|
8 | if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON) |
1653 | { | ||
1654 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | g_assert (self->capturing == FALSE); |
1655 | |||
1656 | /* start capturer */ | ||
1657 | 1 | self->capturing = TRUE; | |
1658 | 1 | fpi_ssm_start (fpi_ssm_new (FP_DEVICE (dev), | |
1659 | elanspi_fp_capture_ssm_handler, | ||
1660 | ELANSPI_FPCAPT_NSTATES), | ||
1661 | elanspi_fp_capture_finish); | ||
1662 | |||
1663 | 1 | fp_dbg ("<change_state> started capturer"); | |
1664 | } | ||
1665 | else | ||
1666 | { | ||
1667 | /* todo: other states? */ | ||
1668 | 8 | } | |
1669 | 8 | } | |
1670 | |||
1671 | static void | ||
1672 | 1 | fpi_device_elanspi_init (FpiDeviceElanSpi *self) | |
1673 | { | ||
1674 | 1 | self->spi_fd = -1; | |
1675 | 1 | self->sensor_id = 0xff; | |
1676 | 1 | } | |
1677 | |||
1678 | static void | ||
1679 | 1 | fpi_device_elanspi_finalize (GObject *this) | |
1680 | { | ||
1681 | 1 | FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (this); | |
1682 | |||
1683 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | g_clear_pointer (&self->bg_image, g_free); |
1684 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | g_clear_pointer (&self->last_image, g_free); |
1685 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | g_clear_pointer (&self->prev_frame_image, g_free); |
1686 | 1 | g_slist_free_full (g_steal_pointer (&self->fp_frame_list), g_free); | |
1687 | |||
1688 | 1 | G_OBJECT_CLASS (fpi_device_elanspi_parent_class)->finalize (this); | |
1689 | 1 | } | |
1690 | |||
1691 | static void | ||
1692 | 120 | fpi_device_elanspi_class_init (FpiDeviceElanSpiClass *klass) | |
1693 | { | ||
1694 | 120 | FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); | |
1695 | 120 | FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass); | |
1696 | |||
1697 | 120 | dev_class->id = "elanspi"; | |
1698 | 120 | dev_class->full_name = "ElanTech Embedded Fingerprint Sensor"; | |
1699 | 120 | dev_class->type = FP_DEVICE_TYPE_UDEV; | |
1700 | 120 | dev_class->id_table = elanspi_id_table; | |
1701 | 120 | dev_class->scan_type = FP_SCAN_TYPE_SWIPE; | |
1702 | 120 | dev_class->nr_enroll_stages = 7; /* these sensors are very hit or miss, may as well record a few extras */ | |
1703 | |||
1704 | 120 | img_class->bz3_threshold = 24; | |
1705 | 120 | img_class->img_open = elanspi_open; | |
1706 | 120 | img_class->activate = elanspi_activate; | |
1707 | 120 | img_class->deactivate = elanspi_deactivate; | |
1708 | 120 | img_class->change_state = elanspi_change_state; | |
1709 | 120 | img_class->img_close = elanspi_close; | |
1710 | |||
1711 | 120 | G_OBJECT_CLASS (klass)->finalize = fpi_device_elanspi_finalize; | |
1712 | } | ||
1713 |