elan.c 27.1 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)
{
295
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
296 297
	int num_frames;
	GSList *raw_frames;
Igor Filatov's avatar
Igor Filatov committed
298 299 300
	GSList *frames = NULL;
	struct fp_img *img;

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

303 304
	num_frames = elandev->num_frames - ELAN_SKIP_LAST_FRAMES;
	raw_frames = g_slist_nth(elandev->frames, ELAN_SKIP_LAST_FRAMES);
Igor Filatov's avatar
Igor Filatov committed
305 306 307 308

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

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

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

Bastien Nocera's avatar
Bastien Nocera committed
323 324 325 326
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
327
{
328 329
	struct fp_img_dev *dev;
	struct elan_dev *elandev;
Igor Filatov's avatar
Igor Filatov committed
330

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

333 334 335 336 337 338 339
	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
340 341 342 343 344
	elandev->cur_transfer = NULL;

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

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

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

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

385 386 387 388 389 390
	if (elandev->dev_type == ELAN_0C42) {
		/* ELAN_0C42 sends an extra byte in one byte responses */
		if (elandev->cmd->response_len == 1)
			response_len = 2;
	}

391
	if (elandev->cmd->cmd == get_image_cmd.cmd)
Igor Filatov's avatar
Igor Filatov committed
392 393
		/* raw data has 2-byte "pixels" and the frame is vertical */
		response_len =
394
		    elandev->raw_frame_height * elandev->frame_width * 2;
Igor Filatov's avatar
Igor Filatov committed
395

Bastien Nocera's avatar
Bastien Nocera committed
396 397 398 399 400 401 402 403 404 405 406 407
	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
408
	if (r < 0)
409
		fpi_ssm_mark_failed(ssm, r);
Igor Filatov's avatar
Igor Filatov committed
410 411
}

412 413 414 415 416
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
417
{
418
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
419

420 421
	dbg_buf(cmd->cmd, 2);

422 423 424 425
	elandev->cmd = cmd;
	if (cmd_timeout != -1)
		elandev->cmd_timeout = cmd_timeout;

426
	if (cmd->devices != ELAN_ALL_DEV && !(cmd->devices & elandev->dev_type)) {
427 428
		fp_dbg("skipping command 0x%x 0x%x for this device (for devices 0x%x but device is 0x%x)",
		       cmd->cmd[0], cmd->cmd[1], cmd->devices, elandev->dev_type);
429 430 431
		elan_cmd_done(ssm);
		return;
	}
Igor Filatov's avatar
Igor Filatov committed
432

Bastien Nocera's avatar
Bastien Nocera committed
433 434 435 436 437 438 439 440 441
	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
442
	if (r < 0)
443
		fpi_ssm_mark_failed(ssm, r);
Igor Filatov's avatar
Igor Filatov committed
444 445
}

446 447 448
enum stop_capture_states {
	STOP_CAPTURE,
	STOP_CAPTURE_NUM_STATES,
Igor Filatov's avatar
Igor Filatov committed
449 450
};

451
static void stop_capture_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
452
{
453 454
	G_DEBUG_HERE();

455
	switch (fpi_ssm_get_cur_state(ssm)) {
456
	case STOP_CAPTURE:
457
		elan_run_cmd(ssm, FP_IMG_DEV(dev), &stop_cmd, ELAN_CMD_TIMEOUT);
Igor Filatov's avatar
Igor Filatov committed
458 459 460 461
		break;
	}
}

462
static void stop_capture_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
463
{
464
	struct fp_img_dev *dev = user_data;
465
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
466
	int error = fpi_ssm_get_error(ssm);
Igor Filatov's avatar
Igor Filatov committed
467

468 469
	G_DEBUG_HERE();

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
	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
486 487
}

488
static void elan_stop_capture(struct fp_img_dev *dev)
Igor Filatov's avatar
Igor Filatov committed
489
{
490
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
491

492
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
493 494 495

	elan_dev_reset(elandev);

Bastien Nocera's avatar
Bastien Nocera committed
496
	fpi_ssm *ssm =
497
	    fpi_ssm_new(FP_DEV(dev), stop_capture_run_state,
498
			STOP_CAPTURE_NUM_STATES, dev);
499
	fpi_ssm_start(ssm, stop_capture_complete);
Igor Filatov's avatar
Igor Filatov committed
500 501 502
}

enum capture_states {
503
	CAPTURE_LED_ON,
Igor Filatov's avatar
Igor Filatov committed
504 505
	CAPTURE_WAIT_FINGER,
	CAPTURE_READ_DATA,
506
	CAPTURE_CHECK_ENOUGH_FRAMES,
Igor Filatov's avatar
Igor Filatov committed
507 508 509
	CAPTURE_NUM_STATES,
};

510
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
511
{
512
	struct fp_img_dev *dev = user_data;
513
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
514
	int r;
Igor Filatov's avatar
Igor Filatov committed
515

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

548
static void capture_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
549
{
550
	struct fp_img_dev *dev = user_data;
551
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
Igor Filatov's avatar
Igor Filatov committed
552

553
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
554

555 556 557 558
	if (fpi_ssm_get_error(ssm) == -ECANCELED) {
		fpi_ssm_free(ssm);
		return;
	}
Igor Filatov's avatar
Igor Filatov committed
559 560

	/* either max frames captured or timed out waiting for the next frame */
561 562 563 564
	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
565
			elan_submit_image(dev);
566
		else {
567 568
			fp_dbg("swipe too short: want >= %d frames, got %d",
			       ELAN_MIN_FRAMES, elandev->num_frames);
569
			fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_TOO_SHORT);
570
		}
Igor Filatov's avatar
Igor Filatov committed
571 572

	/* other error
573 574
	 * It says "...abort_scan" but reporting 1 during verification makes it
	 * successful! */
Igor Filatov's avatar
Igor Filatov committed
575
	else
576 577
		fpi_imgdev_abort_scan(dev, fpi_ssm_get_error(ssm));

Igor Filatov's avatar
Igor Filatov committed
578 579 580 581 582
	fpi_ssm_free(ssm);
}

static void elan_capture(struct fp_img_dev *dev)
{
583
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
584

585
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
586 587

	elan_dev_reset(elandev);
Bastien Nocera's avatar
Bastien Nocera committed
588
	fpi_ssm *ssm =
589
	    fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
Igor Filatov's avatar
Igor Filatov committed
590 591 592
	fpi_ssm_start(ssm, capture_complete);
}

593 594
/* this function needs to have elandev->background and elandev->last_read to be
 * the calibration mean */
595 596 597 598 599 600 601 602 603
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;

604 605
	g_assert(frame_size != 0);

606 607 608 609 610 611 612 613
	if (elandev->dev_type == ELAN_0C42) {
		if (calib_mean > 5500 ||
		    calib_mean < 2500) {
			fp_dbg("Forcing needed recalibration");
			return 1;
		}
	}

614
	for (int i = 0; i < frame_size; i++)
615
		bg_mean += elandev->background[i];
616 617 618 619 620 621 622 623 624 625 626
	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
627
enum calibrate_states {
628 629 630
	CALIBRATE_GET_BACKGROUND,
	CALIBRATE_SAVE_BACKGROUND,
	CALIBRATE_GET_MEAN,
631 632 633 634
	CALIBRATE_CHECK_NEEDED,
	CALIBRATE_GET_STATUS,
	CALIBRATE_CHECK_STATUS,
	CALIBRATE_REPEAT_STATUS,
Igor Filatov's avatar
Igor Filatov committed
635 636 637
	CALIBRATE_NUM_STATES,
};

638 639 640 641 642 643 644 645
static gboolean elan_supports_calibration(struct elan_dev *elandev)
{
	if (elandev->dev_type == ELAN_0C42)
		return TRUE;

	return elandev->fw_ver >= ELAN_MIN_CALIBRATION_FW;
}

646
static void calibrate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
647
{
648
	struct fp_img_dev *dev = user_data;
649
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
650 651 652

	G_DEBUG_HERE();

653
	switch (fpi_ssm_get_cur_state(ssm)) {
654
	case CALIBRATE_GET_BACKGROUND:
655
		elan_run_cmd(ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT);
656 657 658
		break;
	case CALIBRATE_SAVE_BACKGROUND:
		elan_save_background(elandev);
659
		if (!elan_supports_calibration(elandev)) {
660 661 662 663 664 665
			fp_dbg("FW does not support calibration");
			fpi_ssm_mark_completed(ssm);
		} else
			fpi_ssm_next_state(ssm);
		break;
	case CALIBRATE_GET_MEAN:
666
		elan_run_cmd(ssm, dev, &get_calib_mean_cmd, ELAN_CMD_TIMEOUT);
667 668 669 670 671 672 673 674 675
		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:
676 677
		elandev->calib_atts_left -= 1;
		if (elandev->calib_atts_left)
678
			elan_run_cmd(ssm, dev, &get_calib_status_cmd,
679 680
				     ELAN_CMD_TIMEOUT);
		else {
681
			fp_dbg("calibration failed");
682
			fpi_ssm_mark_failed(ssm, -1);
683 684
		}
		break;
685
	case CALIBRATE_CHECK_STATUS:
686
		/* 0x01 - retry, 0x03 - ok
687 688 689 690 691
		 * 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 */
692
		fp_dbg("calibration status: 0x%02x", elandev->last_read[0]);
693 694 695 696 697
		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
698 699
			fpi_timeout *timeout;

700 701 702
			if (elandev->calib_status == 0x00
			    && elandev->last_read[0] == 0x01)
				elandev->calib_status = 0x01;
Bastien Nocera's avatar
Bastien Nocera committed
703 704
			timeout = fpi_timeout_add(50, fpi_ssm_next_state_timeout_cb, _dev, ssm);
			fpi_timeout_set_name(timeout, "calibrate_run_state");
705
		}
706
		break;
707 708
	case CALIBRATE_REPEAT_STATUS:
		fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_STATUS);
Igor Filatov's avatar
Igor Filatov committed
709 710 711 712
		break;
	}
}

713
static void calibrate_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
714
{
715
	struct fp_img_dev *dev = user_data;
Igor Filatov's avatar
Igor Filatov committed
716

717
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
718

719 720

	if (fpi_ssm_get_error(ssm) != -ECANCELED)
721
		elan_capture(dev);
722

Igor Filatov's avatar
Igor Filatov committed
723 724 725 726 727
	fpi_ssm_free(ssm);
}

static void elan_calibrate(struct fp_img_dev *dev)
{
728
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
729

730
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
731 732

	elan_dev_reset(elandev);
733 734
	elandev->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS;

735
	fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), calibrate_run_state,
736
					  CALIBRATE_NUM_STATES, dev);
Igor Filatov's avatar
Igor Filatov committed
737 738 739 740
	fpi_ssm_start(ssm, calibrate_complete);
}

enum activate_states {
741
	ACTIVATE_GET_FW_VER,
742
	ACTIVATE_SET_FW_VER,
Igor Filatov's avatar
Igor Filatov committed
743 744
	ACTIVATE_GET_SENSOR_DIM,
	ACTIVATE_SET_SENSOR_DIM,
745
	ACTIVATE_CMD_1,
Igor Filatov's avatar
Igor Filatov committed
746 747 748
	ACTIVATE_NUM_STATES,
};

749
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
750
{
751
	struct fp_img_dev *dev = user_data;
752
	struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
Igor Filatov's avatar
Igor Filatov committed
753

754 755
	G_DEBUG_HERE();

756
	switch (fpi_ssm_get_cur_state(ssm)) {
757
	case ACTIVATE_GET_FW_VER:
758
		elan_run_cmd(ssm, dev, &get_fw_ver_cmd, ELAN_CMD_TIMEOUT);
759
		break;
760 761 762 763
	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);
764 765
		fpi_ssm_next_state(ssm);
		break;
Igor Filatov's avatar
Igor Filatov committed
766
	case ACTIVATE_GET_SENSOR_DIM:
767
		elan_run_cmd(ssm, dev, &get_sensor_dim_cmd, ELAN_CMD_TIMEOUT);
Igor Filatov's avatar
Igor Filatov committed
768 769
		break;
	case ACTIVATE_SET_SENSOR_DIM:
770 771 772 773 774 775 776 777 778 779
		/* 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];
		}
780 781 782 783 784 785 786 787
		/* 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;
		}
788
		if (elandev->frame_height > ELAN_MAX_FRAME_HEIGHT)
789
			elandev->frame_height = ELAN_MAX_FRAME_HEIGHT;
790
		fp_dbg("sensor dimensions, WxH: %dx%d", elandev->frame_width,
791
		       elandev->raw_frame_height);
Igor Filatov's avatar
Igor Filatov committed
792 793
		fpi_ssm_next_state(ssm);
		break;
794 795
	case ACTIVATE_CMD_1:
		/* TODO: find out what this does, if we need it */
796
		elan_run_cmd(ssm, dev, &activate_cmd_1, ELAN_CMD_TIMEOUT);
Igor Filatov's avatar
Igor Filatov committed
797 798 799 800
		break;
	}
}

801
static void activate_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
Igor Filatov's avatar
Igor Filatov committed
802
{
803
	struct fp_img_dev *dev = user_data;
Igor Filatov's avatar
Igor Filatov committed
804

805
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
806

807
	if (fpi_ssm_get_error(ssm) != -ECANCELED) {
808
		fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
809
	}
810

Igor Filatov's avatar
Igor Filatov committed
811 812 813
	fpi_ssm_free(ssm);
}

814
static void elan_activate(struct fp_img_dev *dev)
Igor Filatov's avatar
Igor Filatov committed
815
{
816
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
817

818
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
819
	elan_dev_reset(elandev);
820

Bastien Nocera's avatar
Bastien Nocera committed
821
	fpi_ssm *ssm =
822
	    fpi_ssm_new(FP_DEV(dev), activate_run_state, ACTIVATE_NUM_STATES, dev);
Igor Filatov's avatar
Igor Filatov committed
823 824 825 826 827 828 829 830
	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;

831
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
832

833
	r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
Igor Filatov's avatar
Igor Filatov committed
834 835 836 837 838
	if (r < 0) {
		fp_err("could not claim interface 0: %s", libusb_error_name(r));
		return r;
	}

839
	elandev = g_malloc0(sizeof(struct elan_dev));
840
	fp_dev_set_instance_data(FP_DEV(dev), elandev);
841 842 843

	/* common params */
	elandev->dev_type = driver_data;
844 845
	elandev->background = NULL;
	elandev->process_frame = elan_process_frame_thirds;
846 847 848

	switch (driver_data) {
	case ELAN_0907:
849
		elandev->process_frame = elan_process_frame_linear;
850 851 852
		break;
	}

Igor Filatov's avatar
Igor Filatov committed
853 854 855 856
	fpi_imgdev_open_complete(dev, 0);
	return 0;
}

857 858 859 860 861 862 863
static void elan_deactivate(struct fp_img_dev *dev)
{
	G_DEBUG_HERE();

	fpi_imgdev_deactivate_complete(dev);
}

Igor Filatov's avatar
Igor Filatov committed
864 865
static void dev_deinit(struct fp_img_dev *dev)
{
866
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Igor Filatov's avatar
Igor Filatov committed
867

868
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
869 870

	elan_dev_reset(elandev);
871
	g_free(elandev->background);
Igor Filatov's avatar
Igor Filatov committed
872
	g_free(elandev);
873
	libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
Igor Filatov's avatar
Igor Filatov committed
874 875 876
	fpi_imgdev_close_complete(dev);
}

877
static int dev_activate(struct fp_img_dev *dev)
878
{
879 880
	G_DEBUG_HERE();
	elan_activate(dev);
881 882 883
	return 0;
}

884 885
static void elan_change_state(struct fp_img_dev *dev)
{
886
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
887 888 889 890 891 892 893 894 895 896 897 898
	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
899
			fpi_usb_cancel_transfer(elandev->cur_transfer);
900 901 902 903 904
		else
			elan_deactivate(dev);
		break;
	case IMGDEV_STATE_AWAIT_FINGER_ON:
		/* activation completed or another enroll stage started */
905
		elan_calibrate(dev);
906 907
		break;
	case IMGDEV_STATE_CAPTURE:
908
		/* not used */
909
		break;
910 911
	case IMGDEV_STATE_AWAIT_FINGER_OFF:
		elan_stop_capture(dev);
912 913 914 915 916
	}

	elandev->dev_state = next_state;
}

917 918 919
static void
elan_change_state_async(struct fp_dev *dev,
			void          *data)
920
{
Bastien Nocera's avatar
Bastien Nocera committed
921
	g_message ("state change dev: %p", dev);
922
	elan_change_state(FP_IMG_DEV (dev));
923 924 925
}

static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state)
Igor Filatov's avatar
Igor Filatov committed
926
{
927
	struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
Bastien Nocera's avatar
Bastien Nocera committed
928
	fpi_timeout *timeout;
Igor Filatov's avatar
Igor Filatov committed
929

930
	G_DEBUG_HERE();
Igor Filatov's avatar
Igor Filatov committed
931

932 933 934
	switch (state) {
	case IMGDEV_STATE_INACTIVE:
	case IMGDEV_STATE_AWAIT_FINGER_ON:
Bastien Nocera's avatar
Bastien Nocera committed
935 936 937
	case IMGDEV_STATE_AWAIT_FINGER_OFF: {
		char *name;

938 939 940
		/* 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
941 942 943 944 945 946
		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);

947
		break;
Bastien Nocera's avatar
Bastien Nocera committed
948
		}
949
	case IMGDEV_STATE_CAPTURE:
950
		/* TODO MAYBE: split capture ssm into smaller ssms and use this state */
951 952 953 954 955 956 957 958 959 960 961 962
		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
963

964 965 966 967 968
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
969 970 971 972 973 974 975
}

struct fp_img_driver elan_driver = {
	.driver = {
		   .id = ELAN_ID,
		   .name = FP_COMPONENT,
		   .full_name = "ElanTech Fingerprint Sensor",
976
		   .id_table = elan_id_table,
Igor Filatov's avatar
Igor Filatov committed
977 978 979 980
		   .scan_type = FP_SCAN_TYPE_SWIPE,
		   },
	.flags = 0,

981
	.bz3_threshold = 24,
Igor Filatov's avatar
Igor Filatov committed
982 983 984 985 986

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