fdu2000.c 7.44 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Secugen FDU2000 driver for libfprint
 * Copyright (C) 2007 Gustavo Chain <g@0xff.cl>
 *
 * 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 "fdu2000"

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
#ifndef HAVE_MEMMEM
gpointer
memmem(const gpointer haystack, size_t haystack_len, const gpointer needle, size_t needle_len) {
	const gchar *begin;
	const char *const last_possible = (const char *) haystack + haystack_len - needle_len;

	/* The first occurrence of the empty string is deemed to occur at 
	 * the beginning of the string. */
	if (needle_len == 0)
		return (void *) haystack;
	
	/* Sanity check, otherwise the loop might search through the whole
	 * memory.  */
	if (haystack_len < needle_len)
		return NULL;
	
	for (begin = (const char *) haystack; begin <= last_possible; ++begin)
		if (begin[0] == ((const char *) needle)[0] &&
			!memcmp((const void *) &begin[1],
				(const void *) ((const char *) needle + 1),
				needle_len - 1))
			return (void *) begin;
	
	return NULL;
}
#endif	/* HAVE_MEMMEM */

51 52 53
#define EP_IMAGE	( 0x02 | LIBUSB_ENDPOINT_IN )
#define EP_REPLY	( 0x01 | LIBUSB_ENDPOINT_IN )
#define EP_CMD		( 0x01 | LIBUSB_ENDPOINT_OUT )
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
#define BULK_TIMEOUT	200

/* fdu_req[] index */
typedef enum {
	CAPTURE_READY,
	CAPTURE_READ,
	CAPTURE_END,
	LED_OFF,
	LED_ON
} req_index;


#define CMD_LEN 2
#define ACK_LEN 8
static const struct fdu2000_req {
	const gchar cmd[CMD_LEN];	// Command to send
	const gchar ack[ACK_LEN];	// Expected ACK
	const guint ack_len;	// ACK has variable length
} fdu_req[] = {
	/* Capture */
	{
		.cmd = { 0x00, 0x04 },
		.ack = { 0x00, 0x04, 0x01, 0x01 },
		.ack_len = 4
	},
	
	{
		.cmd = { 0x00, 0x01 },
		.ack = { 0x00, 0x01, 0x01, 0x01 },
		.ack_len = 4
	},

	{
		.cmd = { 0x00, 0x05 },
		.ack = { 0x00, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
		.ack_len = 8
	},

	/* Led */
	{
		.cmd = { 0x05, 0x00 },
		.ack = {},
		.ack_len = 0
	},

	{
		.cmd = { 0x05, 0x01 },
		.ack = {},
		.ack_len = 0
	}
};

/*
 * Write a command and verify reponse
 */
static gint
110
bulk_write_safe(libusb_dev_handle *dev, req_index rIndex) {
111 112 113 114 115 116

	gchar reponse[ACK_LEN];
	gint r;
	gchar *cmd = (gchar *)fdu_req[rIndex].cmd;
	gchar *ack = (gchar *)fdu_req[rIndex].ack;
	gint ack_len = fdu_req[rIndex].ack_len;
117 118 119 120 121 122 123 124 125 126 127 128 129
	struct libusb_bulk_transfer wrmsg = {
		.endpoint = EP_CMD,
		.data = cmd,
		.length = sizeof(cmd),
	};
	struct libusb_bulk_transfer readmsg = {
		.endpoint = EP_REPLY,
		.data = reponse,
		.length = sizeof(reponse),
	};
	int trf;

	r = libusb_bulk_transfer(dev, &wrmsg, &trf, BULK_TIMEOUT);
130 131 132 133 134 135 136
	if (r < 0)
		return r;

	if (ack_len == 0)
		return 0;

	/* Check reply from FP */
137
	r = libusb_bulk_transfer(dev, &readmsg, &trf, BULK_TIMEOUT);
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
	if (r < 0)
		return r;

	if (!strncmp(ack, reponse, ack_len))
		return 0;
	
	fp_err("Expected different ACK from dev");
	return 1;	/* Error */
}

static gint
capture(struct fp_img_dev *dev, gboolean unconditional,
	struct fp_img **ret)
{
#define RAW_IMAGE_WIDTH		398
#define RAW_IMAGE_HEIGTH	301
#define RAW_IMAGE_SIZE		(RAW_IMAGE_WIDTH * RAW_IMAGE_HEIGTH)

	struct fp_img *img = NULL;
157
	int bytes, r;
158 159
	const gchar SOF[] = { 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0c, 0x07 };  // Start of frame
	const gchar SOL[] = { 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0b, 0x06 };  // Start of line + { L L } (L: Line num) (8 nibbles)
160
	gchar *buffer = g_malloc0(RAW_IMAGE_SIZE * 6);
161 162 163
	gchar *image;
	gchar *p;
	guint offset;
164 165 166 167 168
	struct libusb_bulk_transfer msg = {
		.endpoint = EP_IMAGE,
		.data = buffer,
		.length = RAW_IMAGE_SIZE * 6,
	};
169 170 171

	image  = g_malloc0(RAW_IMAGE_SIZE);

172
	if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), LED_ON))) {
173 174 175 176
		fp_err("Command: LED_ON");
		goto out;
	}
	
177
	if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_READY))) {
178 179 180 181 182
		fp_err("Command: CAPTURE_READY");
		goto out;
	}

read:	
183
	if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_READ))) {
184 185 186 187 188 189
		fp_err("Command: CAPTURE_READ");
		goto out;
	}

	/* Now we are ready to read from dev */

190
	r = libusb_bulk_transfer(fpi_dev_get_usb_dev(FP_DEV(dev)), &msg, &bytes, BULK_TIMEOUT * 10);
191
	if (r < 0 || bytes < 1)
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
		goto read;

	/*
	 * Find SOF (start of line)
	 */
	p = memmem(buffer, RAW_IMAGE_SIZE * 6,
			(const gpointer)SOF, sizeof SOF);
	fp_dbg("Read %d byte/s from dev", bytes);
	if (!p)
		goto out;

	p += sizeof SOF;

	int i = 0;
	bytes = 0;
	while(p) {
		if ( i >= RAW_IMAGE_HEIGTH )
			break;

		offset = p - buffer;
		p = memmem(p, (RAW_IMAGE_SIZE * 6) - (offset),
				(const gpointer)SOL, sizeof SOL);
		if (p) {
			p += sizeof SOL + 4;
			int j;
			for (j = 0; j < RAW_IMAGE_WIDTH; j++) {
218
				/*
219 220 221 222 223 224 225 226 227 228 229 230
				 * Convert from 4 to 8 bits
				 * The SECUGEN-FDU2000 has 4 lines of data, so we need to join 2 bytes into 1
				 */
				*(image + bytes + j)  = *(p + (j * 2) + 0) << 4 & 0xf0;
				*(image + bytes + j) |= *(p + (j * 2) + 1) & 0x0f;
			}
			p += RAW_IMAGE_WIDTH * 2;
			bytes += RAW_IMAGE_WIDTH;
			i++;
		}
	}

231
	if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_END))) {
232 233 234 235
		fp_err("Command: CAPTURE_END");
		goto out;
	}

236
	if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), LED_OFF))) {
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
		fp_err("Command: LED_OFF");
		goto out;
	}

	img = fpi_img_new_for_imgdev(dev);
	memcpy(img->data, image, RAW_IMAGE_SIZE);
	img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
	*ret = img;

out:
	g_free(buffer);
	g_free(image);

	return r;
}

static
gint dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
	gint r;
257
	//if ( (r = usb_set_configuration(fpi_dev_get_usb_dev(FP_DEV(dev)), 1)) < 0 )
258
	//	goto out;
259

260
	if ( (r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0)) < 0 ) {
261 262 263
		fp_err("could not claim interface 0: %s", libusb_error_name(r));
		return r;
	}
264

265
	//if ( (r = usb_set_altinterface(fpi_dev_get_usb_dev(FP_DEV(dev)), 1)) < 0 )
266
	//	goto out;
267

268
	//if ( (r = usb_clear_halt(fpi_dev_get_usb_dev(FP_DEV(dev)), EP_CMD)) < 0 )
269
	//	goto out;
270 271

	/* Make sure sensor mode is not capture_{ready|read} */
272
	if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_END))) {
273 274 275 276
		fp_err("Command: CAPTURE_END");
		goto out;
	}

277
	if ((r = bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), LED_OFF))) {
278 279 280 281 282 283 284 285 286 287 288 289 290 291
		fp_err("Command: LED_OFF");
		goto out;
	}

	return 0;

out:
	fp_err("could not init dev");
	return r;
}

static
void dev_exit(struct fp_img_dev *dev)
{
292
	if (bulk_write_safe(fpi_dev_get_usb_dev(FP_DEV(dev)), CAPTURE_END))
293 294
		fp_err("Command: CAPTURE_END");

295
	libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
296 297 298 299 300 301 302 303 304
}

static const struct usb_id id_table[] = {
	{ .vendor = 0x1162, .product = 0x0300 },
	{ 0, 0, 0, },
};

struct fp_img_driver fdu2000_driver = {
	.driver = {
305
		.id = FDU2000_ID,
306 307 308
		.name = FP_COMPONENT,
		.full_name = "Secugen FDU 2000",
		.id_table = id_table,
309
		.scan_type = FP_SCAN_TYPE_PRESS,
310 311 312 313 314 315 316 317 318
	},
	.img_height = RAW_IMAGE_HEIGTH,
	.img_width = RAW_IMAGE_WIDTH,
	.bz3_threshold = 23,

	.init = dev_init,
	.exit = dev_exit,
	.capture = capture,
};