vfs301.c 7.06 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * vfs301/vfs300 fingerprint reader driver
 * https://github.com/andree182/vfs301
 *
 * Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
 *
 * 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
 */

#define FP_COMPONENT "vfs301"

24
#include "drivers_api.h"
25
#include "vfs301_proto.h"
26

27 28 29
/************************** GENERIC STUFF *************************************/

/* Submit asynchronous sleep */
30 31 32 33
static void
async_sleep(unsigned int       msec,
	    fpi_ssm           *ssm,
	    struct fp_img_dev *dev)
34 35
{
	/* Add timeout */
36
	if (fpi_timeout_add(msec, fpi_ssm_next_state_timeout_cb, FP_DEV(dev), ssm) == NULL) {
37 38 39
		/* Failed to add timeout */
		fp_err("failed to add timeout");
		fpi_imgdev_session_error(dev, -ETIME);
40
		fpi_ssm_mark_failed(ssm, -ETIME);
41 42 43
	}
}

44 45 46
static int
submit_image(fpi_ssm           *ssm,
	     struct fp_img_dev *dev)
47
{
48
	vfs301_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
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
	int height;
	struct fp_img *img;

#if 0
	/* XXX: This is probably handled by libfprint automagically? */
	if (vdev->scanline_count < 20) {
		fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
		return 0;
	}
#endif

	img = fpi_img_new(VFS301_FP_OUTPUT_WIDTH * vdev->scanline_count);
	if (img == NULL)
		return 0;

	vfs301_extract_image(vdev, img->data, &height);

	/* TODO: how to detect flip? should the resulting image be
	 * oriented so that it is equal e.g. to a fingerprint on a paper,
	 * or to the finger when I look at it?) */
	img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED;

	img->width = VFS301_FP_OUTPUT_WIDTH;
	img->height = height;

74
	img = fpi_img_realloc(img, img->height * img->width);
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
	fpi_imgdev_image_captured(dev, img);

	return 1;
}

/* Loop ssm states */
enum
{
	/* Step 0 - Scan finger */
	M_REQUEST_PRINT,
	M_WAIT_PRINT,
	M_CHECK_PRINT,
	M_READ_PRINT_START,
	M_READ_PRINT_WAIT,
	M_READ_PRINT_POLL,
	M_SUBMIT_PRINT,

	/* Number of states */
	M_LOOP_NUM_STATES,
};

/* Exec loop sequential state machine */
97
static void m_loop_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
98
{
99
	struct fp_img_dev *dev = user_data;
100
	vfs301_dev_t *vdev = FP_INSTANCE_DATA(_dev);
101

102
	switch (fpi_ssm_get_cur_state(ssm)) {
103
	case M_REQUEST_PRINT:
104
		vfs301_proto_request_fingerprint(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
105 106 107 108 109
		fpi_ssm_next_state(ssm);
		break;

	case M_WAIT_PRINT:
		/* Wait fingerprint scanning */
110
		async_sleep(200, ssm, dev);
111 112 113
		break;

	case M_CHECK_PRINT:
114
		if (!vfs301_proto_peek_event(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev))
115 116 117 118 119 120 121
			fpi_ssm_jump_to_state(ssm, M_WAIT_PRINT);
		else
			fpi_ssm_next_state(ssm);
		break;

	case M_READ_PRINT_START:
		fpi_imgdev_report_finger_status(dev, TRUE);
122
		vfs301_proto_process_event_start(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
123 124 125 126 127
		fpi_ssm_next_state(ssm);
		break;

	case M_READ_PRINT_WAIT:
		/* Wait fingerprint scanning */
128
		async_sleep(200, ssm, dev);
129 130 131 132
		break;

	case M_READ_PRINT_POLL:
		{
133
		int rv = vfs301_proto_process_event_poll(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
134
		g_assert(rv != VFS301_FAILURE);
135 136 137 138 139 140 141 142
		if (rv == VFS301_ONGOING)
			fpi_ssm_jump_to_state(ssm, M_READ_PRINT_WAIT);
		else
			fpi_ssm_next_state(ssm);
		}
		break;

	case M_SUBMIT_PRINT:
143
		if (submit_image(ssm, dev)) {
144 145 146 147 148 149 150 151 152 153 154
			fpi_ssm_mark_completed(ssm);
			/* NOTE: finger off is expected only after submitting image... */
			fpi_imgdev_report_finger_status(dev, FALSE);
		} else {
			fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
		}
		break;
	}
}

/* Complete loop sequential state machine */
155
static void m_loop_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
156 157 158 159 160 161
{
	/* Free sequential state machine */
	fpi_ssm_free(ssm);
}

/* Exec init sequential state machine */
162
static void m_init_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
163
{
164
	struct fp_img_dev *dev = user_data;
165
	vfs301_dev_t *vdev = FP_INSTANCE_DATA(_dev);
166

167
	g_assert(fpi_ssm_get_cur_state(ssm) == 0);
168

169
	vfs301_proto_init(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
170 171 172 173 174

	fpi_ssm_mark_completed(ssm);
}

/* Complete init sequential state machine */
175
static void m_init_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
176
{
177
	struct fp_img_dev *dev = user_data;
Bastien Nocera's avatar
Bastien Nocera committed
178
	fpi_ssm *ssm_loop;
179

180
	if (!fpi_ssm_get_error(ssm)) {
181 182 183 184
		/* Notify activate complete */
		fpi_imgdev_activate_complete(dev, 0);

		/* Start loop ssm */
185
		ssm_loop = fpi_ssm_new(FP_DEV(dev), m_loop_state, M_LOOP_NUM_STATES, dev);
186 187 188 189 190 191 192 193 194 195
		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)
{
Bastien Nocera's avatar
Bastien Nocera committed
196
	fpi_ssm *ssm;
197 198

	/* Start init ssm */
199
	ssm = fpi_ssm_new(FP_DEV(dev), m_init_state, 1, dev);
200 201 202 203 204 205 206 207
	fpi_ssm_start(ssm, m_init_complete);

	return 0;
}

/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *dev)
{
208 209
	vfs301_dev_t *vdev;

210
	vdev = FP_INSTANCE_DATA(FP_DEV(dev));
211
	vfs301_proto_deinit(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
212 213 214 215 216 217 218 219 220
	fpi_imgdev_deactivate_complete(dev);
}

static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
	vfs301_dev_t *vdev = NULL;
	int r;

	/* Claim usb interface */
221
	r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
222 223
	if (r < 0) {
		/* Interface not claimed, return error */
224
		fp_err("could not claim interface 0: %s", libusb_error_name(r));
225 226 227 228 229
		return r;
	}

	/* Initialize private structure */
	vdev = g_malloc0(sizeof(vfs301_dev_t));
230
	fp_dev_set_instance_data(FP_DEV(dev), vdev);
231 232 233 234 235 236 237 238 239 240 241 242

	vdev->scanline_buf = malloc(0);
	vdev->scanline_count = 0;

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

	return 0;
}

static void dev_close(struct fp_img_dev *dev)
{
243 244
	vfs301_dev_t *vdev;

245
	/* Release private structure */
246
	vdev = FP_INSTANCE_DATA(FP_DEV(dev));
247 248
	free(vdev->scanline_buf);
	g_free(vdev);
249 250

	/* Release usb interface */
251
	libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

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

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

/* Device driver definition */
struct fp_img_driver vfs301_driver =
{
	/* Driver specification */
	.driver =
	{
271
		.id = VFS301_ID,
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
		.name = FP_COMPONENT,
		.full_name = "Validity VFS301",
		.id_table = id_table,
		.scan_type = FP_SCAN_TYPE_SWIPE,
	},

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

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