diff --git a/configure.ac b/configure.ac
index 778e7ad..ba48086 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,7 +88,7 @@ AM_CONDITIONAL([ENABLE_UPEKSONLY], [test "$enable_upeksonly" != "no"])
 AM_CONDITIONAL([ENABLE_VCOM5S], [test "$enable_vcom5s" != "no"])
 AM_CONDITIONAL([ENABLE_URU4000], [test "$enable_uru4000" != "no"])
 #AM_CONDITIONAL([ENABLE_FDU2000], [test "$enable_fdu2000" != "no"])
-#AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" != "no"])
+AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" != "no"])
 AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" != "no"])
 AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" != "no"])
 AM_CONDITIONAL([REQUIRE_IMAGEMAGICK], [test "$require_imagemagick" != "no"])
diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am
index 0eb5ca6..22d0373 100644
--- a/libfprint/Makefile.am
+++ b/libfprint/Makefile.am
@@ -91,9 +91,9 @@ endif
 #DRIVER_SRC += $(FDU2000_SRC)
 #endif
 
-#if ENABLE_AES1610
-#DRIVER_SRC += $(AES1610_SRC)
-#endif
+if ENABLE_AES1610
+DRIVER_SRC += $(AES1610_SRC)
+endif
 
 if ENABLE_AES2501
 DRIVER_SRC += $(AES2501_SRC)
diff --git a/libfprint/core.c b/libfprint/core.c
index acbe08f..1c04fd9 100644
--- a/libfprint/core.c
+++ b/libfprint/core.c
@@ -348,11 +348,11 @@ static struct fp_img_driver * const img_drivers[] = {
 #ifdef ENABLE_UPEKSONLY
 	&upeksonly_driver,
 #endif
-	/*
+	
 #ifdef ENABLE_AES1610
 	&aes1610_driver,
 #endif
-#ifdef ENABLE_UPEKTC
+/*#ifdef ENABLE_UPEKTC
 	&upektc_driver,
 #endif
 #ifdef ENABLE_FDU2000
diff --git a/libfprint/drivers/aes1610.c b/libfprint/drivers/aes1610.c
index 7ef5fe2..5e1fc7d 100644
--- a/libfprint/drivers/aes1610.c
+++ b/libfprint/drivers/aes1610.c
@@ -1,11 +1,11 @@
 /*
  * AuthenTec AES1610 driver for libfprint
- * Copyright (C) 2007 Anthony Bretaudeau <wxcover@users.sourceforge.net>
- * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
+ * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
  * Copyright (C) 2007 Cyrille Bagard
  * Copyright (C) 2007 Vasily Khoruzhick
+ * Copyright (C) 2009 Guido Grazioli <guido.grazioli@gmail.com>
  *
- * Based on code from http://home.gna.org/aes2501, relicensed with permission
+ * Based on code from libfprint aes2501 driver.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -32,15 +32,22 @@
 #include <aeslib.h>
 #include <fp_internal.h>
 
+static void start_capture(struct fp_img_dev *dev);
+static void complete_deactivation(struct fp_img_dev *dev);
+static int adjust_gain(unsigned char *buffer, int status);
+
+#define FIRST_AES1610_REG	0x1B
+#define LAST_AES1610_REG	0xFF
+
+#define GAIN_STATUS_FIRST 1
+#define GAIN_STATUS_NORMAL 2
+
 /* FIXME these need checking */
 #define EP_IN			(1 | LIBUSB_ENDPOINT_IN)
 #define EP_OUT			(2 | LIBUSB_ENDPOINT_OUT)
 
 #define BULK_TIMEOUT 4000
 
-#define FIRST_AES1610_REG	0x1B
-#define LAST_AES1610_REG	0xFF
-
 /*
  * The AES1610 is an imaging device using a swipe-type sensor. It samples
  * the finger at preprogrammed intervals, sending a 128x8 frame to the
@@ -53,140 +60,212 @@
  * images returned from this driver vary in height.
  */
 
-#define FRAME_WIDTH		128
+#define FRAME_WIDTH	128
 #define FRAME_HEIGHT	8
-#define FRAME_SIZE		(FRAME_WIDTH * FRAME_HEIGHT)
+#define FRAME_SIZE	(FRAME_WIDTH * FRAME_HEIGHT)
 /* maximum number of frames to read during a scan */
 /* FIXME reduce substantially */
 #define MAX_FRAMES		350
 
-static int read_data(struct fp_img_dev *dev, unsigned char *data, size_t len)
+/****** GENERAL FUNCTIONS ******/
+
+struct aes1610_dev {
+	uint8_t read_regs_retry_count;
+	GSList *strips;
+	size_t strips_len;
+	gboolean deactivating;
+	uint8_t blanks_count;
+};
+
+typedef void (*aes1610_read_regs_cb)(struct fp_img_dev *dev, int status,
+	unsigned char *regs, void *user_data);
+
+struct aes1610_read_regs {
+	struct fp_img_dev *dev;
+	aes1610_read_regs_cb callback;
+	struct aes_regwrite *regwrite;
+	void *user_data;
+};
+
+/* FIXME: what to do here? */
+static void stub_capture_stop_cb(struct fp_img_dev *dev, int result, void *user_data)
 {
-	int r;
-	int transferred;
-	struct libusb_bulk_transfer msg = {
-		.endpoint = EP_IN,
-		.data = data,
-		.length = len,
-	};
-	fp_dbg("len=%zd", len);
+	
+}
 
-	r = libusb_bulk_transfer(dev->udev, &msg, &transferred, BULK_TIMEOUT);
-	if (r < 0) {
-		fp_err("bulk read error %d", r);
-		return r;
-	} else if (transferred < len) {
-		fp_err("unexpected short read %d/%zd", r, len);
-		return -EIO;
-	}
-	return 0;
+
+/* check that read succeeded but ignore all data */
+static void generic_ignore_data_cb(struct libusb_transfer *transfer)
+{
+	struct fpi_ssm *ssm = transfer->user_data;
+
+	if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
+		fpi_ssm_mark_aborted(ssm, -EIO);
+	else if (transfer->length != transfer->actual_length)
+		fpi_ssm_mark_aborted(ssm, -EPROTO);
+	else
+		fpi_ssm_next_state(ssm);
+
+	g_free(transfer->buffer);
+	libusb_free_transfer(transfer);
 }
 
-static const struct aes_regwrite init[] = {
-	{ 0x82, 0x00 }
-};
 
-static const struct aes_regwrite stop_reader[] = {
-	{ 0xFF, 0x00 }
-};
+static void read_regs_data_cb(struct libusb_transfer *transfer)
+{
+	struct aes1610_read_regs *rdata = transfer->user_data;
+	unsigned char *retdata = NULL;
+	int r;
 
-static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
+	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+		r = -EIO;
+	} else if (transfer->length != transfer->actual_length) {
+		r = -EPROTO;
+	} else {
+		r = 0;
+		retdata = transfer->buffer;
+	}
+
+	rdata->callback(rdata->dev, r, retdata, rdata->user_data);
+	g_free(rdata);
+	g_free(transfer->buffer);
+	libusb_free_transfer(transfer);
+}
+
+static void read_regs_rq_cb(struct fp_img_dev *dev, int result, void *user_data)
 {
+	struct aes1610_read_regs *rdata = user_data;
+	struct libusb_transfer *transfer;
+	unsigned char *data;
 	int r;
 
-	r = libusb_claim_interface(dev->udev, 0);
+	g_free(rdata->regwrite);
+	if (result != 0)
+		goto err;
+
+	transfer = libusb_alloc_transfer(0);
+	if (!transfer) {
+		result = -ENOMEM;
+		goto err;
+	}
+
+	data = g_malloc(126);
+	libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 126,
+		read_regs_data_cb, rdata, BULK_TIMEOUT);
+
+	r = libusb_submit_transfer(transfer);
 	if (r < 0) {
-		fp_err("could not claim interface 0");
-		return r;
+		g_free(data);
+		libusb_free_transfer(transfer);
+		result = -EIO;
+		goto err;
 	}
 
-	/* FIXME check endpoints */
+	return;
+err:
+	rdata->callback(dev, result, NULL, rdata->user_data);
+	g_free(rdata);
+}
+
+
+// XXX: this comes from aes2501 driver but it is unused here
+static void read_regs(struct fp_img_dev *dev, aes1610_read_regs_cb callback,
+	void *user_data)
+{
+	/* FIXME: regwrite is dynamic because of asynchronity. is this really
+	 * required? */
+	struct aes_regwrite *regwrite = g_malloc(sizeof(*regwrite));
+	struct aes1610_read_regs *rdata = g_malloc(sizeof(*rdata));
+
+	fp_dbg("");
+	//regwrite->reg = AES1610_REG_CTRL2;
+	//regwrite->value = AES1610_CTRL2_READ_REGS;
+	rdata->dev = dev;
+	rdata->callback = callback;
+	rdata->user_data = user_data;
+	rdata->regwrite = regwrite;
 
-	return aes_write_regv(dev, init, G_N_ELEMENTS(init));
+	//aes_write_regv(dev, (const struct aes_regwrite *) regwrite, 1,
+	//	read_regs_rq_cb, rdata);
 }
 
-static int do_exit(struct fp_img_dev *dev)
+/* Read the value of a specific register from a register dump */
+static int regval_from_dump(unsigned char *data, uint8_t target)
 {
-	return aes_write_regv(dev, stop_reader, G_N_ELEMENTS(stop_reader));
+	if (*data != FIRST_AES1610_REG) {
+		fp_err("not a register dump");
+		return -EILSEQ;
+	}
+
+	if (!(FIRST_AES1610_REG <= target && target <= LAST_AES1610_REG)) {
+		fp_err("out of range");
+		return -EINVAL;
+	}
+
+	target -= FIRST_AES1610_REG;
+	target *= 2;
+	return data[target + 1];
 }
 
-static void dev_exit(struct fp_img_dev *dev)
+static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
+	void *user_data)
 {
-	do_exit(dev);
-	libusb_release_interface(dev->udev, 0);
+	struct fpi_ssm *ssm = user_data;
+	if (result == 0)
+		fpi_ssm_next_state(ssm);
+	else
+		fpi_ssm_mark_aborted(ssm, result);
 }
 
-static const struct aes_regwrite finger_det_reqs[] = {
-	{ 0x80, 0x01 },
-	{ 0x80, 0x12 }, 
-	{ 0x85, 0x00 }, 
-	{ 0x8A, 0x00 },
-	{ 0x8B, 0x0E },
-	{ 0x8C, 0x90 }, 
-	{ 0x8D, 0x83 }, 
-	{ 0x8E, 0x07 }, 
-	{ 0x8F, 0x07 }, 
-	{ 0x96, 0x00 },
-	{ 0x97, 0x48 }, 
-	{ 0xA1, 0x00 }, 
-	{ 0xA2, 0x50 }, 
-	{ 0xA6, 0xE4 }, 
-	{ 0xAD, 0x08 },
-	{ 0xAE, 0x5B }, 
-	{ 0xAF, 0x54 }, 
-	{ 0xB1, 0x28 }, 
-	{ 0xB5, 0xAB }, 
-	{ 0xB6, 0x0E },
-	{ 0x1B, 0x2D }, 
-	{ 0x81, 0x04 }
-};
 
-static const struct aes_regwrite finger_det_none[] = {
-	{ 0x80, 0x01 },
-	{ 0x82, 0x00 }, 
-	{ 0x86, 0x00 }, 
-	{ 0xB1, 0x28 }, 
-	{ 0x1D, 0x00 }
-};
 
-static int detect_finger(struct fp_img_dev *dev)
+/* read the specified number of bytes from the IN endpoint but throw them
+ * away, then increment the SSM */
+static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
 {
-	unsigned char buffer[19];
+	struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+	unsigned char *data;
 	int r;
-	int i;
-	int sum = 0;
 
-	r = aes_write_regv(dev, finger_det_reqs, G_N_ELEMENTS(finger_det_reqs));
-	if (r < 0)
-		return r;
+	if (!transfer) {
+		fpi_ssm_mark_aborted(ssm, -ENOMEM);
+		return;
+	}
 
-	r = read_data(dev, buffer, 19);
-	if (r < 0)
-		return r;
+	data = g_malloc(bytes);
+	libusb_fill_bulk_transfer(transfer, ssm->dev->udev, EP_IN, data, bytes,
+		generic_ignore_data_cb, ssm, BULK_TIMEOUT);
 
-	for (i = 3; i < 17; i++)
-		sum += (buffer[i] & 0xf) + (buffer[i] >> 4);
-		
-	/* We need to answer something if no finger has been detected */
-	if (sum <= 20) {
-		r = aes_write_regv(dev, finger_det_none, G_N_ELEMENTS(finger_det_none));
-		if (r < 0)
-			return r;
+	r = libusb_submit_transfer(transfer);
+	if (r < 0) {
+		g_free(data);
+		libusb_free_transfer(transfer);
+		fpi_ssm_mark_aborted(ssm, r);
 	}
-	
-	return sum > 20;
 }
 
-static int await_finger_on(struct fp_img_dev *dev)
+/****** IMAGE PROCESSING ******/
+
+static int sum_histogram_values(unsigned char *data, uint8_t threshold)
 {
-	int r;
-	do {
-		r = detect_finger(dev);
-	} while (r == 0);
-	return (r < 0) ? r : 0;
+	int r = 0;
+	int i;
+	uint16_t *histogram = (uint16_t *)(data + 1);
+
+	if (*data != 0xde)
+		return -EILSEQ;
+
+	if (threshold > 0x0f)
+		return -EINVAL;
+
+	/* FIXME endianness */
+	for (i = threshold; i < 16; i++)
+		r += histogram[i];
+	
+	return r;
 }
 
-/* find overlapping parts of frames */
+/* find overlapping parts of  frames */
 static unsigned int find_overlap(unsigned char *first_frame,
 	unsigned char *second_frame, unsigned int *min_error)
 {
@@ -218,32 +297,34 @@ static unsigned int find_overlap(unsigned char *first_frame,
 }
 
 /* assemble a series of frames into a single image */
-static unsigned int assemble(unsigned char *input, unsigned char *output,
-	int num_strips, gboolean reverse, unsigned int *errors_sum)
+static unsigned int assemble(struct aes1610_dev *aesdev, unsigned char *output,
+	gboolean reverse, unsigned int *errors_sum)
 {
 	uint8_t *assembled = output;
 	int frame;
 	uint32_t image_height = FRAME_HEIGHT;
 	unsigned int min_error;
+	size_t num_strips = aesdev->strips_len;
+	GSList *list_entry = aesdev->strips;
 	*errors_sum = 0;
 
-	if (num_strips < 1)
+	if (num_strips < 1) 
 		return 0;
 	
 	/* Rotating given data by 90 degrees 
 	 * Taken from document describing aes1610 image format
 	 * TODO: move reversing detection here */
-	
+
 	if (reverse)
 		output += (num_strips - 1) * FRAME_SIZE;
 	for (frame = 0; frame < num_strips; frame++) {
-		aes_assemble_image(input, FRAME_WIDTH, FRAME_HEIGHT, output);
-		input += FRAME_WIDTH * (FRAME_HEIGHT / 2);
+		aes_assemble_image(list_entry->data, FRAME_WIDTH, FRAME_HEIGHT, output);
 
 		if (reverse)
-			output -= FRAME_SIZE;
+		    output -= FRAME_SIZE;
 		else
-			output += FRAME_SIZE;
+		    output += FRAME_SIZE;
+		list_entry = g_slist_next(list_entry);
 	}
 
 	/* Detecting where frames overlaped */
@@ -261,7 +342,179 @@ static unsigned int assemble(unsigned char *input, unsigned char *output,
 	return image_height;
 }
 
-static const struct aes_regwrite capture_reqs[] = {
+static void assemble_and_submit_image(struct fp_img_dev *dev)
+{
+	struct aes1610_dev *aesdev = dev->priv;
+	size_t final_size;
+	struct fp_img *img;
+	unsigned int errors_sum, r_errors_sum;
+
+	fp_dbg("");
+
+	BUG_ON(aesdev->strips_len == 0);
+
+	/* reverse list */
+	aesdev->strips = g_slist_reverse(aesdev->strips);
+
+	/* create buffer big enough for max image */
+	img = fpi_img_new(aesdev->strips_len * FRAME_SIZE);
+
+	img->flags = FP_IMG_COLORS_INVERTED;
+	img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
+	img->height = assemble(aesdev, img->data, TRUE, &r_errors_sum);
+	
+	if (r_errors_sum > errors_sum) {
+	    img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
+		img->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
+		fp_dbg("normal scan direction");
+	} else {
+		fp_dbg("reversed scan direction");
+	}
+
+	/* now that overlap has been removed, resize output image buffer */
+	final_size = img->height * FRAME_WIDTH;
+	img = fpi_img_resize(img, final_size);
+	/* FIXME: ugly workaround */
+	if (img->height < 12) img->height = 12;
+	fpi_imgdev_image_captured(dev, img);
+
+	/* free strips and strip list */
+	g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
+	g_slist_free(aesdev->strips);
+	aesdev->strips = NULL;
+	aesdev->strips_len = 0;
+	aesdev->blanks_count = 0;
+}
+
+
+/****** FINGER PRESENCE DETECTION ******/
+
+
+static const struct aes_regwrite finger_det_reqs[] = {
+	{ 0x80, 0x01 },
+	{ 0x80, 0x12 }, 
+	{ 0x85, 0x00 }, 
+	{ 0x8A, 0x00 },
+	{ 0x8B, 0x0E },
+	{ 0x8C, 0x90 }, 
+	{ 0x8D, 0x83 }, 
+	{ 0x8E, 0x07 }, 
+	{ 0x8F, 0x07 }, 
+	{ 0x96, 0x00 },
+	{ 0x97, 0x48 }, 
+	{ 0xA1, 0x00 }, 
+	{ 0xA2, 0x50 }, 
+	{ 0xA6, 0xE4 }, 
+	{ 0xAD, 0x08 },
+	{ 0xAE, 0x5B }, 
+	{ 0xAF, 0x54 }, 
+	{ 0xB1, 0x28 }, 
+	{ 0xB5, 0xAB }, 
+	{ 0xB6, 0x0E },
+	{ 0x1B, 0x2D }, 
+	{ 0x81, 0x04 }
+};
+
+static const struct aes_regwrite finger_det_none[] = {
+	{ 0x80, 0x01 },
+	{ 0x82, 0x00 }, 
+	{ 0x86, 0x00 }, 
+	{ 0xB1, 0x28 }, 
+	{ 0x1D, 0x00 }
+};
+
+
+static void start_finger_detection(struct fp_img_dev *dev);
+
+static void finger_det_data_cb(struct libusb_transfer *transfer)
+{
+	struct fp_img_dev *dev = transfer->user_data;
+	unsigned char *data = transfer->buffer;
+	int i;
+	int sum = 0;
+
+	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+		fpi_imgdev_session_error(dev, -EIO);
+		goto out;
+	} else if (transfer->length != transfer->actual_length) {
+		fpi_imgdev_session_error(dev, -EPROTO);
+		goto out;
+	}
+
+	/* examine histogram to determine finger presence */
+	for (i = 3; i < 17; i++)
+		sum += (data[i] & 0xf) + (data[i] >> 4);
+	if (sum > 20) {
+		/* reset default gain */
+		adjust_gain(data,GAIN_STATUS_FIRST);
+		/* finger present, start capturing */
+		fpi_imgdev_report_finger_status(dev, TRUE);
+		start_capture(dev);
+	} else {
+		/* no finger, poll for a new histogram */
+		start_finger_detection(dev);
+	}
+
+out:
+	g_free(data);
+	libusb_free_transfer(transfer);
+}
+
+
+static void finger_det_none_cb(struct fp_img_dev *dev, int result, void *user_data) 
+{
+	fpi_imgdev_report_finger_status(dev, FALSE);
+	start_finger_detection(dev);
+}
+
+static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
+{
+	struct libusb_transfer *transfer;
+	unsigned char *data;
+	int r;
+
+	if (result) {
+		fpi_imgdev_session_error(dev, result);
+		return;
+	}
+
+	transfer = libusb_alloc_transfer(0);
+	if (!transfer) {
+		fpi_imgdev_session_error(dev, -ENOMEM);
+		return;
+	}
+
+	
+ 	data = g_malloc(19);
+	libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 19,
+		finger_det_data_cb, dev, BULK_TIMEOUT);
+
+	r = libusb_submit_transfer(transfer);
+	if (r < 0) {
+		g_free(data);
+		libusb_free_transfer(transfer);
+		fpi_imgdev_session_error(dev, r);
+	}
+
+}
+
+static void start_finger_detection(struct fp_img_dev *dev)
+{
+	struct aes1610_dev *aesdev = dev->priv;
+	struct libusb_transfer *transfer;
+
+	if (aesdev->deactivating) {
+		complete_deactivation(dev);
+		return;
+	}
+
+	aes_write_regv(dev, finger_det_reqs, G_N_ELEMENTS(finger_det_reqs), finger_det_reqs_cb, NULL);
+
+}
+
+/****** CAPTURE ******/
+
+static struct aes_regwrite capture_reqs[] = {
 	{ 0x80, 0x01 },
 	{ 0x80, 0x12 },
 	{ 0x84, 0x01 },
@@ -271,8 +524,8 @@ static const struct aes_regwrite capture_reqs[] = {
 	{ 0x8B, 0x0E },
 	{ 0x8C, 0x90 },
 	{ 0xBE, 0x23 },
-	{ 0x29, 0x06 },
-	{ 0x2A, 0x35 },
+	{ 0x29, 0x04 },
+	{ 0x2A, 0xFF },
 	{ 0x96, 0x00 },
 	{ 0x98, 0x03 },
 	{ 0x99, 0x00 },
@@ -387,10 +640,10 @@ static const struct aes_regwrite capture_reqs[] = {
 	{ 0x81, 0x01 }
 };
 
-static const struct aes_regwrite strip_scan_reqs[] = {
+static struct aes_regwrite strip_scan_reqs[] = {
 	{ 0xBE, 0x23 },
-	{ 0x29, 0x06 },
-	{ 0x2A, 0x35 },
+	{ 0x29, 0x04 },
+	{ 0x2A, 0xFF },
 	{ 0xBD, 0x4F },
 	{ 0xFF, 0x00 }
 };
@@ -399,130 +652,461 @@ static const struct aes_regwrite capture_stop[] = {
 	{ 0x81,0x00 }
 };
 
-static int capture(struct fp_img_dev *dev, gboolean unconditional,
-	struct fp_img **ret)
-{
-	int r;
-	struct fp_img *img;
-	unsigned int nstrips;
-	unsigned int errors_sum, r_errors_sum;
-	unsigned char *cooked;
-	unsigned char *imgptr;
-	unsigned char buf[665];
-	int final_size;
-	int sum;
-	unsigned int count_blank = 0;
-	int i;
 
-	/* FIXME can do better here in terms of buffer management? */
-	fp_dbg("");
 
-	r = aes_write_regv(dev, capture_reqs, G_N_ELEMENTS(capture_reqs));
-	if (r < 0)
-		return r;
-  
-	/* FIXME: use histogram data above for gain calibration (0x8e xx) */
 
-	img = fpi_img_new((3 * MAX_FRAMES * FRAME_SIZE) / 2);
-	imgptr = img->data;
-	cooked = imgptr + (MAX_FRAMES * FRAME_SIZE) / 2;
 
-	r = read_data(dev, buf, 665);
-	if (r < 0)
-		goto err;
-	memcpy(imgptr, buf + 1, 128*4);
-	imgptr += 128*4;
+/*
+ * The different possible values for 0xBE register */
+static unsigned char list_BE_values[10] = {
+	0x23, 0x43, 0x63, 0x64, 0x65, 0x67, 0x6A, 0x6B
+};
 
-	r = read_data(dev, buf, 665);
-	if (r < 0)
-		goto err;
-	memcpy(imgptr, buf + 1, 128*4);
-	imgptr += 128*4;
-
-	/* we start at 2 because we captured 2 frames above. the above captures
-	 * should possibly be moved into the loop below, or discarded altogether */
-	for (nstrips = 2; nstrips < MAX_FRAMES - 2; nstrips++) {
-		r = aes_write_regv(dev, strip_scan_reqs, G_N_ELEMENTS(strip_scan_reqs));
-		if (r < 0)
-			goto err;
-		r = read_data(dev, buf, 665);
-		if (r < 0)
-			goto err;
-		memcpy(imgptr, buf + 1, 128*4);
-		imgptr += 128*4;
-
-		r = read_data(dev, buf, 665);
-		if (r < 0)
-			goto err;
-		memcpy(imgptr, buf + 1, 128*4);
-		imgptr += 128*4;
-
-		sum = 0;
-		for (i = 515; i != 530; i++)
-		{
-			/* histogram[i] = number of pixels of value i
-			   Only the pixel values from 10 to 15 are used to detect finger. */
-			sum += buf[i];
+/*
+ * The different possible values for 0xBD register */
+static unsigned char list_BD_values[10] = {
+	0x48, 0x4B, 0x4F, 0x52, 0x57, 0x59, 0x5B
+};
+
+/*
+ * Adjust the gain according to the histogram data
+ * 0xbd, 0xbe, 0x29 and 0x2A registers are affected
+ * Returns 0 if no problem occured
+ * TODO: This is a basic support for gain. It needs testing/tweaking.  */
+static int adjust_gain(unsigned char *buffer, int status)
+{
+	// The position in the array of possible values for 0xBE and 0xBD registers
+	static int pos_list_BE = 0;
+	static int pos_list_BD = 0;
+	
+	// This is the first adjustement (we begin acquisition)
+	// We adjust strip_scan_reqs for future strips and capture_reqs that is sent just after this step
+	if (status == GAIN_STATUS_FIRST) {
+		if (buffer[1] > 0x78) { // maximum gain needed
+			strip_scan_reqs[0].value = 0x6B;
+			strip_scan_reqs[1].value = 0x06;
+			strip_scan_reqs[2].value = 0x35;
+			strip_scan_reqs[3].value = 0x5B;
 		}
-		if (sum < 0) {
-			r = sum;
-			goto err;
+		else if (buffer[1] > 0x55) {
+			strip_scan_reqs[0].value = 0x63;
+			strip_scan_reqs[1].value = 0x15;
+			strip_scan_reqs[2].value = 0x35;
+			strip_scan_reqs[3].value = 0x4F;
 		}
-		fp_dbg("sum=%d", sum);
-		if (sum == 0)
-			count_blank++;
-		else
-			count_blank = 0;
-			
-		/* if we got 50 blank frames, assume scan has ended. */
-		if (count_blank >= 50)
-			break;
+		else if (buffer[1] > 0x40 || buffer[16] > 0x19) {
+			strip_scan_reqs[0].value = 0x43;
+			strip_scan_reqs[1].value = 0x13;
+			strip_scan_reqs[2].value = 0x35;
+			strip_scan_reqs[3].value = 0x4B;
+		}
+		else { // minimum gain needed
+			strip_scan_reqs[0].value = 0x23;
+			strip_scan_reqs[1].value = 0x07;
+			strip_scan_reqs[2].value = 0x35;
+			strip_scan_reqs[3].value = 0x48;
+		}
+		
+		// Now copy this values in capture_reqs
+		capture_reqs[8].value = strip_scan_reqs[0].value;
+		capture_reqs[9].value = strip_scan_reqs[1].value;
+		capture_reqs[10].value = strip_scan_reqs[2].value;
+		capture_reqs[21].value = strip_scan_reqs[3].value;
+		
+		fp_dbg("first gain: %x %x %x %x %x %x %x %x", strip_scan_reqs[0].reg, strip_scan_reqs[0].value, strip_scan_reqs[1].reg, strip_scan_reqs[1].value, strip_scan_reqs[2].reg, strip_scan_reqs[2].value, strip_scan_reqs[3].reg, strip_scan_reqs[3].value);
 	}
 	
-	r = aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop));
-	if (r < 0)
-		goto err;
-	r = read_data(dev, buf, 665);
-	if (r < 0)
-		goto err;
-	memcpy(imgptr, buf + 1, 128*4);
-	imgptr += 128*4;
-	nstrips++;
-
-	r = read_data(dev, buf, 665);
-	if (r < 0)
-		goto err;
-	memcpy(imgptr, buf + 1, 128*4);
-	imgptr += 128*4;
-	nstrips++;
+	// Every 2/3 strips
+	// We try to soften big changes of the gain (at least for 0xBE and 0xBD
+	// FIXME: This softenning will need testing and tweaking too
+	else if (status == GAIN_STATUS_NORMAL) {
+		if (buffer[514] > 0x78) { // maximum gain needed
+			if (pos_list_BE < 7)
+				pos_list_BE++;
+				
+			if (pos_list_BD < 6)
+				pos_list_BD++;
+				
+			strip_scan_reqs[1].value = 0x04;
+			strip_scan_reqs[2].value = 0x35;
+		}
+		else if (buffer[514] > 0x55) {
+			if (pos_list_BE < 2)
+				pos_list_BE++;
+			else if (pos_list_BE > 2)
+				pos_list_BE--;
+				
+			if (pos_list_BD < 2)
+				pos_list_BD++;
+			else if (pos_list_BD > 2)
+				pos_list_BD--;
+				
+			strip_scan_reqs[1].value = 0x15;
+			strip_scan_reqs[2].value = 0x35;
+		}
+		else if (buffer[514] > 0x40 || buffer[529] > 0x19) {
+			if (pos_list_BE < 1)
+				pos_list_BE++;
+			else if (pos_list_BE > 1)
+				pos_list_BE--;
+				
+			if (pos_list_BD < 1)
+				pos_list_BD++;
+			else if (pos_list_BD > 1)
+				pos_list_BD--;
+				
+			strip_scan_reqs[1].value = 0x13;
+			strip_scan_reqs[2].value = 0x35;
+		}
+		else { // minimum gain needed
+		if (pos_list_BE > 0)
+				pos_list_BE--;
+				
+			if (pos_list_BD > 0)
+				pos_list_BD--;
+				
+			strip_scan_reqs[1].value = 0x07;
+			strip_scan_reqs[2].value = 0x35;
+		}
+		
+		strip_scan_reqs[0].value = list_BE_values[pos_list_BE];
+		strip_scan_reqs[3].value = list_BD_values[pos_list_BD];
+		
+		fp_dbg("gain: %x %x %x %x %x %x %x %x", strip_scan_reqs[0].reg, strip_scan_reqs[0].value, strip_scan_reqs[1].reg, strip_scan_reqs[1].value, strip_scan_reqs[2].reg, strip_scan_reqs[2].value, strip_scan_reqs[3].reg, strip_scan_reqs[3].value);
+	}
 	
-	if (nstrips == MAX_FRAMES)
-		fp_warn("swiping finger too slow?");
+	// Unknown status
+	else {
+		fp_err("Unexpected gain status.");
+		return 1;
+	}
+	
+	return 0;
+}
 
-	img->flags = FP_IMG_COLORS_INVERTED;
-	img->height = assemble(img->data, cooked, nstrips, FALSE, &errors_sum);
-	img->height = assemble(img->data, cooked, nstrips, TRUE, &r_errors_sum);
+/*
+ * Restore the default gain values */
+static void restore_gain()
+{
+	strip_scan_reqs[0].value = list_BE_values[0];
+	strip_scan_reqs[1].value = 0x04;
+	strip_scan_reqs[2].value = 0xFF;
+	strip_scan_reqs[3].value = list_BD_values[0];
 	
-	if (r_errors_sum > errors_sum) {
-		img->height = assemble(img->data, cooked, nstrips, FALSE, &errors_sum);
-		img->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
-		fp_dbg("normal scan direction");
+	capture_reqs[8].value = list_BE_values[0];
+	capture_reqs[9].value = 0x04;
+	capture_reqs[10].value = 0xFF;
+	capture_reqs[21].value = list_BD_values[0];
+}
+
+
+/* capture SM movement:
+ * request and read strip,
+ * jump back to request UNLESS theres no finger, in which case exit SM,
+ * report lack of finger presence, and move to finger detection */
+
+enum capture_states {
+	CAPTURE_WRITE_REQS,
+	CAPTURE_READ_DATA,
+	CAPTURE_REQUEST_STRIP,
+	CAPTURE_READ_STRIP,
+	CAPTURE_NUM_STATES,
+};
+
+static void capture_read_strip_cb(struct libusb_transfer *transfer)
+{
+	unsigned char *stripdata;
+	struct fpi_ssm *ssm = transfer->user_data;
+	struct fp_img_dev *dev = ssm->priv;
+	struct aes1610_dev *aesdev = dev->priv;
+	unsigned char *data = transfer->buffer;
+	int sum, i;
+	int threshold;
+
+	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+		fpi_ssm_mark_aborted(ssm, -EIO);
+		goto out;
+	} else if (transfer->length != transfer->actual_length) {
+		fpi_ssm_mark_aborted(ssm, -EPROTO);
+		goto out;
+	}
+
+	/* FIXME: would preallocating strip buffers be a decent optimization? */
+	//stripdata = g_malloc(128 * 4);
+	//memcpy(stripdata, data + 1, 128 * 4);
+	//aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
+	//aesdev->strips_len++;
+
+	/*threshold = regval_from_dump(data + 1 + 128*8 + 1 + 16*2 + 1 + 8,
+		0x97);
+	if (threshold < 0) {
+		fpi_ssm_mark_aborted(ssm, threshold);
+		goto out;
+	}*/
+
+	sum = 0;
+	for (i = 516; i < 530; i++)
+	{
+		/* histogram[i] = number of pixels of value i
+		   Only the pixel values from 10 to 15 are used to detect finger. */
+		sum += data[i];
+	}
+
+	if (sum > 0) {
+	        /* FIXME: would preallocating strip buffers be a decent optimization? */
+	        stripdata = g_malloc(128 * 4);
+	        memcpy(stripdata, data + 1, 128 * 4);
+	        aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
+	        aesdev->strips_len++;
+		aesdev->blanks_count = 0;
+	}
+
+	if (sum < 0) {
+		fpi_ssm_mark_aborted(ssm, sum);
+		goto out;
+	}
+	fp_dbg("sum=%d", sum);
+
+	/* FIXME: 0 might be too low as a threshold */
+	/* FIXME: sometimes we get 0 in the middle of a scan, should we wait for
+	 * a few consecutive zeroes? */
+
+	/* If sum is 0 for a reasonable # of frames, finger has been removed */
+	if (sum == 0) {
+		aesdev->blanks_count++;
+		fp_dbg("got blank frame");
+	}
+
+	/* use histogram data above for gain calibration (0xbd, 0xbe, 0x29 and 0x2A ) */
+	adjust_gain(data, GAIN_STATUS_NORMAL);
+
+	/* stop capturing if MAX_FRAMES is reached */
+	if (aesdev->blanks_count > 10 || g_slist_length(aesdev->strips) >= MAX_FRAMES) {
+		fp_dbg("sending stop capture.... blanks=%d  frames=%d", aesdev->blanks_count, g_slist_length(aesdev->strips));
+		/* send stop capture bits */
+		aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop), stub_capture_stop_cb, NULL);
+		/* assemble image and submit it to library */
+		assemble_and_submit_image(dev);
+		fpi_imgdev_report_finger_status(dev, FALSE);
+		/* marking machine complete will re-trigger finger detection loop */
+		fpi_ssm_mark_completed(ssm);
+		/* Acquisition finished: restore default gain values */
+		restore_gain();
 	} else {
-		fp_dbg("reversed scan direction");
+		/* obtain next strip */
+		fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
 	}
 
-	final_size = img->height * FRAME_WIDTH;
-	memcpy(img->data, cooked, final_size);
-	img = fpi_img_resize(img, final_size);
-	*ret = img;
+out:
+	g_free(data);
+	libusb_free_transfer(transfer);
+}
+
+static void capture_run_state(struct fpi_ssm *ssm)
+{
+	struct fp_img_dev *dev = ssm->priv;
+	struct aes1610_dev *aesdev = dev->priv;
+	int r;
+
+	switch (ssm->cur_state) {
+	case CAPTURE_WRITE_REQS:
+		fp_dbg("write reqs");
+		aes_write_regv(dev, capture_reqs, G_N_ELEMENTS(capture_reqs),
+			generic_write_regv_cb, ssm);
+		break;
+	case CAPTURE_READ_DATA:
+		fp_dbg("read data");
+		generic_read_ignore_data(ssm, 665);
+		break;
+	case CAPTURE_REQUEST_STRIP:
+		fp_dbg("request strip");
+		if (aesdev->deactivating)
+			fpi_ssm_mark_completed(ssm);
+		else
+			aes_write_regv(dev, strip_scan_reqs, G_N_ELEMENTS(strip_scan_reqs),
+				generic_write_regv_cb, ssm);
+		break;
+	case CAPTURE_READ_STRIP: ;
+		struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+		unsigned char *data;
+
+		if (!transfer) {
+			fpi_ssm_mark_aborted(ssm, -ENOMEM);
+			break;
+		}
+
+		data = g_malloc(665);
+		libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 665,
+			capture_read_strip_cb, ssm, BULK_TIMEOUT);
+
+		r = libusb_submit_transfer(transfer);
+		if (r < 0) {
+			g_free(data);
+			libusb_free_transfer(transfer);
+			fpi_ssm_mark_aborted(ssm, r);
+		}
+		break;
+	};
+}
+
+static void capture_sm_complete(struct fpi_ssm *ssm)
+{
+	struct fp_img_dev *dev = ssm->priv;
+	struct aes1610_dev *aesdev = dev->priv;
+
+	fp_dbg("");
+	if (aesdev->deactivating)
+		complete_deactivation(dev);
+	else if (ssm->error)
+		fpi_imgdev_session_error(dev, ssm->error);
+	else
+		start_finger_detection(dev);
+	fpi_ssm_free(ssm);
+}
+
+static void start_capture(struct fp_img_dev *dev)
+{
+	struct aes1610_dev *aesdev = dev->priv;
+	struct fpi_ssm *ssm;
+
+	if (aesdev->deactivating) {
+		complete_deactivation(dev);
+		return;
+	}
+
+	ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
+	fp_dbg("");
+	ssm->priv = dev;
+	fpi_ssm_start(ssm, capture_sm_complete);
+}
+
+/****** INITIALIZATION/DEINITIALIZATION ******/
+
+static const struct aes_regwrite init[] = {
+	{ 0x82, 0x00 }
+};
+
+static const struct aes_regwrite stop_reader[] = {
+	{ 0xFF, 0x00 }
+};
+
+
+enum activate_states {
+	WRITE_INIT,
+//	READ_DATA,
+//	READ_REGS,
+	ACTIVATE_NUM_STATES,
+};
+
+/* this come from aes2501 and is unused here
+void activate_read_regs_cb(struct fp_img_dev *dev, int status,
+	unsigned char *regs, void *user_data)
+{
+	struct fpi_ssm *ssm = user_data;
+	struct aes1610_dev *aesdev = dev->priv;
+
+	if (status != 0) {
+		fpi_ssm_mark_aborted(ssm, status);
+	} else {
+		fpi_ssm_next_state(ssm);
+	}
+}
+*/
+
+static void activate_run_state(struct fpi_ssm *ssm)
+{
+	struct fp_img_dev *dev = ssm->priv;
+
+	/* activation on aes1610 seems much more straightforward compared to aes2501 */
+	/* verify theres anything missing here */
+	switch (ssm->cur_state) {
+	case WRITE_INIT:
+		fp_dbg("write init");
+		aes_write_regv(dev, init, G_N_ELEMENTS(init), generic_write_regv_cb, ssm);
+		break;
+/*	case READ_DATA:
+		fp_dbg("read data");
+		generic_read_ignore_data(ssm, 20);
+		break;
+	case READ_REGS:
+		fp_dbg("read regs");
+		read_regs(dev, activate_read_regs_cb, ssm);
+		break;*/
+	}
+}
+
+/* jump to finger detection */
+static void activate_sm_complete(struct fpi_ssm *ssm)
+{
+	struct fp_img_dev *dev = ssm->priv;
+	fp_dbg("status %d", ssm->error);
+	fpi_imgdev_activate_complete(dev, ssm->error);
+
+	if (!ssm->error)
+		start_finger_detection(dev);
+	fpi_ssm_free(ssm);
+}
+
+static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
+{
+	struct aes1610_dev *aesdev = dev->priv;
+	struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
+		ACTIVATE_NUM_STATES);
+	ssm->priv = dev;
+	aesdev->read_regs_retry_count = 0;
+	fpi_ssm_start(ssm, activate_sm_complete);
 	return 0;
-err:
-	fp_img_free(img);
-	return r;
+}
+
+static void dev_deactivate(struct fp_img_dev *dev)
+{
+	struct aes1610_dev *aesdev = dev->priv;
+	/* FIXME: audit cancellation points, probably need more, specifically
+	 * in error handling paths? */
+	aesdev->deactivating = TRUE;
+}
+
+static void complete_deactivation(struct fp_img_dev *dev)
+{
+	struct aes1610_dev *aesdev = dev->priv;
+	fp_dbg("");
+
+	/* FIXME: if we're in the middle of a scan, we should cancel the scan.
+	 * maybe we can do this with a master reset, unconditionally? */
+
+	aesdev->deactivating = FALSE;
+	g_slist_free(aesdev->strips);
+	aesdev->strips = NULL;
+	aesdev->strips_len = 0;
+	aesdev->blanks_count = 0;
+	fpi_imgdev_deactivate_complete(dev);
+}
+
+static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
+{
+	/* FIXME check endpoints */
+	int r;
+
+	r = libusb_claim_interface(dev->udev, 0);
+	if (r < 0) {
+		fp_err("could not claim interface 0");
+		return r;
+	}
+
+	dev->priv = g_malloc0(sizeof(struct aes1610_dev));
+	fpi_imgdev_open_complete(dev, 0);
+	return 0;
+}
+
+static void dev_deinit(struct fp_img_dev *dev)
+{
+	g_free(dev->priv);
+	libusb_release_interface(dev->udev, 0);
+	fpi_imgdev_close_complete(dev);
 }
 
 static const struct usb_id id_table[] = {
-	{ .vendor = 0x08ff, .product = 0x1600 },
+	{ .vendor = 0x08ff, .product = 0x1600 }, /* AES1600 */
 	{ 0, 0, 0, },
 };
 
@@ -532,20 +1116,16 @@ struct fp_img_driver aes1610_driver = {
 		.name = FP_COMPONENT,
 		.full_name = "AuthenTec AES1610",
 		.id_table = id_table,
+		.scan_type = FP_SCAN_TYPE_SWIPE,
 	},
 	.flags = 0,
 	.img_height = -1,
 	.img_width = 128,
-
-	/* temporarily lowered until we sort out image processing code
-	 * binarized scan quality is good, minutiae detection is accurate,
-	 * it's just that we get fewer minutiae than other scanners (less scanning
-	 * area) */
 	.bz3_threshold = 10,
 
-	.init = dev_init,
-	.exit = dev_exit,
-	.await_finger_on = await_finger_on,
-	.capture = capture,
+	.open = dev_init,
+	.close = dev_deinit,
+	.activate = dev_activate,
+	.deactivate = dev_deactivate,
 };
 
