Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * EgisTec ES603 driver for libfprint | ||
3 | * Copyright (C) 2012 Patrick Marlier | ||
4 | * | ||
5 | * This library is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; either | ||
8 | * version 2.1 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This library is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this library; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | /* EgisTec ES603 device information | ||
21 | * Sensor area: 192 x 4 pixels | ||
22 | * Sensor gray: 16 gray levels/sensor pixel | ||
23 | * Sensor resolution: 508 dpi | ||
24 | * USB Manufacturer ID: 1C7A | ||
25 | * USB Product ID: 0603 | ||
26 | * | ||
27 | * Possible compatibility LTT-SS500/SS501 | ||
28 | * | ||
29 | * Extra features not present in this driver (see https://code.google.com/p/etes603): | ||
30 | * Tuning of DTVRT for contact detection | ||
31 | * Contact detection via capacitance | ||
32 | * Capture mode using assembled frames (usually better quality) | ||
33 | * | ||
34 | */ | ||
35 | |||
36 | #define FP_COMPONENT "etes603" | ||
37 | |||
38 | #include "drivers_api.h" | ||
39 | |||
40 | /* libusb defines */ | ||
41 | #define EP_IN 0x81 | ||
42 | #define EP_OUT 0x02 | ||
43 | /* Note that 1000 ms is usually enough but with CMD_READ_FE could be longer | ||
44 | * since the sensor is waiting motion. */ | ||
45 | #define BULK_TIMEOUT 1000 | ||
46 | |||
47 | /* es603 defines */ | ||
48 | #define FRAME_WIDTH 192 /* pixels per row */ | ||
49 | #define FRAME_HEIGHT 4 /* number of rows */ | ||
50 | #define FRAME_SIZE 384 /* size in bytes (4 bits per pixels) */ | ||
51 | #define FE_WIDTH 256 /* pixels per row for Fly-Estimation */ | ||
52 | #define FE_HEIGHT 500 /* number of rows for Fly-Estimation */ | ||
53 | #define FE_SIZE 64000 /* size in bytes (4 bits per pixels) */ | ||
54 | |||
55 | #define GAIN_SMALL_INIT 0x23 /* Initial small gain */ | ||
56 | #define VRT_MAX 0x3F /* Maximum value for VRT */ | ||
57 | #define VRB_MAX 0x3A /* Maximum value for VRB */ | ||
58 | #define DTVRT_MAX 0x3A /* Maximum value for DTVRT */ | ||
59 | #define DCOFFSET_MIN 0x00 /* Minimum value for DCoffset */ | ||
60 | #define DCOFFSET_MAX 0x35 /* Maximum value for DCoffset */ | ||
61 | |||
62 | /* es603 commands */ | ||
63 | #define CMD_READ_REG 0x01 | ||
64 | #define CMD_WRITE_REG 0x02 | ||
65 | #define CMD_READ_FRAME 0x03 /* Read the sensor area */ | ||
66 | #define CMD_READ_FE 0x06 /* Read a fingerprint using Fly-Estimation */ | ||
67 | #define CMD_20 0x20 /* ? */ | ||
68 | #define CMD_25 0x25 /* ? */ | ||
69 | #define CMD_60 0x60 /* ? */ | ||
70 | |||
71 | #define CMD_OK 0x01 /* Command successfully executed */ | ||
72 | |||
73 | /* es603 registers */ | ||
74 | #define REG_MAX 0x18 /* Maximum number of registers in one message */ | ||
75 | #define REG_MODE_CONTROL 0x02 /* Mode control */ | ||
76 | #define REG_03 0x03 /* Contact register? */ | ||
77 | #define REG_04 0x04 /* ? */ | ||
78 | #define REG_10 0x10 /* MVS FRMBUF control */ | ||
79 | #define REG_1A 0x1A /* ? */ | ||
80 | /* BEGIN init sensor */ | ||
81 | #define REG_20 0x20 /* (def: 0x00) */ | ||
82 | #define REG_21 0x21 /* Small gain (def: 0x23) */ | ||
83 | #define REG_22 0x22 /* Normal gain (def: 0x21) */ | ||
84 | #define REG_23 0x23 /* Large gain (def: 0x20) */ | ||
85 | #define REG_24 0x24 /* (def: 0x14) */ | ||
86 | #define REG_25 0x25 /* (def: 0x6A) */ | ||
87 | #define REG_26 0x26 /* VRB again? (def: 0x00) */ | ||
88 | #define REG_27 0x27 /* VRT again? (def: 0x00) */ | ||
89 | #define REG_28 0x28 /* (def: 0x00) */ | ||
90 | #define REG_29 0x29 /* (def: 0xC0) */ | ||
91 | #define REG_2A 0x2A /* (def: 0x50) */ | ||
92 | #define REG_2B 0x2B /* (def: 0x50) */ | ||
93 | #define REG_2C 0x2C /* (def: 0x4D) */ | ||
94 | #define REG_2D 0x2D /* (def: 0x03) */ | ||
95 | #define REG_2E 0x2E /* (def: 0x06) */ | ||
96 | #define REG_2F 0x2F /* (def: 0x06) */ | ||
97 | #define REG_30 0x30 /* (def: 0x10) */ | ||
98 | #define REG_31 0x31 /* (def: 0x02) */ | ||
99 | #define REG_32 0x32 /* (def: 0x14) */ | ||
100 | #define REG_33 0x33 /* (def: 0x34) */ | ||
101 | #define REG_34 0x34 /* (def: 0x01) */ | ||
102 | #define REG_35 0x35 /* (def: 0x08) */ | ||
103 | #define REG_36 0x36 /* (def: 0x03) */ | ||
104 | #define REG_37 0x37 /* (def: 0x21) */ | ||
105 | /* END init sensor */ | ||
106 | |||
107 | #define REG_ENC1 0x41 /* Encryption 1 */ | ||
108 | #define REG_ENC2 0x42 | ||
109 | #define REG_ENC3 0x43 | ||
110 | #define REG_ENC4 0x44 | ||
111 | #define REG_ENC5 0x45 | ||
112 | #define REG_ENC6 0x46 | ||
113 | #define REG_ENC7 0x47 | ||
114 | #define REG_ENC8 0x48 /* Encryption 8 */ | ||
115 | |||
116 | #define REG_50 0x50 /* ? For contact detection */ | ||
117 | #define REG_51 0x51 /* ? */ | ||
118 | #define REG_59 0x59 /* ? */ | ||
119 | #define REG_5A 0x5A /* ? */ | ||
120 | #define REG_5B 0x5B /* ? */ | ||
121 | |||
122 | #define REG_INFO0 0x70 /* Sensor model byte0 */ | ||
123 | #define REG_INFO1 0x71 /* Sensor model byte1 */ | ||
124 | #define REG_INFO2 0x72 /* Sensor model byte2 */ | ||
125 | #define REG_INFO3 0x73 /* Sensor model byte3 */ | ||
126 | |||
127 | #define REG_GAIN 0xE0 | ||
128 | #define REG_VRT 0xE1 | ||
129 | #define REG_VRB 0xE2 | ||
130 | #define REG_DTVRT 0xE3 /* used for contact detection */ | ||
131 | #define REG_VCO_CONTROL 0xE5 /* 0x13 (IDLE?), 0x14 (REALTIME) */ | ||
132 | #define REG_DCOFFSET 0xE6 | ||
133 | |||
134 | #define REG_F0 0xF0 /* ? init:0x00 close:0x01 */ | ||
135 | #define REG_F2 0xF2 /* ? init:0x00 close:0x4E */ | ||
136 | |||
137 | #define REG_MODE_SLEEP 0x30 /* Sleep mode */ | ||
138 | #define REG_MODE_CONTACT 0x31 /* Contact mode */ | ||
139 | #define REG_MODE_SENSOR 0x33 /* Sensor mode */ | ||
140 | #define REG_MODE_FP 0x34 /* FingerPrint mode (Fly-Estimation®) */ | ||
141 | |||
142 | #define REG_VCO_IDLE 0x13 | ||
143 | #define REG_VCO_RT 0x14 /* Realtime */ | ||
144 | |||
145 | /* The size of the message header is 5 plus 1 for the command. */ | ||
146 | #define MSG_HDR_SIZE 6 | ||
147 | |||
148 | /* This structure must be packed because it is a the raw message sent. */ | ||
149 | struct egis_msg | ||
150 | { | ||
151 | guint8 magic[5]; /* out: 'EGIS' 0x09 / in: 'SIGE' 0x0A */ | ||
152 | guint8 cmd; | ||
153 | union | ||
154 | { | ||
155 | struct | ||
156 | { | ||
157 | guint8 nb; | ||
158 | guint8 regs[REG_MAX]; | ||
159 | } egis_readreg; | ||
160 | struct | ||
161 | { | ||
162 | guint8 regs[REG_MAX]; | ||
163 | } sige_readreg; | ||
164 | struct | ||
165 | { | ||
166 | guint8 nb; | ||
167 | struct | ||
168 | { | ||
169 | guint8 reg; | ||
170 | guint8 val; | ||
171 | } regs[REG_MAX]; | ||
172 | } egis_writereg; | ||
173 | struct | ||
174 | { | ||
175 | guint8 length_factor; | ||
176 | guint8 length; | ||
177 | guint8 use_gvv; | ||
178 | guint8 gain; | ||
179 | guint8 vrt; | ||
180 | guint8 vrb; | ||
181 | } egis_readf; | ||
182 | struct | ||
183 | { | ||
184 | guint8 len[2]; | ||
185 | guint8 val[3]; | ||
186 | } egis_readfp; | ||
187 | struct | ||
188 | { | ||
189 | guint8 val[5]; | ||
190 | } sige_misc; | ||
191 | guint8 padding[0x40 - 6]; /* Ensure size of 0x40 */ | ||
192 | }; | ||
193 | } __attribute__((packed)); | ||
194 | |||
195 | |||
196 | /* Structure to keep information between asynchronous functions. */ | ||
197 | struct _FpiDeviceEtes603 | ||
198 | { | ||
199 | FpImageDevice parent; | ||
200 | |||
201 | guint8 regs[256]; | ||
202 | struct egis_msg *req; | ||
203 | size_t req_len; | ||
204 | struct egis_msg *ans; | ||
205 | size_t ans_len; | ||
206 | |||
207 | guint8 *fp; | ||
208 | guint16 fp_height; | ||
209 | |||
210 | guint8 tunedc_min; | ||
211 | guint8 tunedc_max; | ||
212 | |||
213 | /* Device parameters */ | ||
214 | guint8 gain; | ||
215 | guint8 dcoffset; | ||
216 | guint8 vrt; | ||
217 | guint8 vrb; | ||
218 | |||
219 | unsigned int is_active; | ||
220 | }; | ||
221 | G_DECLARE_FINAL_TYPE (FpiDeviceEtes603, fpi_device_etes603, FPI, DEVICE_ETES603, | ||
222 | FpImageDevice); | ||
223 |
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 (FpiDeviceEtes603, fpi_device_etes603, FP_TYPE_IMAGE_DEVICE); |
224 | |||
225 | static void m_start_fingerdetect (FpImageDevice *idev); | ||
226 | /* | ||
227 | * Prepare the header of the message to be sent to the device. | ||
228 | */ | ||
229 | static void | ||
230 | ✗ | msg_header_prepare (struct egis_msg *msg) | |
231 | { | ||
232 | ✗ | msg->magic[0] = 'E'; | |
233 | ✗ | msg->magic[1] = 'G'; | |
234 | ✗ | msg->magic[2] = 'I'; | |
235 | ✗ | msg->magic[3] = 'S'; | |
236 | ✗ | msg->magic[4] = 0x09; | |
237 | } | ||
238 | |||
239 | /* | ||
240 | * Check that the header of the received message is correct. | ||
241 | */ | ||
242 | static int | ||
243 | ✗ | msg_header_check (struct egis_msg *msg) | |
244 | { | ||
245 | ✗ | if (msg->magic[0] == 'S' && msg->magic[1] == 'I' && | |
246 | ✗ | msg->magic[2] == 'G' && msg->magic[3] == 'E' && | |
247 | ✗ | msg->magic[4] == 0x0A) | |
248 | ✗ | return 0; | |
249 | return -1; | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * Prepare message to ask for a frame. | ||
254 | */ | ||
255 | static void | ||
256 | ✗ | msg_get_frame (FpiDeviceEtes603 *self, | |
257 | guint8 use_gvv, guint8 gain, guint8 vrt, | ||
258 | guint8 vrb) | ||
259 | { | ||
260 | ✗ | struct egis_msg *msg = self->req; | |
261 | |||
262 | ✗ | msg_header_prepare (msg); | |
263 | ✗ | msg->cmd = CMD_READ_FRAME; | |
264 | ✗ | msg->egis_readf.length_factor = 0x01; | |
265 | /* length should be 0xC0 */ | ||
266 | ✗ | msg->egis_readf.length = FRAME_WIDTH; | |
267 | ✗ | msg->egis_readf.use_gvv = use_gvv; | |
268 | /* if use_gvv is set, gain/vrt/vrb are used */ | ||
269 | ✗ | msg->egis_readf.gain = gain; | |
270 | ✗ | msg->egis_readf.vrt = vrt; | |
271 | ✗ | msg->egis_readf.vrb = vrb; | |
272 | |||
273 | ✗ | self->req_len = MSG_HDR_SIZE + 6; | |
274 | ✗ | self->ans_len = FRAME_SIZE; | |
275 | ✗ | } | |
276 | |||
277 | /* | ||
278 | * Prepare message to ask for a fingerprint frame. | ||
279 | */ | ||
280 | static void | ||
281 | ✗ | msg_get_fp (FpiDeviceEtes603 *self, guint8 len0, guint8 len1, | |
282 | guint8 v2, guint8 v3, guint8 v4) | ||
283 | { | ||
284 | ✗ | struct egis_msg *msg = self->req; | |
285 | |||
286 | ✗ | msg_header_prepare (msg); | |
287 | ✗ | msg->cmd = CMD_READ_FE; | |
288 | /* Unknown values and always same on captured frames. | ||
289 | * 1st 2nd bytes is unsigned short for height, but only on value range | ||
290 | * 0x01 0xF4 (500), 0x02 0x00 (512), 0x02 0xF4 (756) are ok | ||
291 | */ | ||
292 | ✗ | msg->egis_readfp.len[0] = len0; | |
293 | ✗ | msg->egis_readfp.len[1] = len1; | |
294 | /* 3rd byte : ?? but changes frame size | ||
295 | * 4th byte : 0x00 -> can change width | ||
296 | * 5th byte : motion sensibility? | ||
297 | */ | ||
298 | ✗ | msg->egis_readfp.val[0] = v2; | |
299 | ✗ | msg->egis_readfp.val[1] = v3; | |
300 | ✗ | msg->egis_readfp.val[2] = v4; | |
301 | |||
302 | ✗ | self->req_len = MSG_HDR_SIZE + 5; | |
303 | ✗ | self->ans_len = FE_SIZE; | |
304 | } | ||
305 | |||
306 | /* | ||
307 | * Prepare message to read registers from the sensor. | ||
308 | * Variadic argument pattern: int reg, ... | ||
309 | */ | ||
310 | static void | ||
311 | ✗ | msg_get_regs (FpiDeviceEtes603 *self, int n_args, ... ) | |
312 | { | ||
313 | ✗ | struct egis_msg *msg = self->req; | |
314 | ✗ | va_list ap; | |
315 | ✗ | int i; | |
316 | |||
317 | ✗ | g_assert (n_args > 0 && n_args <= REG_MAX); | |
318 | |||
319 | ✗ | msg_header_prepare (msg); | |
320 | ✗ | msg->cmd = CMD_READ_REG; | |
321 | ✗ | msg->egis_readreg.nb = n_args; | |
322 | ✗ | va_start (ap, n_args); | |
323 | ✗ | for (i = 0; i < n_args; i++) | |
324 | ✗ | msg->egis_readreg.regs[i] = va_arg (ap, int); | |
325 | ✗ | va_end (ap); | |
326 | |||
327 | ✗ | self->req_len = MSG_HDR_SIZE + 1 + n_args; | |
328 | ✗ | self->ans_len = MSG_HDR_SIZE + 1 + n_args; | |
329 | ✗ | } | |
330 | |||
331 | /* | ||
332 | * Parse the result of read register command. | ||
333 | */ | ||
334 | static int | ||
335 | ✗ | msg_parse_regs (FpiDeviceEtes603 *dev) | |
336 | { | ||
337 | ✗ | size_t i, n_args; | |
338 | ✗ | struct egis_msg *msg_req = dev->req; | |
339 | ✗ | struct egis_msg *msg_ans = dev->ans; | |
340 | |||
341 | ✗ | n_args = dev->ans_len - MSG_HDR_SIZE; | |
342 | |||
343 | ✗ | if (msg_header_check (msg_ans)) | |
344 | return -1; | ||
345 | ✗ | if (msg_ans->cmd != CMD_OK) | |
346 | return -2; | ||
347 | |||
348 | ✗ | for (i = 0; i < n_args; i++) | |
349 | { | ||
350 | ✗ | int reg = msg_req->egis_readreg.regs[i]; | |
351 | ✗ | dev->regs[reg] = msg_ans->sige_readreg.regs[i]; | |
352 | } | ||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | /* | ||
357 | * Prepare message to write sensor's registers. | ||
358 | * Variadic arguments are: int reg, int val, ... | ||
359 | */ | ||
360 | static void | ||
361 | ✗ | msg_set_regs (FpiDeviceEtes603 *self, int n_args, ...) | |
362 | { | ||
363 | ✗ | struct egis_msg *msg = self->req; | |
364 | ✗ | va_list ap; | |
365 | ✗ | int i; | |
366 | |||
367 | ✗ | g_assert (n_args != 0 && n_args % 2 == 0 && n_args <= REG_MAX * 2); | |
368 | |||
369 | ✗ | msg_header_prepare (msg); | |
370 | ✗ | msg->cmd = CMD_WRITE_REG; | |
371 | ✗ | msg->egis_writereg.nb = n_args / 2; | |
372 | |||
373 | ✗ | va_start (ap, n_args); | |
374 | ✗ | for (i = 0; i < n_args / 2; i++) | |
375 | { | ||
376 | ✗ | msg->egis_writereg.regs[i].reg = va_arg (ap, int); | |
377 | ✗ | msg->egis_writereg.regs[i].val = va_arg (ap, int); | |
378 | } | ||
379 | ✗ | va_end (ap); | |
380 | |||
381 | ✗ | self->req_len = MSG_HDR_SIZE + 1 + n_args; | |
382 | ✗ | self->ans_len = MSG_HDR_SIZE + 1; | |
383 | ✗ | } | |
384 | |||
385 | static int | ||
386 | ✗ | msg_check_ok (FpiDeviceEtes603 *dev) | |
387 | { | ||
388 | ✗ | struct egis_msg *msg = dev->ans; | |
389 | |||
390 | ✗ | if (msg_header_check (msg)) | |
391 | ✗ | goto err; | |
392 | ✗ | if (msg->cmd != CMD_OK) | |
393 | ✗ | goto err; | |
394 | return 0; | ||
395 | ✗ | err: | |
396 | return -1; | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * Check the model of the sensor. | ||
401 | */ | ||
402 | static int | ||
403 | ✗ | check_info (FpiDeviceEtes603 *dev) | |
404 | { | ||
405 | ✗ | if (dev->regs[0x70] == 0x4A && dev->regs[0x71] == 0x44 && | |
406 | ✗ | dev->regs[0x72] == 0x49 && dev->regs[0x73] == 0x31) | |
407 | return 0; | ||
408 | ✗ | fp_err ("unknown device parameters (REG_70:%02X REG_71:%02X " | |
409 | "REG_FIRMWARE:%02X REG_VERSION:%02X)", | ||
410 | dev->regs[0x70], dev->regs[0x71], dev->regs[0x72], | ||
411 | dev->regs[0x73]); | ||
412 | ✗ | return -1; | |
413 | } | ||
414 | |||
415 | static void | ||
416 | ✗ | msg_get_cmd20 (FpiDeviceEtes603 *dev) | |
417 | { | ||
418 | ✗ | struct egis_msg *msg = dev->req; | |
419 | |||
420 | ✗ | msg_header_prepare (msg); | |
421 | ✗ | msg->cmd = CMD_20; | |
422 | ✗ | dev->req_len = MSG_HDR_SIZE; | |
423 | } | ||
424 | |||
425 | static int | ||
426 | ✗ | msg_check_cmd20 (FpiDeviceEtes603 *dev) | |
427 | { | ||
428 | ✗ | struct egis_msg *msg = dev->ans; | |
429 | |||
430 | ✗ | if (msg_header_check (msg)) | |
431 | { | ||
432 | ✗ | fp_err ("msg_header_check failed"); | |
433 | ✗ | return -1; | |
434 | } | ||
435 | /* status or flashtype/flashinfo or ? */ | ||
436 | ✗ | if (msg->cmd != 0x05 || | |
437 | ✗ | msg->sige_misc.val[0] != 0x00 || | |
438 | ✗ | msg->sige_misc.val[1] != 0x00) | |
439 | ✗ | fp_warn ("unexpected answer CMD_20 from device(%02X %02X %02X)", | |
440 | msg->cmd, msg->sige_misc.val[0], msg->sige_misc.val[1]); | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static void | ||
446 | ✗ | msg_get_cmd25 (FpiDeviceEtes603 *dev) | |
447 | { | ||
448 | ✗ | struct egis_msg *msg = dev->req; | |
449 | |||
450 | ✗ | msg_header_prepare (msg); | |
451 | ✗ | msg->cmd = CMD_25; | |
452 | ✗ | dev->req_len = MSG_HDR_SIZE; | |
453 | } | ||
454 | |||
455 | static int | ||
456 | ✗ | msg_check_cmd25 (FpiDeviceEtes603 *dev) | |
457 | { | ||
458 | ✗ | struct egis_msg *msg = dev->ans; | |
459 | |||
460 | ✗ | if (msg_header_check (msg)) | |
461 | { | ||
462 | ✗ | fp_err ("msg_header_check failed"); | |
463 | ✗ | goto err; | |
464 | } | ||
465 | ✗ | if (msg->cmd != CMD_OK) | |
466 | { | ||
467 | ✗ | fp_err ("CMD_OK failed"); | |
468 | ✗ | goto err; | |
469 | } | ||
470 | /* flashtype or status or ? */ | ||
471 | ✗ | if (msg->sige_misc.val[0] != 0x00) | |
472 | ✗ | fp_warn ("unexpected answer for CMD_25 (%02X)", | |
473 | msg->sige_misc.val[0]); | ||
474 | return 0; | ||
475 | err: | ||
476 | return -1; | ||
477 | } | ||
478 | |||
479 | static void | ||
480 | ✗ | msg_set_mode_control (FpiDeviceEtes603 *self, guint8 mode) | |
481 | { | ||
482 | ✗ | msg_set_regs (self, 2, REG_MODE_CONTROL, mode); | |
483 | } | ||
484 | |||
485 | |||
486 | /* Processing functions */ | ||
487 | |||
488 | /* | ||
489 | * Return the brightness of a 4bpp frame | ||
490 | */ | ||
491 | static unsigned int | ||
492 | ✗ | process_get_brightness (guint8 *f, size_t s) | |
493 | { | ||
494 | ✗ | unsigned int i, sum = 0; | |
495 | |||
496 | ✗ | for (i = 0; i < s; i++) | |
497 | { | ||
498 | ✗ | sum += f[i] >> 4; | |
499 | ✗ | sum += f[i] & 0x0F; | |
500 | } | ||
501 | ✗ | return sum; | |
502 | } | ||
503 | |||
504 | /* | ||
505 | * Return the histogram of a 4bpp frame | ||
506 | */ | ||
507 | static void | ||
508 | ✗ | process_hist (guint8 *f, size_t s, float stat[5]) | |
509 | { | ||
510 | ✗ | float hist[16]; | |
511 | ✗ | float black_mean, white_mean; | |
512 | ✗ | int i; | |
513 | |||
514 | /* Clean histogram */ | ||
515 | ✗ | for (i = 0; i < 16; i++) | |
516 | ✗ | hist[i] = 0.0; | |
517 | ✗ | for (i = 0; i < s; i++) | |
518 | { | ||
519 | ✗ | hist[f[i] >> 4]++; | |
520 | ✗ | hist[f[i] & 0x0F]++; | |
521 | } | ||
522 | /* histogram average */ | ||
523 | ✗ | for (i = 0; i < 16; i++) | |
524 | ✗ | hist[i] = hist[i] / (s * 2); | |
525 | /* Average black/white pixels (full black and full white pixels | ||
526 | * are excluded). */ | ||
527 | ✗ | black_mean = white_mean = 0.0; | |
528 | ✗ | for (i = 1; i < 8; i++) | |
529 | ✗ | black_mean += hist[i]; | |
530 | ✗ | for (i = 8; i < 15; i++) | |
531 | ✗ | white_mean += hist[i]; | |
532 | ✗ | stat[0] = hist[0]; | |
533 | ✗ | stat[1] = black_mean; | |
534 | ✗ | stat[2] = black_mean + white_mean; | |
535 | ✗ | stat[3] = white_mean; | |
536 | ✗ | stat[4] = hist[15]; | |
537 | ✗ | fp_dbg ("fullb=%6f black=%6f grey=%6f white=%6f fullw=%6f", | |
538 | hist[0], black_mean, black_mean + white_mean, white_mean, | ||
539 | hist[15]); | ||
540 | ✗ | } | |
541 | |||
542 | /* | ||
543 | * Return true if the frame is almost empty. | ||
544 | */ | ||
545 | static int | ||
546 | ✗ | process_frame_empty (guint8 *frame, size_t size) | |
547 | { | ||
548 | ✗ | unsigned int sum = process_get_brightness (frame, size); | |
549 | |||
550 | /* Allow an average of 'threshold' luminosity per pixel */ | ||
551 | ✗ | if (sum < size) | |
552 | ✗ | return 1; | |
553 | return 0; | ||
554 | } | ||
555 | |||
556 | /* Transform 4 bits image to 8 bits image */ | ||
557 | static void | ||
558 | ✗ | process_4to8_bpp (guint8 *input, unsigned int input_size, | |
559 | guint8 *output) | ||
560 | { | ||
561 | ✗ | unsigned int i, j = 0; | |
562 | |||
563 | ✗ | for (i = 0; i < input_size; i++, j += 2) | |
564 | { | ||
565 | /* 16 gray levels transform to 256 levels using << 4 */ | ||
566 | ✗ | output[j] = input[i] & 0xF0; | |
567 | ✗ | output[j + 1] = input[i] << 4; | |
568 | } | ||
569 | ✗ | } | |
570 | |||
571 | /* | ||
572 | * Remove duplicated lines at the end of a fingerprint. | ||
573 | */ | ||
574 | static void | ||
575 | ✗ | process_removefpi_end (FpiDeviceEtes603 *dev) | |
576 | { | ||
577 | ✗ | unsigned int i; | |
578 | /* 2 last lines with Fly-Estimation are the empty pattern. */ | ||
579 | ✗ | guint8 *pattern = dev->fp + (dev->fp_height - 2) * FE_WIDTH / 2; | |
580 | |||
581 | ✗ | for (i = 2; i < dev->fp_height; i += 2) | |
582 | ✗ | if (memcmp (pattern, pattern - (i * FE_WIDTH / 2), FE_WIDTH)) | |
583 | break; | ||
584 | ✗ | dev->fp_height -= i; | |
585 | ✗ | fp_dbg ("Removing %d empty lines from image", i - 2); | |
586 | ✗ | } | |
587 | |||
588 | static void | ||
589 | ✗ | reset_param (FpiDeviceEtes603 *dev) | |
590 | { | ||
591 | ✗ | dev->dcoffset = 0; | |
592 | ✗ | dev->vrt = 0; | |
593 | ✗ | dev->vrb = 0; | |
594 | ✗ | dev->gain = 0; | |
595 | } | ||
596 | |||
597 | |||
598 | /* Asynchronous stuff */ | ||
599 | |||
600 | enum { | ||
601 | INIT_CHECK_INFO_REQ, | ||
602 | INIT_CHECK_INFO_ANS, | ||
603 | INIT_CMD20_REQ, | ||
604 | INIT_CMD20_ANS, | ||
605 | INIT_CMD25_REQ, | ||
606 | INIT_CMD25_ANS, | ||
607 | INIT_SENSOR_REQ, | ||
608 | INIT_SENSOR_ANS, | ||
609 | INIT_ENC_REQ, | ||
610 | INIT_ENC_ANS, | ||
611 | INIT_REGS_REQ, | ||
612 | INIT_REGS_ANS, | ||
613 | INIT_NUM_STATES | ||
614 | }; | ||
615 | |||
616 | enum { | ||
617 | TUNEDC_INIT, | ||
618 | TUNEDC_SET_DCOFFSET_REQ, | ||
619 | TUNEDC_SET_DCOFFSET_ANS, | ||
620 | TUNEDC_GET_FRAME_REQ, | ||
621 | TUNEDC_GET_FRAME_ANS, | ||
622 | TUNEDC_FINAL_SET_REG2122_REQ, | ||
623 | TUNEDC_FINAL_SET_REG2122_ANS, | ||
624 | TUNEDC_FINAL_SET_GAIN_REQ, | ||
625 | TUNEDC_FINAL_SET_GAIN_ANS, | ||
626 | TUNEDC_FINAL_SET_DCOFFSET_REQ, | ||
627 | TUNEDC_FINAL_SET_DCOFFSET_ANS, | ||
628 | TUNEDC_NUM_STATES | ||
629 | }; | ||
630 | |||
631 | enum { | ||
632 | TUNEVRB_INIT, | ||
633 | TUNEVRB_GET_GAIN_REQ, | ||
634 | TUNEVRB_GET_GAIN_ANS, | ||
635 | TUNEVRB_GET_DCOFFSET_REQ, | ||
636 | TUNEVRB_GET_DCOFFSET_ANS, | ||
637 | TUNEVRB_SET_DCOFFSET_REQ, | ||
638 | TUNEVRB_SET_DCOFFSET_ANS, | ||
639 | TUNEVRB_FRAME_REQ, | ||
640 | TUNEVRB_FRAME_ANS, | ||
641 | TUNEVRB_FINAL_SET_DCOFFSET_REQ, | ||
642 | TUNEVRB_FINAL_SET_DCOFFSET_ANS, | ||
643 | TUNEVRB_FINAL_SET_REG2627_REQ, | ||
644 | TUNEVRB_FINAL_SET_REG2627_ANS, | ||
645 | TUNEVRB_FINAL_SET_GAINVRTVRB_REQ, | ||
646 | TUNEVRB_FINAL_SET_GAINVRTVRB_ANS, | ||
647 | TUNEVRB_FINAL_SET_MODE_SLEEP_REQ, | ||
648 | TUNEVRB_FINAL_SET_MODE_SLEEP_ANS, | ||
649 | TUNEVRB_NUM_STATES | ||
650 | }; | ||
651 | |||
652 | enum { | ||
653 | FGR_FPA_INIT_SET_MODE_SLEEP_REQ, | ||
654 | FGR_FPA_INIT_SET_MODE_SLEEP_ANS, | ||
655 | FGR_FPA_INIT_SET_DCOFFSET_REQ, | ||
656 | FGR_FPA_INIT_SET_DCOFFSET_ANS, | ||
657 | FGR_FPA_INIT_SET_GAINVRTVRB_REQ, | ||
658 | FGR_FPA_INIT_SET_GAINVRTVRB_ANS, | ||
659 | FGR_FPA_INIT_SET_VCO_CONTROL_RT_REQ, | ||
660 | FGR_FPA_INIT_SET_VCO_CONTROL_RT_ANS, | ||
661 | FGR_FPA_INIT_SET_REG04_REQ, | ||
662 | FGR_FPA_INIT_SET_REG04_ANS, | ||
663 | FGR_FPA_INIT_SET_MODE_SENSOR_REQ, | ||
664 | FGR_FPA_INIT_SET_MODE_SENSOR_ANS, | ||
665 | FGR_FPA_GET_FRAME_REQ, | ||
666 | FGR_FPA_GET_FRAME_ANS, | ||
667 | FGR_NUM_STATES | ||
668 | }; | ||
669 | |||
670 | enum { | ||
671 | CAP_FP_INIT_SET_REG10_REQ, | ||
672 | CAP_FP_INIT_SET_REG10_ANS, | ||
673 | CAP_FP_INIT_SET_MODE_FP_REQ, | ||
674 | CAP_FP_INIT_SET_MODE_FP_ANS, | ||
675 | CAP_FP_GET_FP_REQ, | ||
676 | CAP_FP_GET_FP_ANS, | ||
677 | CAP_NUM_STATES | ||
678 | }; | ||
679 | |||
680 | enum { | ||
681 | EXIT_SET_REGS_REQ, | ||
682 | EXIT_SET_REGS_ANS, | ||
683 | EXIT_NUM_STATES | ||
684 | }; | ||
685 | |||
686 | static void | ||
687 | ✗ | async_tx (FpDevice *dev, unsigned int ep, void *cb, | |
688 | FpiSsm *ssm) | ||
689 | { | ||
690 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
691 | ✗ | FpiUsbTransfer *transfer = fpi_usb_transfer_new (dev); | |
692 | ✗ | unsigned char *buffer = NULL; | |
693 | ✗ | int length; | |
694 | |||
695 | ✗ | if (ep == EP_OUT) | |
696 | { | ||
697 | ✗ | buffer = (unsigned char *) self->req; | |
698 | ✗ | length = self->req_len; | |
699 | } | ||
700 | ✗ | else if (ep == EP_IN) | |
701 | { | ||
702 | ✗ | buffer = (unsigned char *) self->ans; | |
703 | ✗ | length = self->ans_len; | |
704 | } | ||
705 | else | ||
706 | { | ||
707 | ✗ | g_assert_not_reached (); | |
708 | } | ||
709 | ✗ | transfer->ssm = ssm; | |
710 | ✗ | fpi_usb_transfer_fill_bulk_full (transfer, ep, buffer, length, NULL); | |
711 | ✗ | fpi_usb_transfer_submit (transfer, BULK_TIMEOUT, NULL, cb, NULL); | |
712 | ✗ | } | |
713 | |||
714 | |||
715 | static void | ||
716 | ✗ | async_tx_cb (FpiUsbTransfer *transfer, FpDevice *device, | |
717 | gpointer user_data, GError *error) | ||
718 | { | ||
719 | ✗ | FpImageDevice *idev = FP_IMAGE_DEVICE (device); | |
720 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (idev); | |
721 | |||
722 | ✗ | if (error) | |
723 | { | ||
724 | ✗ | fp_warn ("transfer is not completed (result: %s)", | |
725 | error->message); | ||
726 | ✗ | fpi_ssm_mark_failed (transfer->ssm, error); | |
727 | } | ||
728 | else | ||
729 | { | ||
730 | ✗ | unsigned char endpoint = transfer->endpoint; | |
731 | ✗ | int actual_length = transfer->actual_length; | |
732 | ✗ | int length = transfer->length; | |
733 | |||
734 | ✗ | if (endpoint == EP_OUT) | |
735 | { | ||
736 | ✗ | if (length != actual_length) | |
737 | ✗ | fp_warn ("length %d != actual_length %d", | |
738 | length, actual_length); | ||
739 | |||
740 | /* Chained with the answer */ | ||
741 | ✗ | async_tx (device, EP_IN, async_tx_cb, transfer->ssm); | |
742 | } | ||
743 | ✗ | else if (endpoint == EP_IN) | |
744 | { | ||
745 | ✗ | self->ans_len = actual_length; | |
746 | ✗ | fpi_ssm_next_state (transfer->ssm); | |
747 | } | ||
748 | } | ||
749 | ✗ | } | |
750 | |||
751 | static void | ||
752 | ✗ | m_exit_state (FpiSsm *ssm, FpDevice *dev) | |
753 | { | ||
754 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
755 | |||
756 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
757 | { | ||
758 | ✗ | case EXIT_SET_REGS_REQ: | |
759 | ✗ | msg_set_regs (self, 4, REG_VCO_CONTROL, REG_VCO_IDLE, | |
760 | REG_MODE_CONTROL, REG_MODE_SLEEP); | ||
761 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
762 | ✗ | break; | |
763 | |||
764 | ✗ | case EXIT_SET_REGS_ANS: | |
765 | ✗ | if (msg_check_ok (self)) | |
766 | ✗ | goto err; | |
767 | ✗ | fpi_ssm_mark_completed (ssm); | |
768 | ✗ | break; | |
769 | |||
770 | ✗ | default: | |
771 | ✗ | g_assert_not_reached (); | |
772 | break; | ||
773 | } | ||
774 | |||
775 | return; | ||
776 | ✗ | err: | |
777 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
778 | } | ||
779 | |||
780 | static void | ||
781 | ✗ | m_exit_complete (FpiSsm *ssm, FpDevice *dev, GError *error) | |
782 | { | ||
783 | ✗ | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
784 | |||
785 | ✗ | if (error) | |
786 | ✗ | fp_err ("Error switching the device to idle state"); | |
787 | else | ||
788 | ✗ | fp_dbg ("The device is now in idle state"); | |
789 | ✗ | fpi_image_device_deactivate_complete (idev, error); | |
790 | ✗ | } | |
791 | |||
792 | static void | ||
793 | ✗ | m_exit_start (FpImageDevice *idev) | |
794 | { | ||
795 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (idev); | |
796 | ✗ | FpiSsm *ssm = fpi_ssm_new (FP_DEVICE (idev), m_exit_state, EXIT_NUM_STATES); | |
797 | |||
798 | ✗ | self->is_active = FALSE; | |
799 | ✗ | fp_dbg ("Switching device to idle mode"); | |
800 | ✗ | fpi_ssm_start (ssm, m_exit_complete); | |
801 | ✗ | } | |
802 | |||
803 | static void | ||
804 | ✗ | m_capture_state (FpiSsm *ssm, FpDevice *dev) | |
805 | { | ||
806 | ✗ | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
807 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
808 | |||
809 | ✗ | if (self->is_active == FALSE) | |
810 | { | ||
811 | ✗ | fpi_ssm_mark_completed (ssm); | |
812 | ✗ | return; | |
813 | } | ||
814 | |||
815 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
816 | { | ||
817 | ✗ | case CAP_FP_INIT_SET_REG10_REQ: | |
818 | /* Reset fingerprint */ | ||
819 | ✗ | fp_dbg ("Capturing a fingerprint..."); | |
820 | ✗ | memset (self->fp, 0, FE_SIZE * 2); | |
821 | ✗ | self->fp_height = 0; | |
822 | ✗ | msg_set_regs (self, 2, REG_10, 0x92); | |
823 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
824 | ✗ | break; | |
825 | |||
826 | ✗ | case CAP_FP_INIT_SET_REG10_ANS: | |
827 | ✗ | if (msg_check_ok (self)) | |
828 | ✗ | goto err; | |
829 | ✗ | fpi_ssm_next_state (ssm); | |
830 | ✗ | break; | |
831 | |||
832 | case CAP_FP_INIT_SET_MODE_FP_REQ: | ||
833 | ✗ | msg_set_mode_control (self, REG_MODE_FP); | |
834 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
835 | ✗ | break; | |
836 | |||
837 | ✗ | case CAP_FP_INIT_SET_MODE_FP_ANS: | |
838 | ✗ | if (msg_check_ok (self)) | |
839 | ✗ | goto err; | |
840 | ✗ | fp_dbg ("Capturing a 1st frame..."); | |
841 | ✗ | fpi_ssm_next_state (ssm); | |
842 | ✗ | break; | |
843 | |||
844 | case CAP_FP_GET_FP_REQ: | ||
845 | ✗ | msg_get_fp (self, 0x01, 0xF4, 0x02, 0x01, 0x64); | |
846 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
847 | ✗ | break; | |
848 | |||
849 | ✗ | case CAP_FP_GET_FP_ANS: | |
850 | ✗ | memcpy (self->fp + self->fp_height * FE_WIDTH / 2, self->ans, | |
851 | FE_SIZE); | ||
852 | ✗ | self->fp_height += FE_HEIGHT; | |
853 | ✗ | if (self->fp_height <= FE_HEIGHT) | |
854 | { | ||
855 | /* 2 lines are at least removed each time */ | ||
856 | ✗ | self->fp_height -= 2; | |
857 | ✗ | fp_dbg ("Capturing a 2nd frame..."); | |
858 | ✗ | fpi_ssm_jump_to_state (ssm, CAP_FP_GET_FP_REQ); | |
859 | } | ||
860 | else | ||
861 | { | ||
862 | /* Remove empty parts 2 times for the 2 frames */ | ||
863 | ✗ | process_removefpi_end (self); | |
864 | ✗ | process_removefpi_end (self); | |
865 | |||
866 | ✗ | if (self->fp_height >= FE_WIDTH) | |
867 | { | ||
868 | ✗ | FpImage *img = fp_image_new (FE_WIDTH, self->fp_height); | |
869 | ✗ | unsigned int img_size = self->fp_height * FE_WIDTH; | |
870 | |||
871 | /* Images received are white on black, so invert it. */ | ||
872 | /* TODO detect sweep direction */ | ||
873 | ✗ | img->flags = FPI_IMAGE_COLORS_INVERTED | FPI_IMAGE_V_FLIPPED; | |
874 | ✗ | img->height = self->fp_height; | |
875 | ✗ | process_4to8_bpp (self->fp, img_size / 2, img->data); | |
876 | ✗ | fp_dbg ("Sending the raw fingerprint image (%dx%d)", | |
877 | img->width, img->height); | ||
878 | ✗ | fpi_image_device_image_captured (idev, img); | |
879 | } | ||
880 | else | ||
881 | { | ||
882 | ✗ | fpi_image_device_retry_scan (idev, FP_DEVICE_RETRY_TOO_SHORT); | |
883 | } | ||
884 | |||
885 | ✗ | fpi_image_device_report_finger_status (idev, FALSE); | |
886 | ✗ | fpi_ssm_mark_completed (ssm); | |
887 | } | ||
888 | break; | ||
889 | |||
890 | ✗ | default: | |
891 | ✗ | g_assert_not_reached (); | |
892 | break; | ||
893 | } | ||
894 | |||
895 | return; | ||
896 | ✗ | err: | |
897 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
898 | } | ||
899 | |||
900 | static void | ||
901 | ✗ | m_capture_complete (FpiSsm *ssm, FpDevice *dev, GError *error) | |
902 | { | ||
903 | ✗ | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
904 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
905 | |||
906 | ✗ | if (error) | |
907 | { | ||
908 | ✗ | if (self->is_active) | |
909 | { | ||
910 | ✗ | fp_err ("Error while capturing fingerprint " | |
911 | "(%s)", error->message); | ||
912 | ✗ | fpi_image_device_session_error (idev, error); | |
913 | } | ||
914 | else | ||
915 | { | ||
916 | ✗ | g_error_free (error); | |
917 | } | ||
918 | } | ||
919 | |||
920 | ✗ | if (self->is_active == TRUE) | |
921 | { | ||
922 | ✗ | fp_dbg ("Device is still active, restarting finger detection"); | |
923 | ✗ | m_start_fingerdetect (idev); | |
924 | } | ||
925 | else | ||
926 | { | ||
927 | ✗ | fp_dbg ("And it's over."); | |
928 | ✗ | m_exit_start (idev); | |
929 | } | ||
930 | ✗ | } | |
931 | |||
932 | static void | ||
933 | ✗ | m_finger_state (FpiSsm *ssm, FpDevice *dev) | |
934 | { | ||
935 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
936 | |||
937 | ✗ | if (self->is_active == FALSE) | |
938 | { | ||
939 | ✗ | fpi_ssm_mark_completed (ssm); | |
940 | ✗ | return; | |
941 | } | ||
942 | |||
943 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
944 | { | ||
945 | case FGR_FPA_INIT_SET_MODE_SLEEP_REQ: | ||
946 | ✗ | msg_set_mode_control (self, REG_MODE_SLEEP); | |
947 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
948 | ✗ | break; | |
949 | |||
950 | ✗ | case FGR_FPA_INIT_SET_MODE_SLEEP_ANS: | |
951 | ✗ | if (msg_check_ok (self)) | |
952 | ✗ | goto err; | |
953 | ✗ | fpi_ssm_next_state (ssm); | |
954 | ✗ | break; | |
955 | |||
956 | ✗ | case FGR_FPA_INIT_SET_DCOFFSET_REQ: | |
957 | ✗ | msg_set_regs (self, 2, REG_DCOFFSET, self->dcoffset); | |
958 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
959 | ✗ | break; | |
960 | |||
961 | ✗ | case FGR_FPA_INIT_SET_DCOFFSET_ANS: | |
962 | ✗ | if (msg_check_ok (self)) | |
963 | ✗ | goto err; | |
964 | ✗ | fpi_ssm_next_state (ssm); | |
965 | ✗ | break; | |
966 | |||
967 | ✗ | case FGR_FPA_INIT_SET_GAINVRTVRB_REQ: | |
968 | ✗ | msg_set_regs (self, 6, REG_GAIN, self->gain, REG_VRT, | |
969 | ✗ | self->vrt, | |
970 | ✗ | REG_VRB, self->vrb); | |
971 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
972 | ✗ | break; | |
973 | |||
974 | ✗ | case FGR_FPA_INIT_SET_GAINVRTVRB_ANS: | |
975 | ✗ | if (msg_check_ok (self)) | |
976 | ✗ | goto err; | |
977 | ✗ | fpi_ssm_next_state (ssm); | |
978 | ✗ | break; | |
979 | |||
980 | ✗ | case FGR_FPA_INIT_SET_VCO_CONTROL_RT_REQ: | |
981 | ✗ | msg_set_regs (self, 2, REG_VCO_CONTROL, REG_VCO_RT); | |
982 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
983 | ✗ | break; | |
984 | |||
985 | ✗ | case FGR_FPA_INIT_SET_VCO_CONTROL_RT_ANS: | |
986 | ✗ | if (msg_check_ok (self)) | |
987 | ✗ | goto err; | |
988 | ✗ | fpi_ssm_next_state (ssm); | |
989 | ✗ | break; | |
990 | |||
991 | ✗ | case FGR_FPA_INIT_SET_REG04_REQ: | |
992 | ✗ | msg_set_regs (self, 2, REG_04, 0x00); | |
993 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
994 | ✗ | break; | |
995 | |||
996 | ✗ | case FGR_FPA_INIT_SET_REG04_ANS: | |
997 | ✗ | if (msg_check_ok (self)) | |
998 | ✗ | goto err; | |
999 | ✗ | fpi_ssm_next_state (ssm); | |
1000 | ✗ | break; | |
1001 | |||
1002 | case FGR_FPA_INIT_SET_MODE_SENSOR_REQ: | ||
1003 | ✗ | msg_set_mode_control (self, REG_MODE_SENSOR); | |
1004 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1005 | ✗ | break; | |
1006 | |||
1007 | ✗ | case FGR_FPA_INIT_SET_MODE_SENSOR_ANS: | |
1008 | ✗ | if (msg_check_ok (self)) | |
1009 | ✗ | goto err; | |
1010 | ✗ | fpi_ssm_next_state (ssm); | |
1011 | ✗ | break; | |
1012 | |||
1013 | ✗ | case FGR_FPA_GET_FRAME_REQ: | |
1014 | ✗ | msg_get_frame (self, 0x00, 0x00, 0x00, 0x00); | |
1015 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1016 | ✗ | break; | |
1017 | |||
1018 | ✗ | case FGR_FPA_GET_FRAME_ANS: | |
1019 | ✗ | if (process_frame_empty ((guint8 *) self->ans, FRAME_SIZE)) | |
1020 | { | ||
1021 | ✗ | fpi_ssm_jump_to_state (ssm, FGR_FPA_GET_FRAME_REQ); | |
1022 | } | ||
1023 | else | ||
1024 | { | ||
1025 | ✗ | fpi_image_device_report_finger_status (FP_IMAGE_DEVICE (dev), TRUE); | |
1026 | ✗ | fpi_ssm_mark_completed (ssm); | |
1027 | } | ||
1028 | break; | ||
1029 | |||
1030 | ✗ | default: | |
1031 | ✗ | g_assert_not_reached (); | |
1032 | break; | ||
1033 | } | ||
1034 | |||
1035 | return; | ||
1036 | ✗ | err: | |
1037 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
1038 | } | ||
1039 | |||
1040 | static void | ||
1041 | ✗ | m_finger_complete (FpiSsm *ssm, FpDevice *dev, GError *error) | |
1042 | { | ||
1043 | ✗ | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
1044 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
1045 | |||
1046 | ✗ | if (!error) | |
1047 | { | ||
1048 | ✗ | FpiSsm *ssm_cap; | |
1049 | ✗ | ssm_cap = fpi_ssm_new (dev, m_capture_state, CAP_NUM_STATES); | |
1050 | ✗ | fpi_ssm_start (ssm_cap, m_capture_complete); | |
1051 | } | ||
1052 | else | ||
1053 | { | ||
1054 | ✗ | if (self->is_active) | |
1055 | { | ||
1056 | ✗ | fp_err ("Error while capturing fingerprint " | |
1057 | "(%s)", error->message); | ||
1058 | ✗ | fpi_image_device_session_error (idev, error); | |
1059 | } | ||
1060 | else | ||
1061 | { | ||
1062 | ✗ | m_exit_start (idev); | |
1063 | ✗ | g_error_free (error); | |
1064 | } | ||
1065 | ✗ | self->is_active = FALSE; | |
1066 | } | ||
1067 | |||
1068 | ✗ | } | |
1069 | |||
1070 | static void | ||
1071 | ✗ | m_start_fingerdetect (FpImageDevice *idev) | |
1072 | { | ||
1073 | ✗ | FpiSsm *ssmf; | |
1074 | |||
1075 | ✗ | ssmf = fpi_ssm_new (FP_DEVICE (idev), m_finger_state, FGR_NUM_STATES); | |
1076 | ✗ | fpi_ssm_start (ssmf, m_finger_complete); | |
1077 | ✗ | } | |
1078 | |||
1079 | /* | ||
1080 | * Tune value of VRT and VRB for contrast and brightness. | ||
1081 | */ | ||
1082 | static void | ||
1083 | ✗ | m_tunevrb_state (FpiSsm *ssm, FpDevice *dev) | |
1084 | { | ||
1085 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
1086 | ✗ | float hist[5]; | |
1087 | |||
1088 | ✗ | if (self->is_active == FALSE) | |
1089 | { | ||
1090 | ✗ | fpi_ssm_mark_completed (ssm); | |
1091 | ✗ | return; | |
1092 | } | ||
1093 | |||
1094 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
1095 | { | ||
1096 | ✗ | case TUNEVRB_INIT: | |
1097 | ✗ | fp_dbg ("Tuning of VRT/VRB"); | |
1098 | ✗ | g_assert (self->dcoffset); | |
1099 | /* VRT(reg E1)=0x0A and VRB(reg E2)=0x10 are starting values */ | ||
1100 | ✗ | self->vrt = 0x0A; | |
1101 | ✗ | self->vrb = 0x10; | |
1102 | ✗ | fpi_ssm_next_state (ssm); | |
1103 | ✗ | break; | |
1104 | |||
1105 | ✗ | case TUNEVRB_GET_GAIN_REQ: | |
1106 | ✗ | msg_get_regs (self, 1, REG_GAIN); | |
1107 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1108 | ✗ | break; | |
1109 | |||
1110 | ✗ | case TUNEVRB_GET_GAIN_ANS: | |
1111 | ✗ | if (msg_parse_regs (self)) | |
1112 | ✗ | goto err; | |
1113 | ✗ | fpi_ssm_next_state (ssm); | |
1114 | ✗ | break; | |
1115 | |||
1116 | ✗ | case TUNEVRB_GET_DCOFFSET_REQ: | |
1117 | ✗ | msg_get_regs (self, 1, REG_DCOFFSET); | |
1118 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1119 | ✗ | break; | |
1120 | |||
1121 | ✗ | case TUNEVRB_GET_DCOFFSET_ANS: | |
1122 | ✗ | if (msg_parse_regs (self)) | |
1123 | ✗ | goto err; | |
1124 | ✗ | fpi_ssm_next_state (ssm); | |
1125 | ✗ | break; | |
1126 | |||
1127 | ✗ | case TUNEVRB_SET_DCOFFSET_REQ: | |
1128 | /* Reduce DCoffset by 1 to allow tuning */ | ||
1129 | ✗ | msg_set_regs (self, 2, REG_DCOFFSET, self->dcoffset - 1); | |
1130 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1131 | ✗ | break; | |
1132 | |||
1133 | ✗ | case TUNEVRB_SET_DCOFFSET_ANS: | |
1134 | ✗ | if (msg_check_ok (self)) | |
1135 | ✗ | goto err; | |
1136 | ✗ | fpi_ssm_next_state (ssm); | |
1137 | ✗ | break; | |
1138 | |||
1139 | ✗ | case TUNEVRB_FRAME_REQ: | |
1140 | ✗ | fp_dbg ("Testing VRT=0x%02X VRB=0x%02X", self->vrt, self->vrb); | |
1141 | ✗ | msg_get_frame (self, 0x01, self->gain, self->vrt, self->vrb); | |
1142 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1143 | ✗ | break; | |
1144 | |||
1145 | ✗ | case TUNEVRB_FRAME_ANS: | |
1146 | ✗ | process_hist ((guint8 *) self->ans, FRAME_SIZE, hist); | |
1147 | /* Note that this tuning could probably be improved */ | ||
1148 | ✗ | if (hist[0] + hist[1] > 0.95) | |
1149 | { | ||
1150 | ✗ | if (self->vrt <= 0 || self->vrb <= 0) | |
1151 | { | ||
1152 | ✗ | fp_dbg ("Image is too dark, reducing DCOffset"); | |
1153 | ✗ | self->dcoffset--; | |
1154 | ✗ | fpi_ssm_jump_to_state (ssm, TUNEVRB_INIT); | |
1155 | } | ||
1156 | else | ||
1157 | { | ||
1158 | ✗ | self->vrt--; | |
1159 | ✗ | self->vrb--; | |
1160 | ✗ | fpi_ssm_jump_to_state (ssm, TUNEVRB_FRAME_REQ); | |
1161 | } | ||
1162 | break; | ||
1163 | } | ||
1164 | ✗ | if (hist[4] > 0.95) | |
1165 | { | ||
1166 | ✗ | fp_dbg ("Image is too bright, increasing DCOffset"); | |
1167 | ✗ | self->dcoffset++; | |
1168 | ✗ | fpi_ssm_jump_to_state (ssm, TUNEVRB_INIT); | |
1169 | ✗ | break; | |
1170 | } | ||
1171 | ✗ | if (hist[4] + hist[3] > 0.4) | |
1172 | { | ||
1173 | ✗ | if (self->vrt >= 2 * self->vrb - 0x0a) | |
1174 | { | ||
1175 | ✗ | self->vrt++; | |
1176 | ✗ | self->vrb++; | |
1177 | } | ||
1178 | else | ||
1179 | { | ||
1180 | ✗ | self->vrt++; | |
1181 | } | ||
1182 | /* Check maximum for vrt/vrb */ | ||
1183 | /* TODO if maximum is reached, leave with an error? */ | ||
1184 | ✗ | if (self->vrt > VRT_MAX) | |
1185 | ✗ | self->vrt = VRT_MAX; | |
1186 | ✗ | if (self->vrb > VRB_MAX) | |
1187 | ✗ | self->vrb = VRB_MAX; | |
1188 | ✗ | fpi_ssm_jump_to_state (ssm, TUNEVRB_FRAME_REQ); | |
1189 | ✗ | break; | |
1190 | } | ||
1191 | ✗ | fpi_ssm_next_state (ssm); | |
1192 | ✗ | break; | |
1193 | |||
1194 | ✗ | case TUNEVRB_FINAL_SET_DCOFFSET_REQ: | |
1195 | ✗ | fp_dbg ("-> VRT=0x%02X VRB=0x%02X", self->vrt, self->vrb); | |
1196 | /* Reset the DCOffset */ | ||
1197 | ✗ | msg_set_regs (self, 2, REG_DCOFFSET, self->dcoffset); | |
1198 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1199 | ✗ | break; | |
1200 | |||
1201 | ✗ | case TUNEVRB_FINAL_SET_DCOFFSET_ANS: | |
1202 | ✗ | if (msg_check_ok (self)) | |
1203 | ✗ | goto err; | |
1204 | ✗ | fpi_ssm_next_state (ssm); | |
1205 | ✗ | break; | |
1206 | |||
1207 | ✗ | case TUNEVRB_FINAL_SET_REG2627_REQ: | |
1208 | /* In traces, REG_26/REG_27 are set. purpose? values? */ | ||
1209 | ✗ | msg_set_regs (self, 4, REG_26, 0x11, REG_27, 0x00); | |
1210 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1211 | ✗ | break; | |
1212 | |||
1213 | ✗ | case TUNEVRB_FINAL_SET_REG2627_ANS: | |
1214 | ✗ | if (msg_check_ok (self)) | |
1215 | ✗ | goto err; | |
1216 | ✗ | fpi_ssm_next_state (ssm); | |
1217 | ✗ | break; | |
1218 | |||
1219 | ✗ | case TUNEVRB_FINAL_SET_GAINVRTVRB_REQ: | |
1220 | /* Set Gain/VRT/VRB values found */ | ||
1221 | ✗ | msg_set_regs (self, 6, REG_GAIN, self->gain, REG_VRT, | |
1222 | ✗ | self->vrt, | |
1223 | ✗ | REG_VRB, self->vrb); | |
1224 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1225 | ✗ | break; | |
1226 | |||
1227 | ✗ | case TUNEVRB_FINAL_SET_GAINVRTVRB_ANS: | |
1228 | ✗ | if (msg_check_ok (self)) | |
1229 | ✗ | goto err; | |
1230 | /* In traces, Gain/VRT/VRB are read again. */ | ||
1231 | ✗ | fpi_ssm_next_state (ssm); | |
1232 | ✗ | break; | |
1233 | |||
1234 | case TUNEVRB_FINAL_SET_MODE_SLEEP_REQ: | ||
1235 | ✗ | msg_set_mode_control (self, REG_MODE_SLEEP); | |
1236 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1237 | ✗ | break; | |
1238 | |||
1239 | ✗ | case TUNEVRB_FINAL_SET_MODE_SLEEP_ANS: | |
1240 | ✗ | if (msg_check_ok (self)) | |
1241 | ✗ | goto err; | |
1242 | ✗ | fpi_ssm_mark_completed (ssm); | |
1243 | ✗ | break; | |
1244 | |||
1245 | ✗ | default: | |
1246 | ✗ | g_assert_not_reached (); | |
1247 | break; | ||
1248 | } | ||
1249 | |||
1250 | return; | ||
1251 | ✗ | err: | |
1252 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
1253 | } | ||
1254 | |||
1255 | static void | ||
1256 | ✗ | m_tunevrb_complete (FpiSsm *ssm, FpDevice *dev, GError *error) | |
1257 | { | ||
1258 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
1259 | ✗ | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
1260 | |||
1261 | ✗ | fpi_image_device_activate_complete (idev, error); | |
1262 | ✗ | if (!error) | |
1263 | { | ||
1264 | ✗ | fp_dbg ("Tuning is done. Starting finger detection."); | |
1265 | ✗ | m_start_fingerdetect (idev); | |
1266 | } | ||
1267 | |||
1268 | ✗ | if (!self->is_active) | |
1269 | ✗ | m_exit_start (idev); | |
1270 | |||
1271 | ✗ | } | |
1272 | |||
1273 | /* | ||
1274 | * This function tunes the DCoffset value and adjusts the gain value if | ||
1275 | * required. | ||
1276 | */ | ||
1277 | static void | ||
1278 | ✗ | m_tunedc_state (FpiSsm *ssm, FpDevice *dev) | |
1279 | { | ||
1280 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
1281 | |||
1282 | ✗ | if (self->is_active == FALSE) | |
1283 | { | ||
1284 | ✗ | fpi_ssm_mark_completed (ssm); | |
1285 | ✗ | return; | |
1286 | } | ||
1287 | |||
1288 | /* TODO To get better results, tuning could be done 3 times as in | ||
1289 | * captured traffic to make sure that the value is correct. */ | ||
1290 | /* The default gain should work but it may reach a DCOffset limit so in | ||
1291 | * this case we decrease the gain. */ | ||
1292 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
1293 | { | ||
1294 | ✗ | case TUNEDC_INIT: | |
1295 | /* reg_e0 = 0x23 is sensor normal/small gain */ | ||
1296 | ✗ | self->gain = GAIN_SMALL_INIT; | |
1297 | ✗ | self->tunedc_min = DCOFFSET_MIN; | |
1298 | ✗ | self->tunedc_max = DCOFFSET_MAX; | |
1299 | ✗ | fp_dbg ("Tuning DCoffset"); | |
1300 | ✗ | fpi_ssm_next_state (ssm); | |
1301 | ✗ | break; | |
1302 | |||
1303 | ✗ | case TUNEDC_SET_DCOFFSET_REQ: | |
1304 | /* Dichotomic search to find at which value the frame becomes | ||
1305 | * almost black. */ | ||
1306 | ✗ | self->dcoffset = (self->tunedc_max + self->tunedc_min) / 2; | |
1307 | ✗ | fp_dbg ("Testing DCoffset=0x%02X Gain=0x%02X", self->dcoffset, | |
1308 | self->gain); | ||
1309 | ✗ | msg_set_regs (self, 2, REG_DCOFFSET, self->dcoffset); | |
1310 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1311 | ✗ | break; | |
1312 | |||
1313 | ✗ | case TUNEDC_SET_DCOFFSET_ANS: | |
1314 | ✗ | if (msg_check_ok (self)) | |
1315 | ✗ | goto err; | |
1316 | ✗ | fpi_ssm_next_state (ssm); | |
1317 | ✗ | break; | |
1318 | |||
1319 | ✗ | case TUNEDC_GET_FRAME_REQ: | |
1320 | /* vrt:0x15 vrb:0x10 are constant in all tuning frames. */ | ||
1321 | ✗ | msg_get_frame (self, 0x01, self->gain, 0x15, 0x10); | |
1322 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1323 | ✗ | break; | |
1324 | |||
1325 | ✗ | case TUNEDC_GET_FRAME_ANS: | |
1326 | ✗ | if (process_frame_empty ((guint8 *) self->ans, FRAME_WIDTH)) | |
1327 | ✗ | self->tunedc_max = self->dcoffset; | |
1328 | else | ||
1329 | ✗ | self->tunedc_min = self->dcoffset; | |
1330 | ✗ | if (self->tunedc_min + 1 < self->tunedc_max) | |
1331 | { | ||
1332 | ✗ | fpi_ssm_jump_to_state (ssm, TUNEDC_SET_DCOFFSET_REQ); | |
1333 | } | ||
1334 | ✗ | else if (self->tunedc_max < DCOFFSET_MAX) | |
1335 | { | ||
1336 | ✗ | self->dcoffset = self->tunedc_max + 1; | |
1337 | ✗ | fpi_ssm_next_state (ssm); | |
1338 | } | ||
1339 | else | ||
1340 | { | ||
1341 | ✗ | self->gain--; | |
1342 | ✗ | fpi_ssm_jump_to_state (ssm, TUNEDC_SET_DCOFFSET_REQ); | |
1343 | } | ||
1344 | break; | ||
1345 | |||
1346 | ✗ | case TUNEDC_FINAL_SET_REG2122_REQ: | |
1347 | ✗ | fp_dbg ("-> DCoffset=0x%02X Gain=0x%02X", self->dcoffset, | |
1348 | self->gain); | ||
1349 | /* ??? how reg21 / reg22 are calculated */ | ||
1350 | ✗ | msg_set_regs (self, 4, REG_21, 0x23, REG_22, 0x21); | |
1351 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1352 | ✗ | break; | |
1353 | |||
1354 | ✗ | case TUNEDC_FINAL_SET_REG2122_ANS: | |
1355 | ✗ | if (msg_check_ok (self)) | |
1356 | ✗ | goto err; | |
1357 | ✗ | fpi_ssm_next_state (ssm); | |
1358 | ✗ | break; | |
1359 | |||
1360 | ✗ | case TUNEDC_FINAL_SET_GAIN_REQ: | |
1361 | ✗ | msg_set_regs (self, 2, REG_GAIN, self->gain); | |
1362 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1363 | ✗ | break; | |
1364 | |||
1365 | ✗ | case TUNEDC_FINAL_SET_GAIN_ANS: | |
1366 | ✗ | fpi_ssm_next_state (ssm); | |
1367 | ✗ | break; | |
1368 | |||
1369 | ✗ | case TUNEDC_FINAL_SET_DCOFFSET_REQ: | |
1370 | ✗ | msg_set_regs (self, 2, REG_DCOFFSET, self->dcoffset); | |
1371 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1372 | ✗ | break; | |
1373 | |||
1374 | ✗ | case TUNEDC_FINAL_SET_DCOFFSET_ANS: | |
1375 | /* In captured traffic, read GAIN, VRT, and VRB registers. */ | ||
1376 | ✗ | if (msg_check_ok (self)) | |
1377 | ✗ | goto err; | |
1378 | ✗ | fpi_ssm_mark_completed (ssm); | |
1379 | ✗ | break; | |
1380 | |||
1381 | ✗ | default: | |
1382 | ✗ | g_assert_not_reached (); | |
1383 | break; | ||
1384 | } | ||
1385 | |||
1386 | return; | ||
1387 | ✗ | err: | |
1388 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
1389 | } | ||
1390 | |||
1391 | static void | ||
1392 | ✗ | m_tunedc_complete (FpiSsm *ssm, FpDevice *dev, GError *error) | |
1393 | { | ||
1394 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
1395 | ✗ | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
1396 | |||
1397 | ✗ | if (!error) | |
1398 | { | ||
1399 | ✗ | FpiSsm *ssm_tune; | |
1400 | ✗ | ssm_tune = fpi_ssm_new (FP_DEVICE (idev), m_tunevrb_state, | |
1401 | TUNEVRB_NUM_STATES); | ||
1402 | ✗ | fpi_ssm_start (ssm_tune, m_tunevrb_complete); | |
1403 | } | ||
1404 | else | ||
1405 | { | ||
1406 | ✗ | fp_err ("Error while tuning DCOFFSET"); | |
1407 | ✗ | reset_param (FPI_DEVICE_ETES603 (dev)); | |
1408 | ✗ | fpi_image_device_session_error (idev, error); | |
1409 | } | ||
1410 | |||
1411 | ✗ | if (!self->is_active) | |
1412 | ✗ | m_exit_start (idev); | |
1413 | |||
1414 | ✗ | } | |
1415 | |||
1416 | static void | ||
1417 | ✗ | m_init_state (FpiSsm *ssm, FpDevice *dev) | |
1418 | { | ||
1419 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (dev); | |
1420 | |||
1421 | ✗ | if (self->is_active == FALSE) | |
1422 | { | ||
1423 | ✗ | fpi_ssm_mark_completed (ssm); | |
1424 | ✗ | return; | |
1425 | } | ||
1426 | |||
1427 | ✗ | switch (fpi_ssm_get_cur_state (ssm)) | |
1428 | { | ||
1429 | ✗ | case INIT_CHECK_INFO_REQ: | |
1430 | ✗ | msg_get_regs (self, 4, REG_INFO0, REG_INFO1, REG_INFO2, | |
1431 | REG_INFO3); | ||
1432 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1433 | ✗ | break; | |
1434 | |||
1435 | ✗ | case INIT_CHECK_INFO_ANS: | |
1436 | ✗ | if (msg_parse_regs (self)) | |
1437 | ✗ | goto err; | |
1438 | ✗ | if (check_info (self)) | |
1439 | ✗ | goto err; | |
1440 | ✗ | fpi_ssm_next_state (ssm); | |
1441 | ✗ | break; | |
1442 | |||
1443 | case INIT_CMD20_REQ: | ||
1444 | ✗ | msg_get_cmd20 (self); | |
1445 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1446 | ✗ | break; | |
1447 | |||
1448 | ✗ | case INIT_CMD20_ANS: | |
1449 | ✗ | if (msg_check_cmd20 (self)) | |
1450 | ✗ | goto err; | |
1451 | ✗ | fpi_ssm_next_state (ssm); | |
1452 | ✗ | break; | |
1453 | |||
1454 | case INIT_CMD25_REQ: | ||
1455 | ✗ | msg_get_cmd25 (self); | |
1456 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1457 | ✗ | break; | |
1458 | |||
1459 | ✗ | case INIT_CMD25_ANS: | |
1460 | ✗ | if (msg_check_cmd25 (self)) | |
1461 | ✗ | goto err; | |
1462 | ✗ | fpi_ssm_next_state (ssm); | |
1463 | ✗ | break; | |
1464 | |||
1465 | ✗ | case INIT_SENSOR_REQ: | |
1466 | /* In captured traffic, those are split. */ | ||
1467 | ✗ | msg_set_regs (self, 18, REG_MODE_CONTROL, REG_MODE_SLEEP, | |
1468 | REG_50, 0x0F, REG_GAIN, 0x04, REG_VRT, 0x08, | ||
1469 | REG_VRB, 0x0D, REG_VCO_CONTROL, REG_VCO_RT, | ||
1470 | REG_DCOFFSET, 0x36, REG_F0, 0x00, REG_F2, 0x00); | ||
1471 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1472 | ✗ | break; | |
1473 | |||
1474 | ✗ | case INIT_SENSOR_ANS: | |
1475 | ✗ | if (msg_check_ok (self)) | |
1476 | ✗ | goto err; | |
1477 | ✗ | fpi_ssm_next_state (ssm); | |
1478 | ✗ | break; | |
1479 | |||
1480 | ✗ | case INIT_ENC_REQ: | |
1481 | /* Initialize encryption registers without encryption. */ | ||
1482 | /* Set registers from 0x41 to 0x48 (0x8 regs) */ | ||
1483 | ✗ | msg_set_regs (self, 16, REG_ENC1, 0x12, REG_ENC2, 0x34, | |
1484 | REG_ENC3, 0x56, REG_ENC4, 0x78, REG_ENC5, 0x90, | ||
1485 | REG_ENC6, 0xAB, REG_ENC7, 0xCD, REG_ENC8, 0xEF); | ||
1486 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1487 | ✗ | break; | |
1488 | |||
1489 | ✗ | case INIT_ENC_ANS: | |
1490 | ✗ | if (msg_check_ok (self)) | |
1491 | ✗ | goto err; | |
1492 | ✗ | fpi_ssm_next_state (ssm); | |
1493 | ✗ | break; | |
1494 | |||
1495 | ✗ | case INIT_REGS_REQ: | |
1496 | /* Set register from 0x20 to 0x37 (0x18 regs) */ | ||
1497 | ✗ | msg_set_regs (self, 48, | |
1498 | REG_20, 0x00, REG_21, 0x23, REG_22, 0x21, REG_23, | ||
1499 | 0x20, | ||
1500 | REG_24, 0x14, REG_25, 0x6A, REG_26, 0x00, REG_27, | ||
1501 | 0x00, | ||
1502 | REG_28, 0x00, REG_29, 0xC0, REG_2A, 0x50, REG_2B, | ||
1503 | 0x50, | ||
1504 | REG_2C, 0x4D, REG_2D, 0x03, REG_2E, 0x06, REG_2F, | ||
1505 | 0x06, | ||
1506 | REG_30, 0x10, REG_31, 0x02, REG_32, 0x14, REG_33, | ||
1507 | 0x34, | ||
1508 | REG_34, 0x01, REG_35, 0x08, REG_36, 0x03, REG_37, | ||
1509 | 0x21); | ||
1510 | ✗ | async_tx (dev, EP_OUT, async_tx_cb, ssm); | |
1511 | ✗ | break; | |
1512 | |||
1513 | ✗ | case INIT_REGS_ANS: | |
1514 | ✗ | if (msg_check_ok (self)) | |
1515 | ✗ | goto err; | |
1516 | ✗ | fpi_ssm_mark_completed (ssm); | |
1517 | ✗ | break; | |
1518 | |||
1519 | ✗ | default: | |
1520 | ✗ | g_assert_not_reached (); | |
1521 | break; | ||
1522 | } | ||
1523 | |||
1524 | return; | ||
1525 | ✗ | err: | |
1526 | ✗ | fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); | |
1527 | } | ||
1528 | |||
1529 | static void | ||
1530 | ✗ | m_init_complete (FpiSsm *ssm, FpDevice *dev, GError *error) | |
1531 | { | ||
1532 | ✗ | FpImageDevice *idev = FP_IMAGE_DEVICE (dev); | |
1533 | |||
1534 | ✗ | if (!error) | |
1535 | { | ||
1536 | ✗ | FpiSsm *ssm_tune; | |
1537 | ✗ | ssm_tune = fpi_ssm_new (FP_DEVICE (idev), m_tunedc_state, | |
1538 | TUNEDC_NUM_STATES); | ||
1539 | ✗ | fpi_ssm_start (ssm_tune, m_tunedc_complete); | |
1540 | } | ||
1541 | else | ||
1542 | { | ||
1543 | ✗ | fp_err ("Error initializing the device"); | |
1544 | ✗ | reset_param (FPI_DEVICE_ETES603 (dev)); | |
1545 | ✗ | fpi_image_device_session_error (idev, error); | |
1546 | } | ||
1547 | ✗ | } | |
1548 | |||
1549 | static void | ||
1550 | ✗ | dev_activate (FpImageDevice *idev) | |
1551 | { | ||
1552 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (idev); | |
1553 | ✗ | FpiSsm *ssm; | |
1554 | |||
1555 | ✗ | g_assert (self); | |
1556 | |||
1557 | /* Reset info and data */ | ||
1558 | ✗ | self->is_active = TRUE; | |
1559 | |||
1560 | ✗ | if (self->dcoffset == 0) | |
1561 | { | ||
1562 | ✗ | fp_dbg ("Tuning device..."); | |
1563 | ✗ | ssm = fpi_ssm_new (FP_DEVICE (idev), m_init_state, INIT_NUM_STATES); | |
1564 | ✗ | fpi_ssm_start (ssm, m_init_complete); | |
1565 | } | ||
1566 | else | ||
1567 | { | ||
1568 | ✗ | fp_dbg ("Using previous tuning (DCOFFSET=0x%02X,VRT=0x%02X," | |
1569 | "VRB=0x%02X,GAIN=0x%02X).", self->dcoffset, self->vrt, | ||
1570 | self->vrb, self->gain); | ||
1571 | ✗ | fpi_image_device_activate_complete (idev, NULL); | |
1572 | ✗ | ssm = fpi_ssm_new (FP_DEVICE (idev), m_finger_state, FGR_NUM_STATES); | |
1573 | ✗ | fpi_ssm_start (ssm, m_finger_complete); | |
1574 | } | ||
1575 | ✗ | } | |
1576 | |||
1577 | static void | ||
1578 | ✗ | dev_deactivate (FpImageDevice *idev) | |
1579 | { | ||
1580 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (idev); | |
1581 | |||
1582 | ✗ | fp_dbg ("deactivating"); | |
1583 | |||
1584 | /* this can be called even if still activated. */ | ||
1585 | ✗ | if (self->is_active == TRUE) | |
1586 | ✗ | self->is_active = FALSE; | |
1587 | else | ||
1588 | ✗ | m_exit_start (idev); | |
1589 | ✗ | } | |
1590 | |||
1591 | static void | ||
1592 | ✗ | dev_open (FpImageDevice *idev) | |
1593 | { | ||
1594 | ✗ | GError *error = NULL; | |
1595 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (idev); | |
1596 | |||
1597 | ✗ | if (!g_usb_device_claim_interface (fpi_device_get_usb_device (FP_DEVICE (idev)), 0, 0, &error)) | |
1598 | { | ||
1599 | ✗ | fpi_image_device_open_complete (idev, error); | |
1600 | ✗ | return; | |
1601 | } | ||
1602 | |||
1603 | ✗ | self->req = g_malloc (sizeof (struct egis_msg)); | |
1604 | ✗ | self->ans = g_malloc (FE_SIZE); | |
1605 | ✗ | self->fp = g_malloc (FE_SIZE * 4); | |
1606 | |||
1607 | ✗ | fpi_image_device_open_complete (idev, NULL); | |
1608 | } | ||
1609 | |||
1610 | static void | ||
1611 | ✗ | dev_close (FpImageDevice *idev) | |
1612 | { | ||
1613 | ✗ | GError *error = NULL; | |
1614 | ✗ | FpiDeviceEtes603 *self = FPI_DEVICE_ETES603 (idev); | |
1615 | |||
1616 | ✗ | g_free (self->req); | |
1617 | ✗ | g_free (self->ans); | |
1618 | ✗ | g_free (self->fp); | |
1619 | |||
1620 | ✗ | g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (idev)), | |
1621 | 0, 0, &error); | ||
1622 | ✗ | fpi_image_device_close_complete (idev, error); | |
1623 | ✗ | } | |
1624 | |||
1625 | static const FpIdEntry id_table[] = { | ||
1626 | /* EgisTec (aka Lightuning) ES603 */ | ||
1627 | { .vid = 0x1c7a, .pid = 0x0603, }, | ||
1628 | { .vid = 0, .pid = 0, .driver_data = 0 }, | ||
1629 | }; | ||
1630 | |||
1631 | static void | ||
1632 | ✗ | fpi_device_etes603_init (FpiDeviceEtes603 *self) | |
1633 | { | ||
1634 | ✗ | } | |
1635 | static void | ||
1636 | 120 | fpi_device_etes603_class_init (FpiDeviceEtes603Class *klass) | |
1637 | { | ||
1638 | 120 | FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); | |
1639 | 120 | FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass); | |
1640 | |||
1641 | 120 | dev_class->id = "etes603"; | |
1642 | 120 | dev_class->full_name = "EgisTec ES603"; | |
1643 | 120 | dev_class->type = FP_DEVICE_TYPE_USB; | |
1644 | 120 | dev_class->id_table = id_table; | |
1645 | 120 | dev_class->scan_type = FP_SCAN_TYPE_SWIPE; | |
1646 | |||
1647 | 120 | img_class->img_open = dev_open; | |
1648 | 120 | img_class->img_close = dev_close; | |
1649 | 120 | img_class->activate = dev_activate; | |
1650 | 120 | img_class->deactivate = dev_deactivate; | |
1651 | |||
1652 | 120 | img_class->img_width = 256; | |
1653 | 120 | img_class->img_height = -1; | |
1654 | } | ||
1655 |