elan.c 26.5 KB
Newer Older
Igor Filatov's avatar
Igor Filatov committed
1
2
3
4
/*
 * Elan driver for libfprint
 *
 * Copyright (C) 2017 Igor Filatov <ia.filatov@gmail.com>
5
 * Copyright (C) 2018 Sébastien Béchet <sebastien.bechet@osinix.com >
Igor Filatov's avatar
Igor Filatov committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
 * The algorithm which libfprint uses to match fingerprints doesn't like small
 * images like the ones these drivers produce. There's just not enough minutiae
 * (recognizable print-specific points) on them for a reliable match. This means
 * that unless another matching algo is found/implemented, these readers will
 * not work as good with libfprint as they do with vendor drivers.
 *
 * To get bigger images the driver expects you to swipe the finger over the
 * reader. This works quite well for readers with a rectangular 144x64 sensor.
 * Worse than real swipe readers but good enough for day-to-day use. It needs
 * a steady and relatively slow swipe. There are also square 96x96 sensors and
 * I don't know whether they are in fact usable or not because I don't have one.
 * I imagine they'd be less reliable because the resulting image is even
 * smaller. If they can't be made usable with libfprint, I might end up dropping
 * them because it's better than saying they work when they don't.
 */

Igor Filatov's avatar
Igor Filatov committed
39
40
#define FP_COMPONENT "elan"

41
#include "drivers_api.h"
Igor Filatov's avatar
Igor Filatov committed
42
43
#include "elan.h"

44
45
#define dbg_buf(buf, len)                                     \
  if (len == 1)                                               \
46
    fp_dbg("%02x", buf[0]);                                  \
47
  else if (len == 2)                                          \
48
    fp_dbg("%04x", buf[0] << 8 | buf[1]);                    \
49
  else if (len > 2)                                           \
50
    fp_dbg("%04x... (%d bytes)", buf[0] << 8 | buf[1], len)
51

Igor Filatov's avatar
Igor Filatov committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
unsigned char elan_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
			     struct fpi_frame *frame, unsigned int x,
			     unsigned int y)
{
	return frame->data[x + y * ctx->frame_width];
}

static struct fpi_frame_asmbl_ctx assembling_ctx = {
	.frame_width = 0,
	.frame_height = 0,
	.image_width = 0,
	.get_pixel = elan_get_pixel,
};

struct elan_dev {
67
68
	/* device config */
	unsigned short dev_type;
69
70
	unsigned short fw_ver;
	void (*process_frame) (unsigned short *raw_frame, GSList ** frames);
71
72
73
74
	/* end device config */

	/* commands */
	const struct elan_cmd *cmd;
Igor Filatov's avatar
Igor Filatov committed
75
	int cmd_timeout;
Bastien Nocera's avatar
Bastien Nocera committed
76
	fpi_usb_transfer *cur_transfer;
77
	/* end commands */
Igor Filatov's avatar
Igor Filatov committed
78

79
	/* state */
80
81
	enum fp_imgdev_state dev_state;
	enum fp_imgdev_state dev_state_next;
Igor Filatov's avatar
Igor Filatov committed
82
	unsigned char *last_read;
83
84
85
	unsigned char calib_atts_left;
	unsigned char calib_status;
	unsigned short *background;
Igor Filatov's avatar
Igor Filatov committed
86
87
	unsigned char frame_width;
	unsigned char frame_height;
88
	unsigned char raw_frame_height;
Igor Filatov's avatar
Igor Filatov committed
89
90
	int num_frames;
	GSList *frames;
91
	/* end state */
Igor Filatov's avatar
Igor Filatov committed
92
93
};

94
95
96
97
98
int cmp_short(const void *a, const void *b)
{
	return (int)(*(short *)a - *(short *)b);
}

Igor Filatov's avatar
Igor Filatov committed
99
100
static void elan_dev_reset(struct elan_dev *elandev)
{
101
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
102
103
104

	BUG_ON(elandev->cur_transfer);

105
	elandev->cmd = NULL;
Igor Filatov's avatar
Igor Filatov committed
106
107
	elandev->cmd_timeout = ELAN_CMD_TIMEOUT;

108
109
	elandev->calib_status = 0;

Igor Filatov's avatar
Igor Filatov committed
110
111
112
113
114
115
116
117
	g_free(elandev->last_read);
	elandev->last_read = NULL;

	g_slist_free_full(elandev->frames, g_free);
	elandev->frames = NULL;
	elandev->num_frames = 0;
}

118
static void elan_save_frame(struct elan_dev *elandev, unsigned short *frame)
Igor Filatov's avatar
Igor Filatov committed
119
{
120
121
	G_DEBUG_HERE();

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
	/* so far 3 types of readers by sensor dimensions and orientation have been
	 * seen in the wild:
	 * 1. 144x64. Raw images are in portrait orientation while readers themselves
	 *    are placed (e.g. built into a touchpad) in landscape orientation. These
	 *    need to be rotated before assembling.
	 * 2. 96x96 rotated. Like the first type but square. Likewise, need to be
	 *    rotated before assembling.
	 * 3. 96x96 normal. Square and need NOT be rotated. So far there's only been
	 *    1 report of a 0c03 of this type. Hopefully this type can be identified
	 *    by device id (and manufacturers don't just install the readers as they
	 *    please).
	 * we also discard stripes of 'frame_margin' from bottom and top because
	 * assembling works bad for tall frames */

	unsigned char frame_width = elandev->frame_width;
	unsigned char frame_height = elandev->frame_height;
	unsigned char raw_height = elandev->raw_frame_height;
	unsigned char frame_margin = (raw_height - elandev->frame_height) / 2;
	int frame_idx, raw_idx;

	for (int y = 0; y < frame_height; y++)
		for (int x = 0; x < frame_width; x++) {
			if (elandev->dev_type & ELAN_NOT_ROTATED)
				raw_idx = x + (y + frame_margin) * frame_width;
			else
				raw_idx = frame_margin + y + x * raw_height;
			frame_idx = x + y * frame_width;
Igor Filatov's avatar
Igor Filatov committed
149
150
151
			frame[frame_idx] =
			    ((unsigned short *)elandev->last_read)[raw_idx];
		}
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
}

static void elan_save_background(struct elan_dev *elandev)
{
	G_DEBUG_HERE();

	g_free(elandev->background);
	elandev->background =
	    g_malloc(elandev->frame_width * elandev->frame_height *
		     sizeof(short));
	elan_save_frame(elandev, elandev->background);
}

/* save a frame as part of the fingerprint image
 * background needs to have been captured for this routine to work
 * Elantech recommends 2-step non-linear normalization in order to reduce
 * 2^14 ADC resolution to 2^8 image:
 *
 * 1. background is subtracted (done here)
 *
 * 2. pixels are grouped in 3 groups by intensity and each group is mapped
 *    separately onto the normalized frame (done in elan_process_frame_*)
 *    ==== 16383     ____> ======== 255
 *                  /
 *    ----- lvl3 __/
 *                   35% pixels
 *
 *    ----- lvl2 --------> ======== 156
 *
 *                   30% pixels
 *    ----- lvl1 --------> ======== 99
 *
 *                   35% pixels
 *    ----- lvl0 __
 *                 \
 *    ======== 0    \____> ======== 0
 *
 * For some devices we don't do 2. but instead do a simple linear mapping
 * because it seems to produce better results (or at least as good):
 *    ==== 16383      ___> ======== 255
 *                   /
 *    ------ max  __/
 *
 *
 *    ------ min  __
 *                  \
 *    ======== 0     \___> ======== 0
 */
200
static int elan_save_img_frame(struct elan_dev *elandev)
201
202
203
204
205
206
{
	G_DEBUG_HERE();

	unsigned int frame_size = elandev->frame_width * elandev->frame_height;
	unsigned short *frame = g_malloc(frame_size * sizeof(short));
	elan_save_frame(elandev, frame);
207
	unsigned int sum = 0;
208

209
	for (int i = 0; i < frame_size; i++) {
210
211
212
213
		if (elandev->background[i] > frame[i])
			frame[i] = 0;
		else
			frame[i] -= elandev->background[i];
214
215
216
217
218
219
220
221
		sum += frame[i];
	}

	if (sum == 0) {
		fp_dbg
		    ("frame darker than background; finger present during calibration?");
		return -1;
	}
Igor Filatov's avatar
Igor Filatov committed
222
223
224

	elandev->frames = g_slist_prepend(elandev->frames, frame);
	elandev->num_frames += 1;
225
	return 0;
Igor Filatov's avatar
Igor Filatov committed
226
227
}

228
229
static void elan_process_frame_linear(unsigned short *raw_frame,
				      GSList ** frames)
Igor Filatov's avatar
Igor Filatov committed
230
231
232
233
234
235
{
	unsigned int frame_size =
	    assembling_ctx.frame_width * assembling_ctx.frame_height;
	struct fpi_frame *frame =
	    g_malloc(frame_size + sizeof(struct fpi_frame));

236
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
237
238
239
240
241
242
243
244
245

	unsigned short min = 0xffff, max = 0;
	for (int i = 0; i < frame_size; i++) {
		if (raw_frame[i] < min)
			min = raw_frame[i];
		if (raw_frame[i] > max)
			max = raw_frame[i];
	}

246
247
	g_assert(max != min);

Igor Filatov's avatar
Igor Filatov committed
248
249
250
	unsigned short px;
	for (int i = 0; i < frame_size; i++) {
		px = raw_frame[i];
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
		px = (px - min) * 0xff / (max - min);
		frame->data[i] = (unsigned char)px;
	}

	*frames = g_slist_prepend(*frames, frame);
}

static void elan_process_frame_thirds(unsigned short *raw_frame,
				      GSList ** frames)
{
	G_DEBUG_HERE();

	unsigned int frame_size =
	    assembling_ctx.frame_width * assembling_ctx.frame_height;
	struct fpi_frame *frame =
	    g_malloc(frame_size + sizeof(struct fpi_frame));

	unsigned short lvl0, lvl1, lvl2, lvl3;
	unsigned short *sorted = g_malloc(frame_size * sizeof(short));
	memcpy(sorted, raw_frame, frame_size * sizeof(short));
	qsort(sorted, frame_size, sizeof(short), cmp_short);
	lvl0 = sorted[0];
	lvl1 = sorted[frame_size * 3 / 10];
	lvl2 = sorted[frame_size * 65 / 100];
	lvl3 = sorted[frame_size - 1];
	g_free(sorted);

	unsigned short px;
	for (int i = 0; i < frame_size; i++) {
		px = raw_frame[i];
		if (lvl0 <= px && px < lvl1)
			px = (px - lvl0) * 99 / (lvl1 - lvl0);
		else if (lvl1 <= px && px < lvl2)
			px = 99 + ((px - lvl1) * 56 / (lvl2 - lvl1));
		else		// (lvl2 <= px && px <= lvl3)
			px = 155 + ((px - lvl2) * 100 / (lvl3 - lvl2));
Igor Filatov's avatar
Igor Filatov committed
287
288
289
290
291
292
293
294
		frame->data[i] = (unsigned char)px;
	}

	*frames = g_slist_prepend(*frames, frame);
}

static void elan_submit_image(struct fp_img_dev *dev)
{
Bastien Nocera's avatar
Bastien Nocera committed
295
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
296
297
298
	GSList *frames = NULL;
	struct fp_img *img;

299
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
300
301
302

	for (int i = 0; i < ELAN_SKIP_LAST_FRAMES; i++)
		elandev->frames = g_slist_next(elandev->frames);
303
	elandev->num_frames -= ELAN_SKIP_LAST_FRAMES;
Igor Filatov's avatar
Igor Filatov committed
304
305
306
307

	assembling_ctx.frame_width = elandev->frame_width;
	assembling_ctx.frame_height = elandev->frame_height;
	assembling_ctx.image_width = elandev->frame_width * 3 / 2;
308
309
	g_slist_foreach(elandev->frames, (GFunc) elandev->process_frame,
			&frames);
Igor Filatov's avatar
Igor Filatov committed
310
	fpi_do_movement_estimation(&assembling_ctx, frames,
311
312
				   elandev->num_frames);
	img = fpi_assemble_frames(&assembling_ctx, frames, elandev->num_frames);
Igor Filatov's avatar
Igor Filatov committed
313
314
315
316
317

	img->flags |= FP_IMG_PARTIAL;
	fpi_imgdev_image_captured(dev, img);
}

Bastien Nocera's avatar
Bastien Nocera committed
318
static void elan_cmd_done(fpi_ssm *ssm)
Igor Filatov's avatar
Igor Filatov committed
319
{
320
	G_DEBUG_HERE();
321
	fpi_ssm_next_state(ssm);
Igor Filatov's avatar
Igor Filatov committed
322
323
}

Bastien Nocera's avatar
Bastien Nocera committed
324
325
326
327
static void elan_cmd_cb(struct libusb_transfer *transfer,
			struct fp_dev          *_dev,
			fpi_ssm                *ssm,
			void                   *user_data)
Igor Filatov's avatar
Igor Filatov committed
328
{
329
330
	struct fp_img_dev *dev;
	struct elan_dev *elandev;
Igor Filatov's avatar
Igor Filatov committed
331

332
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
333

334
335
336
337
338
339
340
	if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
		fp_dbg("transfer cancelled");
		return;
	}

	dev = FP_IMG_DEV(_dev);
	elandev = FP_INSTANCE_DATA(_dev);
Igor Filatov's avatar
Igor Filatov committed
341
342
343
344
345
	elandev->cur_transfer = NULL;

	switch (transfer->status) {
	case LIBUSB_TRANSFER_COMPLETED:
		if (transfer->length != transfer->actual_length) {
346
347
			fp_dbg("transfer length error: expected %d, got %d",
			       transfer->length, transfer->actual_length);
Igor Filatov's avatar
Igor Filatov committed
348
			elan_dev_reset(elandev);
349
			fpi_ssm_mark_failed(ssm, -EPROTO);
350
		} else if (transfer->endpoint & LIBUSB_ENDPOINT_IN) {
Igor Filatov's avatar
Igor Filatov committed
351
			/* just finished receiving */
Bastien Nocera's avatar
Bastien Nocera committed
352
			elandev->last_read = g_memdup(transfer->buffer, transfer->actual_length);
353
			dbg_buf(transfer->buffer, transfer->actual_length);
Igor Filatov's avatar
Igor Filatov committed
354
			elan_cmd_done(ssm);
355
		} else {
Igor Filatov's avatar
Igor Filatov committed
356
			/* just finished sending */
357
			G_DEBUG_HERE();
358
			elan_cmd_read(ssm, dev);
359
		}
Igor Filatov's avatar
Igor Filatov committed
360
361
362
		break;
	case LIBUSB_TRANSFER_TIMED_OUT:
		fp_dbg("transfer timed out");
363
		fpi_ssm_mark_failed(ssm, -ETIMEDOUT);
Igor Filatov's avatar
Igor Filatov committed
364
365
366
367
		break;
	default:
		fp_dbg("transfer failed: %d", transfer->status);
		elan_dev_reset(elandev);
368
		fpi_ssm_mark_failed(ssm, -EIO);
Igor Filatov's avatar
Igor Filatov committed
369
370
371
	}
}

372
static void elan_cmd_read(fpi_ssm *ssm, struct fp_img_dev *dev)
Igor Filatov's avatar
Igor Filatov committed
373
{
Bastien Nocera's avatar
Bastien Nocera committed
374
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
375
	int response_len = elandev->cmd->response_len;
Bastien Nocera's avatar
Bastien Nocera committed
376
	unsigned char *buffer;
Igor Filatov's avatar
Igor Filatov committed
377

378
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
379

380
	if (elandev->cmd->response_len == ELAN_CMD_SKIP_READ) {
381
382
383
384
385
386
		fp_dbg("skipping read, not expecting anything");
		elan_cmd_done(ssm);
		return;
	}

	if (elandev->cmd->cmd == get_image_cmd.cmd)
Igor Filatov's avatar
Igor Filatov committed
387
388
		/* raw data has 2-byte "pixels" and the frame is vertical */
		response_len =
389
		    elandev->raw_frame_height * elandev->frame_width * 2;
Igor Filatov's avatar
Igor Filatov committed
390

Bastien Nocera's avatar
Bastien Nocera committed
391
392
393
394
395
396
397
398
399
400
401
402
	g_clear_pointer(&elandev->last_read, g_free);
	buffer = g_malloc(response_len);

	elandev->cur_transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
							   ssm,
							   elandev->cmd->response_in,
							   buffer,
							   response_len,
							   elan_cmd_cb,
							   NULL,
							   elandev->cmd_timeout);
	int r = fpi_usb_submit_transfer(elandev->cur_transfer);
Igor Filatov's avatar
Igor Filatov committed
403
	if (r < 0)
404
		fpi_ssm_mark_failed(ssm, r);
Igor Filatov's avatar
Igor Filatov committed
405
406
}

407
408
409
410
411
static void
elan_run_cmd(fpi_ssm               *ssm,
	     struct fp_img_dev     *dev,
	     const struct elan_cmd *cmd,
	     int                    cmd_timeout)
Igor Filatov's avatar
Igor Filatov committed
412
{
Bastien Nocera's avatar
Bastien Nocera committed
413
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
414

415
416
	dbg_buf(cmd->cmd, 2);

417
418
419
420
	elandev->cmd = cmd;
	if (cmd_timeout != -1)
		elandev->cmd_timeout = cmd_timeout;

421
	if (cmd->devices != ELAN_ALL_DEV && !(cmd->devices & elandev->dev_type)) {
422
423
424
425
		fp_dbg("skipping for this device");
		elan_cmd_done(ssm);
		return;
	}
Igor Filatov's avatar
Igor Filatov committed
426

Bastien Nocera's avatar
Bastien Nocera committed
427
428
429
430
431
432
433
434
435
	elandev->cur_transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
							   ssm,
							   ELAN_EP_CMD_OUT,
							   g_memdup((char *) cmd->cmd, ELAN_CMD_LEN),
							   ELAN_CMD_LEN,
							   elan_cmd_cb,
							   NULL,
							   elandev->cmd_timeout);
	int r = fpi_usb_submit_transfer(elandev->cur_transfer);
Igor Filatov's avatar
Igor Filatov committed
436
	if (r < 0)
437
		fpi_ssm_mark_failed(ssm, r);
Igor Filatov's avatar
Igor Filatov committed
438
439
}

440
441
442
enum stop_capture_states {
	STOP_CAPTURE,
	STOP_CAPTURE_NUM_STATES,
Igor Filatov's avatar
Igor Filatov committed
443
444
};

445
static void stop_capture_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
446
{
447
448
	G_DEBUG_HERE();

449
	switch (fpi_ssm_get_cur_state(ssm)) {
450
	case STOP_CAPTURE:
451
		elan_run_cmd(ssm, FP_IMG_DEV(dev), &stop_cmd, ELAN_CMD_TIMEOUT);
Igor Filatov's avatar
Igor Filatov committed
452
453
454
455
		break;
	}
}

456
static void stop_capture_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
457
{
458
	struct fp_img_dev *dev = user_data;
459
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
460
	int error = fpi_ssm_get_error(ssm);
Igor Filatov's avatar
Igor Filatov committed
461

462
463
	G_DEBUG_HERE();

464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
	fpi_ssm_free(ssm);

	if (!error) {
		fpi_imgdev_report_finger_status(dev, FALSE);

		/* If verify or identify fails because of short swipe, we need to restart
		 * capture manually. It feels like libfprint or the application should know
		 * better if they want to retry, but they don't. Unless we've been asked to
		 * deactivate, try to re-enter the capture loop. Since state change is
		 * async, there's still a chance to be deactivated by another pending
		 * event. */
		if (elandev->dev_state_next != IMGDEV_STATE_INACTIVE)
			dev_change_state(dev, IMGDEV_STATE_AWAIT_FINGER_ON);

	} else if (error != -ECANCELED)
		fpi_imgdev_abort_scan(dev, error);
Igor Filatov's avatar
Igor Filatov committed
480
481
}

482
static void elan_stop_capture(struct fp_img_dev *dev)
Igor Filatov's avatar
Igor Filatov committed
483
{
Bastien Nocera's avatar
Bastien Nocera committed
484
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
485

486
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
487
488
489

	elan_dev_reset(elandev);

Bastien Nocera's avatar
Bastien Nocera committed
490
	fpi_ssm *ssm =
491
	    fpi_ssm_new(FP_DEV(dev), stop_capture_run_state,
492
			STOP_CAPTURE_NUM_STATES, dev);
493
	fpi_ssm_start(ssm, stop_capture_complete);
Igor Filatov's avatar
Igor Filatov committed
494
495
496
}

enum capture_states {
497
	CAPTURE_LED_ON,
Igor Filatov's avatar
Igor Filatov committed
498
499
	CAPTURE_WAIT_FINGER,
	CAPTURE_READ_DATA,
500
	CAPTURE_CHECK_ENOUGH_FRAMES,
Igor Filatov's avatar
Igor Filatov committed
501
502
503
	CAPTURE_NUM_STATES,
};

504
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
505
{
506
	struct fp_img_dev *dev = user_data;
507
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
508
	int r;
Igor Filatov's avatar
Igor Filatov committed
509

510
	switch (fpi_ssm_get_cur_state(ssm)) {
511
	case CAPTURE_LED_ON:
512
		elan_run_cmd(ssm, dev, &led_on_cmd, ELAN_CMD_TIMEOUT);
Igor Filatov's avatar
Igor Filatov committed
513
514
		break;
	case CAPTURE_WAIT_FINGER:
515
		elan_run_cmd(ssm, dev, &pre_scan_cmd, -1);
Igor Filatov's avatar
Igor Filatov committed
516
517
518
		break;
	case CAPTURE_READ_DATA:
		/* 0x55 - finger present
519
		 * 0xff - device not calibrated (probably) */
Igor Filatov's avatar
Igor Filatov committed
520
		if (elandev->last_read && elandev->last_read[0] == 0x55) {
521
522
			if (elandev->dev_state == IMGDEV_STATE_AWAIT_FINGER_ON)
				fpi_imgdev_report_finger_status(dev, TRUE);
523
			elan_run_cmd(ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT);
Igor Filatov's avatar
Igor Filatov committed
524
		} else
525
			fpi_ssm_mark_failed(ssm, -EBADMSG);
Igor Filatov's avatar
Igor Filatov committed
526
		break;
527
	case CAPTURE_CHECK_ENOUGH_FRAMES:
528
529
		r = elan_save_img_frame(elandev);
		if (r < 0)
530
			fpi_ssm_mark_failed(ssm, r);
531
		else if (elandev->num_frames < ELAN_MAX_FRAMES) {
Igor Filatov's avatar
Igor Filatov committed
532
533
534
			/* quickly stop if finger is removed */
			elandev->cmd_timeout = ELAN_FINGER_TIMEOUT;
			fpi_ssm_jump_to_state(ssm, CAPTURE_WAIT_FINGER);
535
536
		} else {
			fpi_ssm_next_state(ssm);
Igor Filatov's avatar
Igor Filatov committed
537
538
539
540
541
		}
		break;
	}
}

542
static void capture_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
543
{
544
	struct fp_img_dev *dev = user_data;
545
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
Igor Filatov's avatar
Igor Filatov committed
546

547
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
548

549
550
551
552
	if (fpi_ssm_get_error(ssm) == -ECANCELED) {
		fpi_ssm_free(ssm);
		return;
	}
Igor Filatov's avatar
Igor Filatov committed
553
554

	/* either max frames captured or timed out waiting for the next frame */
555
556
557
558
	if (!fpi_ssm_get_error(ssm)
	    || (fpi_ssm_get_error(ssm) == -ETIMEDOUT
		&& fpi_ssm_get_cur_state(ssm) == CAPTURE_WAIT_FINGER))
		if (elandev->num_frames >= ELAN_MIN_FRAMES)
Igor Filatov's avatar
Igor Filatov committed
559
			elan_submit_image(dev);
560
		else {
561
562
			fp_dbg("swipe too short: want >= %d frames, got %d",
			       ELAN_MIN_FRAMES, elandev->num_frames);
563
			fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_TOO_SHORT);
564
		}
Igor Filatov's avatar
Igor Filatov committed
565
566

	/* other error
567
568
	 * It says "...abort_scan" but reporting 1 during verification makes it
	 * successful! */
Igor Filatov's avatar
Igor Filatov committed
569
	else
570
571
		fpi_imgdev_abort_scan(dev, fpi_ssm_get_error(ssm));

Igor Filatov's avatar
Igor Filatov committed
572
573
574
575
576
	fpi_ssm_free(ssm);
}

static void elan_capture(struct fp_img_dev *dev)
{
Bastien Nocera's avatar
Bastien Nocera committed
577
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
578

579
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
580
581

	elan_dev_reset(elandev);
Bastien Nocera's avatar
Bastien Nocera committed
582
	fpi_ssm *ssm =
583
	    fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
Igor Filatov's avatar
Igor Filatov committed
584
585
586
	fpi_ssm_start(ssm, capture_complete);
}

587
588
/* this function needs to have elandev->background and elandev->last_read to be
 * the calibration mean */
589
590
591
592
593
594
595
596
597
static int elan_need_calibration(struct elan_dev *elandev)
{
	G_DEBUG_HERE();

	unsigned short calib_mean =
	    elandev->last_read[0] * 0xff + elandev->last_read[1];
	unsigned int bg_mean = 0, delta;
	unsigned int frame_size = elandev->frame_width * elandev->frame_height;

598
599
	g_assert(frame_size != 0);

600
	for (int i = 0; i < frame_size; i++)
601
		bg_mean += elandev->background[i];
602
603
604
605
606
607
608
609
610
611
612
	bg_mean /= frame_size;

	delta =
	    bg_mean > calib_mean ? bg_mean - calib_mean : calib_mean - bg_mean;

	fp_dbg("calibration mean: %d, bg mean: %d, delta: %d", calib_mean,
	       bg_mean, delta);

	return delta > ELAN_CALIBRATION_MAX_DELTA ? 1 : 0;
}

Igor Filatov's avatar
Igor Filatov committed
613
enum calibrate_states {
614
615
616
	CALIBRATE_GET_BACKGROUND,
	CALIBRATE_SAVE_BACKGROUND,
	CALIBRATE_GET_MEAN,
617
618
619
620
	CALIBRATE_CHECK_NEEDED,
	CALIBRATE_GET_STATUS,
	CALIBRATE_CHECK_STATUS,
	CALIBRATE_REPEAT_STATUS,
Igor Filatov's avatar
Igor Filatov committed
621
622
623
	CALIBRATE_NUM_STATES,
};

624
static void calibrate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
625
{
626
	struct fp_img_dev *dev = user_data;
627
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
628
629
630

	G_DEBUG_HERE();

631
	switch (fpi_ssm_get_cur_state(ssm)) {
632
	case CALIBRATE_GET_BACKGROUND:
633
		elan_run_cmd(ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT);
634
635
636
637
638
639
640
641
642
643
		break;
	case CALIBRATE_SAVE_BACKGROUND:
		elan_save_background(elandev);
		if (elandev->fw_ver < ELAN_MIN_CALIBRATION_FW) {
			fp_dbg("FW does not support calibration");
			fpi_ssm_mark_completed(ssm);
		} else
			fpi_ssm_next_state(ssm);
		break;
	case CALIBRATE_GET_MEAN:
644
		elan_run_cmd(ssm, dev, &get_calib_mean_cmd, ELAN_CMD_TIMEOUT);
645
646
647
648
649
650
651
652
653
		break;
	case CALIBRATE_CHECK_NEEDED:
		if (elan_need_calibration(elandev)) {
			elandev->calib_status = 0;
			fpi_ssm_next_state(ssm);
		} else
			fpi_ssm_mark_completed(ssm);
		break;
	case CALIBRATE_GET_STATUS:
654
655
		elandev->calib_atts_left -= 1;
		if (elandev->calib_atts_left)
656
			elan_run_cmd(ssm, dev, &get_calib_status_cmd,
657
658
				     ELAN_CMD_TIMEOUT);
		else {
659
			fp_dbg("calibration failed");
660
			fpi_ssm_mark_failed(ssm, -1);
661
662
		}
		break;
663
	case CALIBRATE_CHECK_STATUS:
664
		/* 0x01 - retry, 0x03 - ok
665
666
667
668
669
		 * It appears that when reading the response soon after 0x4023 the device
		 * can return 0x03, and only after some time (up to 100 ms) the response
		 * changes to 0x01. It stays that way for some time and then changes back
		 * to 0x03. Because of this we don't just expect 0x03, we want to see 0x01
		 * first. This is to make sure that a full calibration loop has completed */
670
		fp_dbg("calibration status: 0x%02x", elandev->last_read[0]);
671
672
673
674
675
		if (elandev->calib_status == 0x01
		    && elandev->last_read[0] == 0x03) {
			elandev->calib_status = 0x03;
			fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_BACKGROUND);
		} else {
Bastien Nocera's avatar
Bastien Nocera committed
676
677
			fpi_timeout *timeout;

678
679
680
			if (elandev->calib_status == 0x00
			    && elandev->last_read[0] == 0x01)
				elandev->calib_status = 0x01;
Bastien Nocera's avatar
Bastien Nocera committed
681
682
			timeout = fpi_timeout_add(50, fpi_ssm_next_state_timeout_cb, _dev, ssm);
			fpi_timeout_set_name(timeout, "calibrate_run_state");
683
		}
684
		break;
685
686
	case CALIBRATE_REPEAT_STATUS:
		fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_STATUS);
Igor Filatov's avatar
Igor Filatov committed
687
688
689
690
		break;
	}
}

691
static void calibrate_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
692
{
693
	struct fp_img_dev *dev = user_data;
Igor Filatov's avatar
Igor Filatov committed
694

695
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
696

697
698

	if (fpi_ssm_get_error(ssm) != -ECANCELED)
699
		elan_capture(dev);
700

Igor Filatov's avatar
Igor Filatov committed
701
702
703
704
705
	fpi_ssm_free(ssm);
}

static void elan_calibrate(struct fp_img_dev *dev)
{
Bastien Nocera's avatar
Bastien Nocera committed
706
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
707

708
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
709
710

	elan_dev_reset(elandev);
711
712
	elandev->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS;

713
	fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), calibrate_run_state,
714
					  CALIBRATE_NUM_STATES, dev);
Igor Filatov's avatar
Igor Filatov committed
715
716
717
718
	fpi_ssm_start(ssm, calibrate_complete);
}

enum activate_states {
719
	ACTIVATE_GET_FW_VER,
720
	ACTIVATE_SET_FW_VER,
Igor Filatov's avatar
Igor Filatov committed
721
722
	ACTIVATE_GET_SENSOR_DIM,
	ACTIVATE_SET_SENSOR_DIM,
723
	ACTIVATE_CMD_1,
Igor Filatov's avatar
Igor Filatov committed
724
725
726
	ACTIVATE_NUM_STATES,
};

727
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
728
{
729
	struct fp_img_dev *dev = user_data;
730
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
Igor Filatov's avatar
Igor Filatov committed
731

732
733
	G_DEBUG_HERE();

734
	switch (fpi_ssm_get_cur_state(ssm)) {
735
	case ACTIVATE_GET_FW_VER:
736
		elan_run_cmd(ssm, dev, &get_fw_ver_cmd, ELAN_CMD_TIMEOUT);
737
		break;
738
739
740
741
	case ACTIVATE_SET_FW_VER:
		elandev->fw_ver =
		    (elandev->last_read[0] << 8 | elandev->last_read[1]);
		fp_dbg("FW ver 0x%04hx", elandev->fw_ver);
742
743
		fpi_ssm_next_state(ssm);
		break;
Igor Filatov's avatar
Igor Filatov committed
744
	case ACTIVATE_GET_SENSOR_DIM:
745
		elan_run_cmd(ssm, dev, &get_sensor_dim_cmd, ELAN_CMD_TIMEOUT);
Igor Filatov's avatar
Igor Filatov committed
746
747
		break;
	case ACTIVATE_SET_SENSOR_DIM:
748
749
750
751
752
753
754
755
756
757
		/* see elan_save_frame for details */
		if (elandev->dev_type & ELAN_NOT_ROTATED) {
			elandev->frame_width = elandev->last_read[0];
			elandev->frame_height = elandev->raw_frame_height =
			    elandev->last_read[2];
		} else {
			elandev->frame_width = elandev->last_read[2];
			elandev->frame_height = elandev->raw_frame_height =
			    elandev->last_read[0];
		}
758
759
760
761
762
763
764
765
		/* Work-around sensors returning the sizes as zero-based index
		 * rather than the number of pixels. */
		if ((elandev->frame_width % 2 == 1) &&
		    (elandev->frame_height % 2 == 1)) {
			elandev->frame_width++;
			elandev->frame_height++;
			elandev->raw_frame_height = elandev->frame_height;
		}
766
		if (elandev->frame_height > ELAN_MAX_FRAME_HEIGHT)
767
			elandev->frame_height = ELAN_MAX_FRAME_HEIGHT;
768
		fp_dbg("sensor dimensions, WxH: %dx%d", elandev->frame_width,
769
		       elandev->raw_frame_height);
Igor Filatov's avatar
Igor Filatov committed
770
771
		fpi_ssm_next_state(ssm);
		break;
772
773
	case ACTIVATE_CMD_1:
		/* TODO: find out what this does, if we need it */
774
		elan_run_cmd(ssm, dev, &activate_cmd_1, ELAN_CMD_TIMEOUT);
Igor Filatov's avatar
Igor Filatov committed
775
776
777
778
		break;
	}
}

779
static void activate_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
780
{
781
	struct fp_img_dev *dev = user_data;
Igor Filatov's avatar
Igor Filatov committed
782

783
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
784

785
	if (fpi_ssm_get_error(ssm) != -ECANCELED) {
786
		fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
787
	}
788

Igor Filatov's avatar
Igor Filatov committed
789
790
791
	fpi_ssm_free(ssm);
}

792
static void elan_activate(struct fp_img_dev *dev)
Igor Filatov's avatar
Igor Filatov committed
793
{
Bastien Nocera's avatar
Bastien Nocera committed
794
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
795

796
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
797
	elan_dev_reset(elandev);
798

Bastien Nocera's avatar
Bastien Nocera committed
799
	fpi_ssm *ssm =
800
	    fpi_ssm_new(FP_DEV(dev), activate_run_state, ACTIVATE_NUM_STATES, dev);
Igor Filatov's avatar
Igor Filatov committed
801
802
803
804
805
806
807
808
	fpi_ssm_start(ssm, activate_complete);
}

static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
	struct elan_dev *elandev;
	int r;

809
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
810

811
	r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
Igor Filatov's avatar
Igor Filatov committed
812
813
814
815
816
	if (r < 0) {
		fp_err("could not claim interface 0: %s", libusb_error_name(r));
		return r;
	}

817
	elandev = g_malloc0(sizeof(struct elan_dev));
Bastien Nocera's avatar
Bastien Nocera committed
818
	fp_dev_set_instance_data(FP_DEV(dev), elandev);
819
820
821

	/* common params */
	elandev->dev_type = driver_data;
822
823
	elandev->background = NULL;
	elandev->process_frame = elan_process_frame_thirds;
824
825
826

	switch (driver_data) {
	case ELAN_0907:
827
		elandev->process_frame = elan_process_frame_linear;
828
829
830
		break;
	}

Igor Filatov's avatar
Igor Filatov committed
831
832
833
834
	fpi_imgdev_open_complete(dev, 0);
	return 0;
}

835
836
837
838
839
840
841
static void elan_deactivate(struct fp_img_dev *dev)
{
	G_DEBUG_HERE();

	fpi_imgdev_deactivate_complete(dev);
}

Igor Filatov's avatar
Igor Filatov committed
842
843
static void dev_deinit(struct fp_img_dev *dev)
{
Bastien Nocera's avatar
Bastien Nocera committed
844
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
845

846
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
847
848

	elan_dev_reset(elandev);
849
	g_free(elandev->background);
Igor Filatov's avatar
Igor Filatov committed
850
	g_free(elandev);
851
	libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
Igor Filatov's avatar
Igor Filatov committed
852
853
854
	fpi_imgdev_close_complete(dev);
}

855
856
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
857
858
	G_DEBUG_HERE();
	elan_activate(dev);
859
860
861
	return 0;
}

862
863
static void elan_change_state(struct fp_img_dev *dev)
{
Bastien Nocera's avatar
Bastien Nocera committed
864
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
865
866
867
868
869
870
871
872
873
874
875
876
	enum fp_imgdev_state next_state = elandev->dev_state_next;

	if (elandev->dev_state == next_state) {
		fp_dbg("already in %d", next_state);
		return;
	} else
		fp_dbg("changing to %d", next_state);

	switch (next_state) {
	case IMGDEV_STATE_INACTIVE:
		if (elandev->cur_transfer)
			/* deactivation will complete in transfer callback */
Bastien Nocera's avatar
Bastien Nocera committed
877
			fpi_usb_cancel_transfer(elandev->cur_transfer);
878
879
880
881
882
		else
			elan_deactivate(dev);
		break;
	case IMGDEV_STATE_AWAIT_FINGER_ON:
		/* activation completed or another enroll stage started */
883
		elan_calibrate(dev);
884
885
		break;
	case IMGDEV_STATE_CAPTURE:
886
		/* not used */
887
		break;
888
889
	case IMGDEV_STATE_AWAIT_FINGER_OFF:
		elan_stop_capture(dev);
890
891
892
893
894
	}

	elandev->dev_state = next_state;
}

895
896
897
static void
elan_change_state_async(struct fp_dev *dev,
			void          *data)
898
{
Bastien Nocera's avatar
Bastien Nocera committed
899
	g_message ("state change dev: %p", dev);
900
	elan_change_state(FP_IMG_DEV (dev));
901
902
903
}

static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state)
Igor Filatov's avatar
Igor Filatov committed
904
{
Bastien Nocera's avatar
Bastien Nocera committed
905
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Bastien Nocera's avatar
Bastien Nocera committed
906
	fpi_timeout *timeout;
Igor Filatov's avatar
Igor Filatov committed
907

908
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
909

910
911
912
	switch (state) {
	case IMGDEV_STATE_INACTIVE:
	case IMGDEV_STATE_AWAIT_FINGER_ON:
Bastien Nocera's avatar
Bastien Nocera committed
913
914
915
	case IMGDEV_STATE_AWAIT_FINGER_OFF: {
		char *name;

916
917
918
		/* schedule state change instead of calling it directly to allow all actions
		 * related to the previous state to complete */
		elandev->dev_state_next = state;
Bastien Nocera's avatar
Bastien Nocera committed
919
920
921
922
923
924
		timeout = fpi_timeout_add(10, elan_change_state_async, FP_DEV(dev), NULL);

		name = g_strdup_printf ("dev_change_state to %d", state);
		fpi_timeout_set_name(timeout, name);
		g_free (name);

925
		break;
Bastien Nocera's avatar
Bastien Nocera committed
926
		}
927
	case IMGDEV_STATE_CAPTURE:
928
		/* TODO MAYBE: split capture ssm into smaller ssms and use this state */
929
930
931
932
933
934
935
936
937
938
939
940
		elandev->dev_state = state;
		elandev->dev_state_next = state;
		break;
	default:
		fp_err("unrecognized state %d", state);
		fpi_imgdev_session_error(dev, -EINVAL);
		return -EINVAL;
	}

	/* as of time of writing libfprint never checks the return value */
	return 0;
}
Igor Filatov's avatar
Igor Filatov committed
941

942
943
944
945
946
static void dev_deactivate(struct fp_img_dev *dev)
{
	G_DEBUG_HERE();

	dev_change_state(dev, IMGDEV_STATE_INACTIVE);
Igor Filatov's avatar
Igor Filatov committed
947
948
949
950
951
952
953
}

struct fp_img_driver elan_driver = {
	.driver = {
		   .id = ELAN_ID,
		   .name = FP_COMPONENT,
		   .full_name = "ElanTech Fingerprint Sensor",
Igor Filatov's avatar
Igor Filatov committed
954
		   .id_table = elan_id_table,
Igor Filatov's avatar
Igor Filatov committed
955
956
957
958
		   .scan_type = FP_SCAN_TYPE_SWIPE,
		   },
	.flags = 0,

959
	.bz3_threshold = 24,
Igor Filatov's avatar
Igor Filatov committed
960
961
962
963
964

	.open = dev_init,
	.close = dev_deinit,
	.activate = dev_activate,
	.deactivate = dev_deactivate,
965
	.change_state = dev_change_state,
Igor Filatov's avatar
Igor Filatov committed
966
};