vfs101.c 35.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
 * Validity VFS101 driver for libfprint
 * Copyright (C) 2011 Sergio Cerlesi <sergio.cerlesi@gmail.com>
 *
 * 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
 */

20
21
#define FP_COMPONENT "vfs101"

22
#include "drivers_api.h"
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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
149
150
151
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
/* Input-Output usb endpoint */
#define EP_IN(n)	(n | LIBUSB_ENDPOINT_IN)
#define EP_OUT(n)	(n | LIBUSB_ENDPOINT_OUT)

/* Usb bulk timeout */
#define BULK_TIMEOUT		100

/* The device send back the image into block of 16 frames of 292 bytes */
#define VFS_FRAME_SIZE		292
#define VFS_BLOCK_SIZE		16 * VFS_FRAME_SIZE

/* Buffer height */
#define VFS_BUFFER_HEIGHT	5000

/* Buffer size */
#define VFS_BUFFER_SIZE		(VFS_BUFFER_HEIGHT * VFS_FRAME_SIZE)

/* Image width */
#define VFS_IMG_WIDTH		200

/* Maximum image height */
#define VFS_IMG_MAX_HEIGHT	1023

/* Minimum image height */
#define VFS_IMG_MIN_HEIGHT	200

/* Scan level thresold */
#define VFS_IMG_SLT_BEGIN		768
#define VFS_IMG_SLT_END			64
#define VFS_IMG_SLT_LINES		4

/* Minimum image level */
#define VFS_IMG_MIN_IMAGE_LEVEL	144

/* Best image contrast */
#define VFS_IMG_BEST_CONRAST	128

/* Device parameters address */
#define VFS_PAR_000E			0x000e
#define VFS_PAR_0011			0x0011
#define VFS_PAR_THRESHOLD		0x0057
#define VFS_PAR_STATE_3			0x005e
#define VFS_PAR_STATE_5			0x005f
#define VFS_PAR_INFO_RATE		0x0062
#define VFS_PAR_0076			0x0076
#define VFS_PAR_INFO_CONTRAST	0x0077
#define VFS_PAR_0078			0x0078

/* Device regiones address */
#define VFS_REG_IMG_EXPOSURE	0xff500e
#define VFS_REG_IMG_CONTRAST	0xff5038

/* Device settings */
#define VFS_VAL_000E			0x0001
#define VFS_VAL_0011			0x0008
#define VFS_VAL_THRESHOLD		0x0096
#define VFS_VAL_STATE_3			0x0064
#define VFS_VAL_STATE_5			0x00c8
#define VFS_VAL_INFO_RATE		0x0001
#define VFS_VAL_0076			0x0012
#define VFS_VAL_0078			0x2230
#define VFS_VAL_IMG_EXPOSURE	0x21c0

/* Structure for Validity device */
struct vfs101_dev
{
	/* Action state */
	int active;

	/* Sequential number */
	unsigned int seqnum;

	/* Usb transfer */
	struct libusb_transfer *transfer;

	/* Buffer for input/output */
	unsigned char buffer[VFS_BUFFER_SIZE];

	/* Length of data to send or received */
	unsigned int length;

	/* Ignore usb error */
	int ignore_error;

	/* Timeout */
	struct fpi_timeout *timeout;

	/* Loop counter */
	int counter;

	/* Number of enroll stage */
	int enroll_stage;

	/* Image contrast */
	int contrast;

	/* Best contrast */
	int best_contrast;

	/* Best contrast level */
	int best_clevel;

	/* Bottom line of image */
	int bottom;

	/* Image height */
	int height;
};

/* Return byte at specified position */
static inline unsigned char byte(int position, int value)
{
	return (value >> (position * 8)) & 0xff;
}

/* Return sequential number */
static inline unsigned short get_seqnum(int h, int l)
{
	return (h<<8) | l;
}

/* Check sequential number */
static inline int check_seqnum(struct vfs101_dev *vdev)
{
	if ((byte(0, vdev->seqnum) == vdev->buffer[0]) &&
		(byte(1, vdev->seqnum) == vdev->buffer[1]))
		return 0;
	else
		return 1;
}

/* Internal result codes */
enum
{
	RESULT_RETRY,
	RESULT_RETRY_SHORT,
	RESULT_RETRY_REMOVE,
	RESULT_COUNT,
};

/* Enroll result codes */
static int result_codes[2][RESULT_COUNT] =
{
	{
		FP_ENROLL_RETRY,
		FP_ENROLL_RETRY_TOO_SHORT,
		FP_ENROLL_RETRY_REMOVE_FINGER,
	},
	{
		FP_VERIFY_RETRY,
		FP_VERIFY_RETRY_TOO_SHORT,
		FP_VERIFY_RETRY_REMOVE_FINGER,
	},
};

/* Return result code based on current action */
static int result_code(struct fp_img_dev *dev, int result)
{
	/* Check result value */
183
	if (result < 0 || result >= RESULT_COUNT)
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
		return result;

	/* Return result code */
	if (dev->action == IMG_ACTION_ENROLL)
		return result_codes[0][result];
	else
		return result_codes[1][result];
};

/* Dump buffer for debug */
#define dump_buffer(buf) \
	fp_dbg("%02x %02x %02x %02x %02x %02x %02x %02x", \
		buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13] \
	)

/* Callback of asynchronous send */
static void async_send_cb(struct libusb_transfer *transfer)
{
	struct fpi_ssm *ssm = transfer->user_data;
203
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
	struct vfs101_dev *vdev = dev->priv;

	/* Cleanup transfer */
	vdev->transfer = NULL;

	/* Skip error check if ignore_error is set */
	if (!vdev->ignore_error)
	{
		if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
		{
			/* Transfer not completed, return IO error */
			fp_err("transfer not completed, status = %d", transfer->status);
			fpi_imgdev_session_error(dev, -EIO);
			fpi_ssm_mark_aborted(ssm, -EIO);
			goto out;
		}

		if (transfer->length != transfer->actual_length)
		{
			/* Data sended mismatch with expected, return protocol error */
			fp_err("length mismatch, got %d, expected %d",
				transfer->actual_length, transfer->length);
			fpi_imgdev_session_error(dev, -EIO);
			fpi_ssm_mark_aborted(ssm, -EIO);
			goto out;
		}
	}
	else
		/* Reset ignore_error flag */
		vdev->ignore_error = FALSE;

	/* Dump buffer for debug */
	dump_buffer(vdev->buffer);

	fpi_ssm_next_state(ssm);

out:
	libusb_free_transfer(transfer);
}

/* Submit asynchronous send */
static void async_send(struct fpi_ssm *ssm)
{
247
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
248
249
250
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
	struct vfs101_dev *vdev = dev->priv;
	int r;

	/* Allocation of transfer */
	vdev->transfer = libusb_alloc_transfer(0);
	if (!vdev->transfer)
	{
		/* Allocation transfer failed, return no memory error */
		fp_err("allocation of usb transfer failed");
		fpi_imgdev_session_error(dev, -ENOMEM);
		fpi_ssm_mark_aborted(ssm, -ENOMEM);
		return;
	}

	/* Put sequential number into the buffer */
	vdev->seqnum++;
	vdev->buffer[0] = byte(0, vdev->seqnum);
	vdev->buffer[1] = byte(1, vdev->seqnum);

	/* Prepare bulk transfer */
	libusb_fill_bulk_transfer(vdev->transfer, dev->udev, EP_OUT(1), vdev->buffer, vdev->length, async_send_cb, ssm, BULK_TIMEOUT);

	/* Submit transfer */
	r = libusb_submit_transfer(vdev->transfer);
	if (r != 0)
	{
		/* Submission of transfer failed, return IO error */
		libusb_free_transfer(vdev->transfer);
		fp_err("submit of usb transfer failed");
		fpi_imgdev_session_error(dev, -EIO);
		fpi_ssm_mark_aborted(ssm, -EIO);
		return;
	}
}

/* Callback of asynchronous recv */
static void async_recv_cb(struct libusb_transfer *transfer)
{
	struct fpi_ssm *ssm = transfer->user_data;
287
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
	struct vfs101_dev *vdev = dev->priv;

	/* Cleanup transfer */
	vdev->transfer = NULL;

	/* Skip error check if ignore_error is set */
	if (!vdev->ignore_error)
	{
		if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
		{
			/* Transfer not completed, return IO error */
			fp_err("transfer not completed, status = %d", transfer->status);
			fpi_imgdev_session_error(dev, -EIO);
			fpi_ssm_mark_aborted(ssm, -EIO);
			goto out;
		}

		if (check_seqnum(vdev))
		{
			/* Sequential number received mismatch, return protocol error */
			fp_err("seqnum mismatch, got %04x, expected %04x",
				get_seqnum(vdev->buffer[1], vdev->buffer[0]), vdev->seqnum);
			fpi_imgdev_session_error(dev, -EIO);
			fpi_ssm_mark_aborted(ssm, -EIO);
			goto out;
		}
	}
	else
		/* Reset ignore_error flag */
		vdev->ignore_error = FALSE;

	/* Dump buffer for debug */
	dump_buffer(vdev->buffer);

	/* Set length of received data */
	vdev->length = transfer->actual_length;

	fpi_ssm_next_state(ssm);

out:
	libusb_free_transfer(transfer);
}

/* Submit asynchronous recv */
static void async_recv(struct fpi_ssm *ssm)
{
334
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
	struct vfs101_dev *vdev = dev->priv;
	int r;

	/* Allocation of transfer */
	vdev->transfer = libusb_alloc_transfer(0);
	if (!vdev->transfer)
	{
		/* Allocation transfer failed, return no memory error */
		fp_err("allocation of usb transfer failed");
		fpi_imgdev_session_error(dev, -ENOMEM);
		fpi_ssm_mark_aborted(ssm, -ENOMEM);
		return;
	}

	/* Prepare bulk transfer */
	libusb_fill_bulk_transfer(vdev->transfer, dev->udev, EP_IN(1), vdev->buffer, 0x0f, async_recv_cb, ssm, BULK_TIMEOUT);

	/* Submit transfer */
	r = libusb_submit_transfer(vdev->transfer);
	if (r != 0)
	{
		/* Submission of transfer failed, free transfer and return IO error */
		libusb_free_transfer(vdev->transfer);
		fp_err("submit of usb transfer failed");
		fpi_imgdev_session_error(dev, -EIO);
		fpi_ssm_mark_aborted(ssm, -EIO);
		return;
	}
}

static void async_load(struct fpi_ssm *ssm);

/* Callback of asynchronous load */
static void async_load_cb(struct libusb_transfer *transfer)
{
	struct fpi_ssm *ssm = transfer->user_data;
371
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
	struct vfs101_dev *vdev = dev->priv;

	/* Cleanup transfer */
	vdev->transfer = NULL;

	/* Skip error check if ignore_error is set */
	if (!vdev->ignore_error)
	{
		if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
		{
			/* Transfer not completed */
			fp_err("transfer not completed, status = %d, length = %d", transfer->status, vdev->length);
			fpi_imgdev_session_error(dev, -EIO);
			fpi_ssm_mark_aborted(ssm, -EIO);
			goto out;
		}

		if (transfer->actual_length % VFS_FRAME_SIZE)
		{
			/* Received incomplete frame, return protocol error */
			fp_err("received incomplete frame");
			fpi_imgdev_session_error(dev, -EIO);
			fpi_ssm_mark_aborted(ssm, -EIO);
			goto out;
		}
	}

	/* Increase image length */
	vdev->length += transfer->actual_length;

	if (transfer->actual_length == VFS_BLOCK_SIZE)
	{
		if ((VFS_BUFFER_SIZE - vdev->length) < VFS_BLOCK_SIZE)
		{
			/* Buffer full, image too large, return no memory error */
			fp_err("buffer full, image too large");
			fpi_imgdev_session_error(dev, -ENOMEM);
			fpi_ssm_mark_aborted(ssm, -ENOMEM);
			goto out;
		}
		else
			/* Image load not completed, submit another asynchronous load */
			async_load(ssm);
	}
	else
	{
		/* Reset ignore_error flag */
		if (vdev->ignore_error)
			vdev->ignore_error = FALSE;

		/* Image load completed, go to next state */
		vdev->height = vdev->length / VFS_FRAME_SIZE;
		fp_dbg("image loaded, height = %d", vdev->height);
		fpi_ssm_next_state(ssm);
	}

out:
	libusb_free_transfer(transfer);
}

/* Submit asynchronous load */
static void async_load(struct fpi_ssm *ssm)
{
435
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
	struct vfs101_dev *vdev = dev->priv;
	unsigned char *buffer;
	int r;

	/* Allocation of transfer */
	vdev->transfer = libusb_alloc_transfer(0);
	if (!vdev->transfer)
	{
		/* Allocation transfer failed, return no memory error */
		fp_err("allocation of usb transfer failed");
		fpi_imgdev_session_error(dev, -ENOMEM);
		fpi_ssm_mark_aborted(ssm, -ENOMEM);
		return;
	}

	/* Append new data into the buffer */
	buffer = vdev->buffer + vdev->length;

	/* Prepare bulk transfer */
	libusb_fill_bulk_transfer(vdev->transfer, dev->udev, EP_IN(2), buffer, VFS_BLOCK_SIZE, async_load_cb, ssm, BULK_TIMEOUT);

	/* Submit transfer */
	r = libusb_submit_transfer(vdev->transfer);
	if (r != 0)
	{
		/* Submission of transfer failed, return IO error */
		libusb_free_transfer(vdev->transfer);
		fp_err("submit of usb transfer failed");
		fpi_imgdev_session_error(dev, -EIO);
		fpi_ssm_mark_aborted(ssm, -EIO);
		return;
	}
}

/* Callback of asynchronous sleep */
static void async_sleep_cb(void *data)
{
	struct fpi_ssm *ssm = data;
474
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
475
476
477
478
479
480
481
482
483
484
485
	struct vfs101_dev *vdev = dev->priv;

	/* Cleanup timeout */
	vdev->timeout = NULL;

	fpi_ssm_next_state(ssm);
}

/* Submit asynchronous sleep */
static void async_sleep(unsigned int msec, struct fpi_ssm *ssm)
{
486
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
	struct vfs101_dev *vdev = dev->priv;

	/* Add timeout */
	vdev->timeout = fpi_timeout_add(msec, async_sleep_cb, ssm);

	if (vdev->timeout == NULL)
	{
		/* Failed to add timeout */
		fp_err("failed to add timeout");
		fpi_imgdev_session_error(dev, -ETIME);
		fpi_ssm_mark_aborted(ssm, -ETIME);
	}
}

/* Swap ssm states */
enum
{
	M_SWAP_SEND,
	M_SWAP_RECV,
	M_SWAP_NUM_STATES,
};

/* Exec swap sequential state machine */
static void m_swap_state(struct fpi_ssm *ssm)
{
512
	switch (fpi_ssm_get_cur_state(ssm))
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
	{
	case M_SWAP_SEND:
		/* Send data */
		async_send(ssm);
		break;

	case M_SWAP_RECV:
		/* Recv response */
		async_recv(ssm);
		break;
	}
}

/* Start swap sequential state machine */
static void m_swap(struct fpi_ssm *ssm, unsigned char *data, size_t length)
{
529
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
530
531
532
533
534
535
536
537
538
539
	struct vfs101_dev *vdev = dev->priv;
	struct fpi_ssm *subsm;

	/* Prepare data for sending */
	memcpy(vdev->buffer, data, length);
	memset(vdev->buffer + length, 0, 16 - length);
	vdev->length = length;

	/* Start swap ssm */
	subsm = fpi_ssm_new(dev->dev, m_swap_state, M_SWAP_NUM_STATES);
540
	fpi_ssm_set_user_data(subsm, dev);
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
	fpi_ssm_start_subsm(ssm, subsm);
}

/* Retrieve fingerprint image */
static void vfs_get_print(struct fpi_ssm *ssm, unsigned int param, int type)
{
	unsigned char data[2][0x0e] = {
		{	0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
			0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01	},
		{	0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
			0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01	}
	};

	fp_dbg("param = %04x, type = %d", param, type);

	/* Prepare data for sending */
	data[type][6] = byte(0, param);
	data[type][7] = byte(1, param);

	/* Run swap sequential state machine */
	m_swap(ssm, data[type], 0x0e);
}

/* Set a parameter value on the device */
static void vfs_set_param(struct fpi_ssm *ssm, unsigned int param, unsigned int value)
{
	unsigned char data[0x0a] = { 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 };

	fp_dbg("param = %04x, value = %04x", param, value);

	/* Prepare data for sending */
	data[6] = byte(0, param);
	data[7] = byte(1, param);
	data[8] = byte(0, value);
	data[9] = byte(1, value);

	/* Run swap sequential state machine */
	m_swap(ssm, data, 0x0a);
}

/* Abort previous print */
static void vfs_abort_print(struct fpi_ssm *ssm)
{
	unsigned char data[0x06] = { 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00 };

586
	G_DEBUG_HERE();
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618

	/* Run swap sequential state machine */
	m_swap (ssm, data, 0x06);
}

/* Poke a value on a region */
static void vfs_poke(struct fpi_ssm *ssm, unsigned int addr, unsigned int value, unsigned int size)
{
	unsigned char data[0x0f] = { 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

	fp_dbg("addr = %04x, value = %04x", addr, value);

	/* Prepare data for sending */
	data[6] = byte(0, addr);
	data[7] = byte(1, addr);
	data[8] = byte(2, addr);
	data[9] = byte(3, addr);
	data[10] = byte(0, value);
	data[11] = byte(1, value);
	data[12] = byte(2, value);
	data[13] = byte(3, value);
	data[14] = byte(0, size);

	/* Run swap sequential state machine */
	m_swap(ssm, data, 0x0f);
}

/* Get current finger state */
static void vfs_get_finger_state(struct fpi_ssm *ssm)
{
	unsigned char data[0x06] = { 0x00, 0x00, 0x00, 0x00, 0x16, 0x00 };

619
	G_DEBUG_HERE();
620
621
622
623
624
625
626
627

	/* Run swap sequential state machine */
	m_swap (ssm, data, 0x06);
}

/* Load raw image from reader */
static void vfs_img_load(struct fpi_ssm *ssm)
{
628
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
629
630
	struct vfs101_dev *vdev = dev->priv;

631
	G_DEBUG_HERE();
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649

	/* Reset buffer length */
	vdev->length = 0;

	/* Reset image properties */
	vdev->bottom = 0;
	vdev->height = -1;

	/* Asynchronous load */
	async_load(ssm);
}

/* Check if action is completed */
static int action_completed(struct fp_img_dev *dev)
{
	struct vfs101_dev *vdev = dev->priv;

	if ((dev->action == IMG_ACTION_ENROLL) &&
650
		(vdev->enroll_stage < fpi_dev_get_nr_enroll_stages(dev->dev)))
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
		/* Enroll not completed, return false */
		return FALSE;

	else if (vdev->enroll_stage < 1)
		/* Action not completed, return false */
		return FALSE;

	/* Action completed, return true */
	return TRUE;
}

#define offset(x, y)	((x) + ((y) * VFS_FRAME_SIZE))

/* Screen image to remove noise and find bottom line and height od image */
static void img_screen(struct vfs101_dev *vdev)
{
	int y, x, count, top;
	long int level;
	int last_line = vdev->height - 1;

	fp_dbg("image height before screen = %d", vdev->height);

	count = 0;

	/* Image returned from sensor can contain many empty lines,
	 * for remove these lines compare byte 282-283 (scan level information)
	 * with two differents threshold, one for the begin of finger image and
	 * one for the end. To increase stability of the code use a counter
	 * of lines that satisfy the threshold.
	 */
	for (y = last_line, top = last_line; y >= 0; y--)
	{
		/* Take image scan level */
		level = vdev->buffer[offset(283, y)] * 256 +
			vdev->buffer[offset(282, y)];

		fp_dbg("line = %d, scan level = %ld", y, level);

		if (level >= VFS_IMG_SLT_BEGIN && top == last_line)
		{
			/* Begin threshold satisfied */
			if (count < VFS_IMG_SLT_LINES)
				/* Increase count */
				count++;
			else
			{
				/* Found top fingerprint line */
				top = y + VFS_IMG_SLT_LINES;
				count = 0;
			}
		}
		else if ((level < VFS_IMG_SLT_END || level >= 65535) &&
			top != last_line)
		{
			/* End threshold satisfied */
			if (count < VFS_IMG_SLT_LINES)
				/* Increase count */
				count++;
			else
			{
				/* Found bottom fingerprint line */
				vdev->bottom = y + VFS_IMG_SLT_LINES + 1;
				break;
			}
		}
		else
			/* Not threshold satisfied, reset count */
			count = 0;
	}

	vdev->height = top - vdev->bottom + 1;

	/* Checkk max height */
	if (vdev->height > VFS_IMG_MAX_HEIGHT)
		vdev->height = VFS_IMG_MAX_HEIGHT;

	fp_dbg("image height after screen = %d", vdev->height);

	/* Scan image and remove noise */
	for (y = vdev->bottom; y <= top; y++)
		for (x = 6; x < VFS_IMG_WIDTH + 6; x++)
			if (vdev->buffer[offset(x, y)] > VFS_IMG_MIN_IMAGE_LEVEL)
				vdev->buffer[offset(x, y)] = 255;
};

/* Copy image from reader buffer and put it into image data */
static void img_copy(struct vfs101_dev *vdev, struct fp_img *img)
{
	unsigned int line;
	unsigned char *img_buffer = img->data;
	unsigned char *vdev_buffer = vdev->buffer + (vdev->bottom * VFS_FRAME_SIZE) + 6;

	for (line = 0; line < img->height; line++)
	{
		/* Copy image line from reader buffer to image data */
		memcpy(img_buffer, vdev_buffer, VFS_IMG_WIDTH);

		/* Next line of reader buffer */
		vdev_buffer = vdev_buffer + VFS_FRAME_SIZE;

		/* Next line of image buffer */
		img_buffer = img_buffer + VFS_IMG_WIDTH;
	}
}

/* Extract fingerpint image from raw data */
static void img_extract(struct fpi_ssm *ssm)
{
759
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
	struct vfs101_dev *vdev = dev->priv;
	struct fp_img *img;

	/* Screen image to remove noise and find top and bottom line */
	img_screen(vdev);

	/* Check image height */
	if (vdev->height < VFS_IMG_MIN_HEIGHT)
	{
		/* Image too short */
		vdev->height = 0;
		return;
	}

	/* Fingerprint is present, load image from reader */
	fpi_imgdev_report_finger_status(dev, TRUE);

	/* Create new image */
	img = fpi_img_new(vdev->height * VFS_IMG_WIDTH);
	img->width = VFS_IMG_WIDTH;
	img->height = vdev->height;
	img->flags = FP_IMG_V_FLIPPED;

	/* Copy data into image */
	img_copy(vdev, img);

	/* Notify image captured */
	fpi_imgdev_image_captured(dev, img);

	/* Check captured result */
	if (dev->action_result >= 0 &&
		dev->action_result != FP_ENROLL_RETRY &&
		dev->action_result != FP_VERIFY_RETRY)
	{
		/* Image captured, increase enroll stage */
		vdev->enroll_stage++;

		/* Check if action is completed */
		if (!action_completed(dev))
			dev->action_result = FP_ENROLL_PASS;
	}
	else
	{
		/* Image capture failed */
		if (dev->action == IMG_ACTION_ENROLL)
			/* Return retry */
			dev->action_result = result_code(dev, RESULT_RETRY);
		else
		{
			/* Return no match */
			vdev->enroll_stage++;
			dev->action_result = FP_VERIFY_NO_MATCH;
		}
	}

	/* Fingerprint is removed from reader */
	fpi_imgdev_report_finger_status(dev, FALSE);
};

/* Finger states */
enum
{
	VFS_FINGER_EMPTY,
	VFS_FINGER_PRESENT,
	VFS_FINGER_UNKNOWN,
};

/* Return finger state */
static inline int vfs_finger_state(struct vfs101_dev *vdev)
{
	/* Check finger state */
	switch (vdev->buffer[0x0a])
	{
	case 0x00:
	case 0x01:
		/* Finger is empty */
		return VFS_FINGER_EMPTY;
		break;

	case 0x02:
	case 0x03:
	case 0x04:
	case 0x05:
	case 0x06:
		/* Finger is present */
		return VFS_FINGER_PRESENT;
		break;

	default:
		return VFS_FINGER_UNKNOWN;
	}
};

/* Check contrast of image */
static void vfs_check_contrast(struct vfs101_dev *vdev)
{
	int y;
	long int count = 0;

	/* Check difference from byte 4 to byte 5 for verify contrast of image */
	for (y = 0; y < vdev->height; y++)
		count = count + vdev->buffer[offset(5, y)] - vdev->buffer[offset(4, y)];
	count = count / vdev->height;

	if (count < 16)
	{
		/* Contrast not valid, retry */
		vdev->contrast++;
		return;
	}

	fp_dbg("contrast = %d, level = %ld", vdev->contrast, count);

	if (abs(count - VFS_IMG_BEST_CONRAST) < abs(vdev->best_clevel - VFS_IMG_BEST_CONRAST))
	{
		/* Better contrast found, use it */
		vdev->best_contrast = vdev->contrast;
		vdev->best_clevel = count;
	}
}

/* Loop ssm states */
enum
{
	/* Step 0 - Scan finger */
	M_LOOP_0_GET_PRINT,
	M_LOOP_0_SLEEP,
	M_LOOP_0_GET_STATE,
	M_LOOP_0_LOAD_IMAGE,
	M_LOOP_0_EXTRACT_IMAGE,
	M_LOOP_0_CHECK_ACTION,

	/* Step 1 - Scan failed */
	M_LOOP_1_GET_STATE,
	M_LOOP_1_CHECK_STATE,
	M_LOOP_1_GET_PRINT,
	M_LOOP_1_LOAD_IMAGE,
	M_LOOP_1_LOOP,
	M_LOOP_1_SLEEP,

	/* Step 2 - Abort print */
	M_LOOP_2_ABORT_PRINT,
	M_LOOP_2_LOAD_IMAGE,

	/* Step 3 - Wait aborting */
	M_LOOP_3_GET_PRINT,
	M_LOOP_3_LOAD_IMAGE,
	M_LOOP_3_CHECK_IMAGE,
	M_LOOP_3_LOOP,

	/* Number of states */
	M_LOOP_NUM_STATES,
};

/* Exec loop sequential state machine */
static void m_loop_state(struct fpi_ssm *ssm)
{
917
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
918
919
920
921
922
923
924
925
926
927
	struct vfs101_dev *vdev = dev->priv;

	/* Check action state */
	if (!vdev->active)
	{
		/* Action not active, mark sequential state machine completed */
		fpi_ssm_mark_completed(ssm);
		return;
	}

928
	switch (fpi_ssm_get_cur_state(ssm))
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
	{
	case M_LOOP_0_GET_PRINT:
		/* Send get print command to the reader */
		vfs_get_print(ssm, VFS_BUFFER_HEIGHT, 1);
		break;

	case M_LOOP_0_SLEEP:
		/* Wait fingerprint scanning */
		async_sleep(50, ssm);
		break;

	case M_LOOP_0_GET_STATE:
		/* Get finger state */
		vfs_get_finger_state(ssm);
		break;

	case M_LOOP_0_LOAD_IMAGE:
		/* Check finger state */
		switch (vfs_finger_state(vdev))
		{
		case VFS_FINGER_EMPTY:
			/* Finger isn't present, loop */
			fpi_ssm_jump_to_state(ssm, M_LOOP_0_SLEEP);
			break;

		case VFS_FINGER_PRESENT:
			/* Load image from reader */
			vdev->ignore_error = TRUE;
			vfs_img_load(ssm);
			break;

		default:
			/* Unknown state */
			fp_err("unknown device state 0x%02x", vdev->buffer[0x0a]);
			fpi_imgdev_session_error(dev, -EIO);
			fpi_ssm_mark_aborted(ssm, -EIO);
			break;
		}
		break;

	case M_LOOP_0_EXTRACT_IMAGE:
		/* Check if image is loaded */
		if (vdev->height > 0)
			/* Fingerprint is loaded, extract image from raw data */
			img_extract(ssm);

		/* Wait handling image */
		async_sleep(10, ssm);
		break;

	case M_LOOP_0_CHECK_ACTION:
		/* Check if action is completed */
		if (action_completed(dev))
			/* Action completed */
			fpi_ssm_mark_completed(ssm);
		else
			/* Action not completed */
			if (vdev->height > 0)
				/* Continue loop */
				fpi_ssm_jump_to_state(ssm, M_LOOP_2_ABORT_PRINT);
			else
				/* Error found */
				fpi_ssm_next_state(ssm);
		break;

	case M_LOOP_1_GET_STATE:
		/* Get finger state */
		vfs_get_finger_state(ssm);
		break;

	case M_LOOP_1_CHECK_STATE:
		/* Check finger state */
		if (vfs_finger_state(vdev) == VFS_FINGER_PRESENT)
		{
			if (vdev->counter < 20)
			{
				if (vdev->counter == 1)
				{
					/* The user should remove their finger from the scanner */
					fp_warn("finger present after scan, remove it");
					fpi_imgdev_session_error(dev, result_code(dev, RESULT_RETRY_REMOVE));
				}

				/* Wait removing finger */
				vdev->counter++;
				async_sleep(250, ssm);
			}
			else
			{
				/* reach max loop counter, return protocol error */
				fp_err("finger not removed from the scanner");
				fpi_imgdev_session_error(dev, -EIO);
				fpi_ssm_mark_aborted(ssm, -EIO);
			}
		}
		else
		{
			/* Finger not present */
			if (vdev->counter == 0)
			{
				/* Check image height */
				if (vdev->height == 0)
				{
					/* Return retry to  short */
					fp_warn("image too short, retry");
					fpi_imgdev_session_error(dev, result_code(dev, RESULT_RETRY_SHORT));
				}
				else
				{
					/* Return retry result */
					fp_warn("load image failed, retry");
					fpi_imgdev_session_error(dev, result_code(dev, RESULT_RETRY));
				}
			}

			/* Continue */
			vdev->counter = 0;
			fpi_ssm_jump_to_state(ssm, M_LOOP_1_SLEEP);
		}
		break;

	case M_LOOP_1_GET_PRINT:
		/* Send get print command to the reader */
		vfs_get_print(ssm, VFS_BUFFER_HEIGHT, 1);
		break;

	case M_LOOP_1_LOAD_IMAGE:
		/* Load image */
		vdev->ignore_error = TRUE;
		vfs_img_load(ssm);
		break;

	case M_LOOP_1_LOOP:
		/* Loop */
		fpi_ssm_jump_to_state(ssm, M_LOOP_1_GET_STATE);
		break;

	case M_LOOP_1_SLEEP:
		/* Wait fingerprint scanning */
		async_sleep(10, ssm);
		break;

	case M_LOOP_2_ABORT_PRINT:
		/* Abort print command */
		vfs_abort_print(ssm);
		break;

	case M_LOOP_2_LOAD_IMAGE:
		/* Load abort image */
		vdev->ignore_error = TRUE;
		vfs_img_load(ssm);
		break;

	case M_LOOP_3_GET_PRINT:
		/* Get empty image */
		vfs_get_print(ssm, 0x000a, 0);
		break;

	case M_LOOP_3_LOAD_IMAGE:
		/* Load abort image */
		vdev->ignore_error = TRUE;
		vfs_img_load(ssm);
		break;

	case M_LOOP_3_CHECK_IMAGE:
		if (vdev->height == 10)
		{
			/* Image load correctly, jump to step 0 */
			vdev->counter = 0;
			fpi_ssm_jump_to_state(ssm, M_LOOP_0_GET_PRINT);
		}
		else if (vdev->counter < 10)
		{
			/* Wait aborting */
			vdev->counter++;
			async_sleep(100, ssm);
		}
		else
		{
			/* reach max loop counter, return protocol error */
			fp_err("waiting abort reach max loop counter");
			fpi_imgdev_session_error(dev, -EIO);
			fpi_ssm_mark_aborted(ssm, -EIO);
		}
		break;

	case M_LOOP_3_LOOP:
		/* Loop */
		fpi_ssm_jump_to_state(ssm, M_LOOP_3_GET_PRINT);
		break;
	}
}

/* Complete loop sequential state machine */
static void m_loop_complete(struct fpi_ssm *ssm)
{
	/* Free sequential state machine */
	fpi_ssm_free(ssm);
}

/* Init ssm states */
enum
{
	/* Step 0 - Cleanup device buffer */
	M_INIT_0_RECV_DIRTY,
	M_INIT_0_ABORT_PRINT,
	M_INIT_0_LOAD_IMAGE,

	/* Step 1 - Wait aborting */
	M_INIT_1_GET_PRINT,
	M_INIT_1_LOAD_IMAGE,
	M_INIT_1_CHECK_IMAGE,
	M_INIT_1_LOOP,

	/* Step 2 - Handle unexpected finger presence */
	M_INIT_2_GET_STATE,
	M_INIT_2_CHECK_STATE,
	M_INIT_2_GET_PRINT,
	M_INIT_2_LOAD_IMAGE,
	M_INIT_2_LOOP,

	/* Step 3 - Set parameters */
	M_INIT_3_SET_000E,
	M_INIT_3_SET_0011,
	M_INIT_3_SET_0076,
	M_INIT_3_SET_0078,
	M_INIT_3_SET_THRESHOLD,
	M_INIT_3_SET_STATE3_COUNT,
	M_INIT_3_SET_STATE5_COUNT,
	M_INIT_3_SET_INFO_CONTRAST,
	M_INIT_3_SET_INFO_RATE,

	/* Step 4 - Autocalibrate contrast */
	M_INIT_4_SET_EXPOSURE,
	M_INIT_4_SET_CONTRAST,
	M_INIT_4_GET_PRINT,
	M_INIT_4_LOAD_IMAGE,
	M_INIT_4_CHECK_CONTRAST,

	/* Step 5 - Set info line parameters */
	M_INIT_5_SET_EXPOSURE,
	M_INIT_5_SET_CONTRAST,
	M_INIT_5_SET_INFO_CONTRAST,
	M_INIT_5_SET_INFO_RATE,

	/* Number of states */
	M_INIT_NUM_STATES,
};

/* Exec init sequential state machine */
static void m_init_state(struct fpi_ssm *ssm)
{
1181
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
	struct vfs101_dev *vdev = dev->priv;

	/* Check action state */
	if (!vdev->active)
	{
		/* Action not active, mark sequential state machine completed */
		fpi_ssm_mark_completed(ssm);
		return;
	}

1192
	switch (fpi_ssm_get_cur_state(ssm))
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
	{
	case M_INIT_0_RECV_DIRTY:
		/* Recv eventualy dirty data */
		vdev->ignore_error = TRUE;
		async_recv(ssm);
		break;

	case M_INIT_0_ABORT_PRINT:
		/* Abort print command */
		vfs_abort_print(ssm);
		break;

	case M_INIT_0_LOAD_IMAGE:
		/* Load abort image */
		vdev->ignore_error = TRUE;
		vfs_img_load(ssm);
		break;

	case M_INIT_1_GET_PRINT:
		/* Get empty image */
		vfs_get_print(ssm, 0x000a, 0);
		break;

	case M_INIT_1_LOAD_IMAGE:
		/* Load abort image */
		vdev->ignore_error = TRUE;
		vfs_img_load(ssm);
		break;

	case M_INIT_1_CHECK_IMAGE:
		if (vdev->height == 10)
		{
			/* Image load correctly, jump to step 2 */
			vdev->counter = 0;
			fpi_ssm_jump_to_state(ssm, M_INIT_2_GET_STATE);
		}
		else if (vdev->counter < 10)
		{
			/* Wait aborting */
			vdev->counter++;
			async_sleep(100, ssm);
		}
		else
		{
			/* reach max loop counter, return protocol error */
			fp_err("waiting abort reach max loop counter");
			fpi_imgdev_session_error(dev, -EIO);
			fpi_ssm_mark_aborted(ssm, -EIO);
		}
		break;

	case M_INIT_1_LOOP:
		/* Loop */
		fpi_ssm_jump_to_state(ssm, M_INIT_1_GET_PRINT);
		break;

	case M_INIT_2_GET_STATE:
		/* Get finger state */
		vfs_get_finger_state(ssm);
		break;

	case M_INIT_2_CHECK_STATE:
		/* Check finger state */
		if (vfs_finger_state(vdev) == VFS_FINGER_PRESENT)
		{
			if (vdev->counter < 20)
			{
				if (vdev->counter == 2)
				{
					/* The user should remove their finger from the scanner */
					fp_warn("unexpected finger find, remove finger from the scanner");
					fpi_imgdev_session_error(dev, result_code(dev, RESULT_RETRY_REMOVE));
				}

				/* Wait removing finger */
				vdev->counter++;
				async_sleep(250, ssm);
			}
			else
			{
				/* reach max loop counter, return protocol error */
				fp_err("finger not removed from the scanner");
				fpi_imgdev_session_error(dev, -EIO);
				fpi_ssm_mark_aborted(ssm, -EIO);
			}
		}
		else
		{
			/* Finger not present */
			if (vdev->counter == 0)
				/* Continue */
				fpi_ssm_jump_to_state(ssm, M_INIT_3_SET_000E);
			else
			{
				/* Finger removed, jump to abort */
				vdev->counter = 0;
				fpi_ssm_jump_to_state(ssm, M_INIT_0_ABORT_PRINT);
			}
		}
		break;

	case M_INIT_2_GET_PRINT:
		/* Send get print command to the reader */
		vfs_get_print(ssm, VFS_BUFFER_HEIGHT, 1);
		break;

	case M_INIT_2_LOAD_IMAGE:
		/* Load unexpected image */
		vdev->ignore_error = TRUE;
		vfs_img_load(ssm);
		break;

	case M_INIT_2_LOOP:
		/* Loop */
		fpi_ssm_jump_to_state(ssm, M_INIT_2_GET_STATE);
		break;

	case M_INIT_3_SET_000E:
		/* Set param 0x000e, required for take image */
		vfs_set_param(ssm, VFS_PAR_000E, VFS_VAL_000E);
		break;

	case M_INIT_3_SET_0011:
		/* Set param 0x0011, required for take image */
		vfs_set_param(ssm, VFS_PAR_0011, VFS_VAL_0011);
		break;

	case M_INIT_3_SET_0076:
		/* Set param 0x0076, required for use info line */
		vfs_set_param(ssm, VFS_PAR_0076, VFS_VAL_0076);
		break;

	case M_INIT_3_SET_0078:
		/* Set param 0x0078, required for use info line */
		vfs_set_param(ssm, VFS_PAR_0078, VFS_VAL_0078);
		break;

	case M_INIT_3_SET_THRESHOLD:
		/* Set threshold */
		vfs_set_param(ssm, VFS_PAR_THRESHOLD, VFS_VAL_THRESHOLD);
		break;

	case M_INIT_3_SET_STATE3_COUNT:
		/* Set state 3 count */
		vfs_set_param(ssm, VFS_PAR_STATE_3, VFS_VAL_STATE_3);
		break;

	case M_INIT_3_SET_STATE5_COUNT:
		/* Set state 5 count */
		vfs_set_param(ssm, VFS_PAR_STATE_5, VFS_VAL_STATE_5);
		break;

	case M_INIT_3_SET_INFO_CONTRAST:
		/* Set info line contrast */
		vfs_set_param(ssm, VFS_PAR_INFO_CONTRAST, 10);
		break;

	case M_INIT_3_SET_INFO_RATE:
		/* Set info line rate */
		vfs_set_param(ssm, VFS_PAR_INFO_RATE, 32);
		break;

	case M_INIT_4_SET_EXPOSURE:
		/* Set exposure level of reader */
		vfs_poke(ssm, VFS_REG_IMG_EXPOSURE, 0x4000, 0x02);
		vdev->counter = 1;
		break;

	case M_INIT_4_SET_CONTRAST:
		/* Set contrast level of reader */
		vfs_poke(ssm, VFS_REG_IMG_CONTRAST, vdev->contrast, 0x01);
		break;

	case M_INIT_4_GET_PRINT:
		/* Get empty image */
		vfs_get_print(ssm, 0x000a, 0);
		break;

	case M_INIT_4_LOAD_IMAGE:
		/* Load empty image */
		vfs_img_load(ssm);
		break;

	case M_INIT_4_CHECK_CONTRAST:
		/* Check contrast */
		vfs_check_contrast(vdev);

		if (vdev->contrast <= 6 || vdev->counter >= 12)
		{
			/* End contrast scan, continue */
			vdev->contrast = vdev->best_contrast;
			vdev->counter = 0;
			fp_dbg("use contrast value = %d", vdev->contrast);
			fpi_ssm_next_state(ssm);
		}
		else
		{
			/* Continue contrast scan, loop */
			vdev->contrast--;
			vdev->counter++;
			fpi_ssm_jump_to_state(ssm, M_INIT_4_SET_CONTRAST);
		}
		break;

	case M_INIT_5_SET_EXPOSURE:
		/* Set exposure level of reader */
		vfs_poke(ssm, VFS_REG_IMG_EXPOSURE, VFS_VAL_IMG_EXPOSURE, 0x02);
		break;

	case M_INIT_5_SET_CONTRAST:
		/* Set contrast level of reader */
		vfs_poke(ssm, VFS_REG_IMG_CONTRAST, vdev->contrast, 0x01);
		break;

	case M_INIT_5_SET_INFO_CONTRAST:
		/* Set info line contrast */
		vfs_set_param(ssm, VFS_PAR_INFO_CONTRAST, vdev->contrast);
		break;

	case M_INIT_5_SET_INFO_RATE:
		/* Set info line rate */
		vfs_set_param(ssm, VFS_PAR_INFO_RATE, VFS_VAL_INFO_RATE);
		break;
	}
}

/* Complete init sequential state machine */
static void m_init_complete(struct fpi_ssm *ssm)
{
1422
	struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
1423
1424
1425
	struct vfs101_dev *vdev = dev->priv;
	struct fpi_ssm *ssm_loop;

1426
	if (!fpi_ssm_get_error(ssm) && vdev->active)
1427
1428
1429
1430
1431
1432
	{
		/* Notify activate complete */
		fpi_imgdev_activate_complete(dev, 0);

		/* Start loop ssm */
		ssm_loop = fpi_ssm_new(dev->dev, m_loop_state, M_LOOP_NUM_STATES);
1433
		fpi_ssm_set_user_data(ssm_loop, dev);
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
		fpi_ssm_start(ssm_loop, m_loop_complete);
	}

	/* Free sequential state machine */
	fpi_ssm_free(ssm);
}

/* Activate device */
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
	struct vfs101_dev *vdev = dev->priv;
	struct fpi_ssm *ssm;

	/* Check if already active */
	if (vdev->active)
	{
		fp_err("device already activated");
		fpi_imgdev_session_error(dev, -EBUSY);
		return 1;
	}

	/* Set active state */
	vdev->active = TRUE;

	/* Set contrast */
	vdev->contrast = 15;
	vdev->best_clevel = -1;

	/* Reset loop counter and enroll stage */
	vdev->counter = 0;
	vdev->enroll_stage = 0;

	/* Start init ssm */
	ssm = fpi_ssm_new(dev->dev, m_init_state, M_INIT_NUM_STATES);
1468
	fpi_ssm_set_user_data(ssm, dev);
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
	fpi_ssm_start(ssm, m_init_complete);

	return 0;
}

/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *dev)
{
	struct vfs101_dev *vdev = dev->priv;

	/* Reset active state */
	vdev->active = FALSE;

	/* Handle eventualy existing events */
	while (vdev->transfer || vdev->timeout)
		fp_handle_events();

	/* Notify deactivate complete */
	fpi_imgdev_deactivate_complete(dev);
}

/* Open device */
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
	struct vfs101_dev *vdev = NULL;
	int r;

	/* Claim usb interface */
	r = libusb_claim_interface(dev->udev, 0);
	if (r < 0)
	{
		/* Interface not claimed, return error */
1501
		fp_err("could not claim interface 0: %s", libusb_error_name(r));
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
		return r;
	}

	/* Initialize private structure */
	vdev = g_malloc0(sizeof(struct vfs101_dev));
	vdev->seqnum = -1;
	dev->priv = vdev;

	/* Notify open complete */
	fpi_imgdev_open_complete(dev, 0);

	return 0;
}

/* Close device */
static void dev_close(struct fp_img_dev *dev)
{
	/* Release private structure */
	g_free(dev->priv);

	/* Release usb interface */
	libusb_release_interface(dev->udev, 0);

	/* Notify close complete */
	fpi_imgdev_close_complete(dev);
}

/* Usb id table of device */
static const struct usb_id id_table[] =
{
	{ .vendor = 0x138a, .product = 0x0001 },
	{ 0, 0, 0, },
};

/* Device driver definition */
struct fp_img_driver vfs101_driver =
{
	/* Driver specification */
	.driver =
	{
1542
		.id = VFS101_ID,
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
		.name = FP_COMPONENT,
		.full_name = "Validity VFS101",
		.id_table = id_table,
		.scan_type = FP_SCAN_TYPE_SWIPE,
	},

	/* Image specification */
	.flags = 0,
	.img_width = VFS_IMG_WIDTH,
	.img_height = -1,
	.bz3_threshold = 24,

	/* Routine specification */
	.open = dev_open,
	.close = dev_close,
	.activate = dev_activate,
	.deactivate = dev_deactivate,
};