From f3a838e856423af802de1d777a19163843cdf635 Mon Sep 17 00:00:00 2001
From: Daniel Drake <dsd@cs.manchester.ac.uk>
Date: Sat, 27 Oct 2007 00:06:53 +0100
Subject: [PATCH] Initial implementation for image device access and capture

Also added new example: img_capture
---
 TODO                    |   3 --
 examples/Makefile.am    |   5 +-
 examples/img_capture.c  | 100 +++++++++++++++++++++++++++++++++++++
 libfprint/Makefile.am   |   1 +
 libfprint/core.c        |   9 +++-
 libfprint/fp_internal.h |  33 +++++++++++-
 libfprint/fprint.h      |  12 +++++
 libfprint/img.c         | 106 +++++++++++++++++++++++++++++++++++++++
 libfprint/imgdev.c      | 108 +++++++++++++++++++++++++++++++++++++++-
 9 files changed, 369 insertions(+), 8 deletions(-)
 create mode 100644 examples/img_capture.c
 create mode 100644 libfprint/img.c

diff --git a/TODO b/TODO
index 4ddda91..d33bc88 100644
--- a/TODO
+++ b/TODO
@@ -1,9 +1,6 @@
 LIBRARY
 =======
 fingerprint data classifcation by device or device type
-storage mechanism
-imaging support
-external imaging APIs
 identification
 external API documentation
 test suite against NFIQ compliance set
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 708a378..d7d4706 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1,5 +1,5 @@
 INCLUDES = -I$(top_srcdir)
-noinst_PROGRAMS = verify_live enroll verify
+noinst_PROGRAMS = verify_live enroll verify img_capture
 
 verify_live_SOURCES = verify_live.c
 verify_live_LDADD = ../libfprint/libfprint.la -lfprint
@@ -10,3 +10,6 @@ enroll_LDADD = ../libfprint/libfprint.la -lfprint
 verify_SOURCES = verify.c
 verify_LDADD = ../libfprint/libfprint.la -lfprint
 
+img_capture_SOURCES = img_capture.c
+img_capture_LDADD = ../libfprint/libfprint.la -lfprint
+
diff --git a/examples/img_capture.c b/examples/img_capture.c
new file mode 100644
index 0000000..6edc191
--- /dev/null
+++ b/examples/img_capture.c
@@ -0,0 +1,100 @@
+/*
+ * Example libfprint image capture program
+ * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
+ *
+ * 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libfprint/fprint.h>
+
+struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
+{
+	struct fp_dscv_dev *ddev = NULL;
+	int i;
+
+	for (i = 0; ddev = discovered_devs[i]; i++) {
+		struct fp_driver *drv = fp_dscv_dev_get_driver(ddev);
+		printf("Found device claimed by %s driver\n",
+			fp_driver_get_full_name(drv));
+		return ddev;
+	}
+
+	return ddev;
+}
+
+int main(void)
+{
+	int r = 1;
+	struct fp_dscv_dev *ddev;
+	struct fp_dscv_dev **discovered_devs;
+	struct fp_dev *dev;
+	struct fp_img_dev *imgdev;
+	struct fp_img *img = NULL;
+
+	r = fp_init();
+	if (r < 0) {
+		fprintf(stderr, "Failed to initialize libfprint\n");
+		exit(1);
+	}
+
+	discovered_devs = fp_discover_devs();
+	if (!discovered_devs) {
+		fprintf(stderr, "Could not discover devices\n");
+		exit(1);
+	}
+
+	ddev = discover_device(discovered_devs);
+	if (!ddev) {
+		fprintf(stderr, "No devices detected.\n");
+		exit(1);
+	}
+
+	dev = fp_dev_open(ddev);
+	fp_dscv_devs_free(discovered_devs);
+	if (!dev) {
+		fprintf(stderr, "Could not open device.\n");
+		exit(1);
+	}
+
+	imgdev = fp_dev_to_img_dev(dev);
+	if (!imgdev) {
+		fprintf(stderr, "could not get image dev, is this an imaging "
+			"device?\n");
+		goto out_close;
+	}
+
+	printf("Opened device. It's now time to scan your finger.\n\n");
+
+	r = fp_imgdev_capture(imgdev, 0, &img);
+	if (r) {
+		fprintf(stderr, "image capture failed, code %d\n", r);
+		goto out_close;
+	}
+
+	r = fp_img_save_to_file(img, "finger.pgm");
+	if (r) {
+		fprintf(stderr, "img save failed, code %d\n", r);
+		goto out_close;
+	}
+
+	r = 0;
+out_close:
+	fp_dev_close(dev);
+	return r;
+}
+
diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am
index 3d84d10..5ffb8d6 100644
--- a/libfprint/Makefile.am
+++ b/libfprint/Makefile.am
@@ -11,6 +11,7 @@ libfprint_la_LIBADD = $(LIBUSB_LIBS) $(GLIB_LIBS)
 libfprint_la_SOURCES =	\
 	core.c		\
 	data.c		\
+	img.c		\
 	imgdev.c	\
 	$(DRIVER_SRC)
 
diff --git a/libfprint/core.c b/libfprint/core.c
index 5b2a3c7..ff8850a 100644
--- a/libfprint/core.c
+++ b/libfprint/core.c
@@ -193,7 +193,7 @@ API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev)
 		fp_err("usb_open failed");
 		return NULL;
 	}
-	
+
 	dev = g_malloc0(sizeof(*dev));
 	dev->drv = drv;
 	dev->udev = udevh;
@@ -242,6 +242,13 @@ API_EXPORTED const char *fp_driver_get_full_name(struct fp_driver *drv)
 	return drv->full_name;
 }
 
+API_EXPORTED struct fp_img_dev *fp_dev_to_img_dev(struct fp_dev *dev)
+{
+	if (dev->drv->type != DRIVER_IMAGING)
+		return NULL;
+	return dev->priv;
+}
+
 API_EXPORTED int fp_enroll_finger(struct fp_dev *dev,
 	struct fp_print_data **print_data)
 {
diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h
index c017f13..e8aeb2e 100644
--- a/libfprint/fp_internal.h
+++ b/libfprint/fp_internal.h
@@ -41,6 +41,10 @@
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))
 
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
 enum fpi_log_level {
 	LOG_LEVEL_DEBUG,
 	LOG_LEVEL_INFO,
@@ -82,6 +86,12 @@ struct fp_dev {
 	int __enroll_stage;
 };
 
+struct fp_img_dev {
+	struct fp_dev *dev;
+	usb_dev_handle *udev;
+	void *priv;
+};
+
 struct usb_id {
 	uint16_t vendor;
 	uint16_t product;
@@ -109,10 +119,20 @@ struct fp_driver {
 	int (*verify)(struct fp_dev *dev, struct fp_print_data *data);
 };
 
+/* flags for fp_img_driver.flags */
+#define FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE (1 << 0)
+
 struct fp_img_driver {
 	struct fp_driver driver;
+	uint16_t flags;
 
 	/* Device operations */
+	int (*init)(struct fp_img_dev *dev, unsigned long driver_data);
+	void (*exit)(struct fp_img_dev *dev);
+	int (*await_finger_on)(struct fp_img_dev *dev);
+	int (*await_finger_off)(struct fp_img_dev *dev);
+	int (*capture)(struct fp_img_dev *dev, gboolean unconditional,
+		struct fp_img **image);
 };
 
 extern struct fp_driver upekts_driver;
@@ -133,9 +153,20 @@ struct fp_print_data {
 };
 
 struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length);
-unsigned char *fpi_print_data_get_buffer(struct fp_print_data *data);
 int fpi_print_data_compatible(struct fp_dev *dev, struct fp_print_data *data);
 
+struct fp_img {
+	int width;
+	int height;
+	size_t length;
+	unsigned char data[0];
+};
+
+struct fp_img *fpi_img_new(size_t length);
+struct fp_img *fpi_img_new_dims(int width, int height);
+struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize);
+gboolean fpi_img_is_sane(struct fp_img *img);
+
 #define bswap16(x) (((x & 0xff) << 8) | (x >> 8))
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 #define cpu_to_le16(x) (x)
diff --git a/libfprint/fprint.h b/libfprint/fprint.h
index bcc9845..128c011 100644
--- a/libfprint/fprint.h
+++ b/libfprint/fprint.h
@@ -25,6 +25,7 @@ struct fp_dscv_dev;
 struct fp_dev;
 struct fp_driver;
 struct fp_print_data;
+struct fp_img;
 
 /* misc/general stuff */
 enum fp_finger {
@@ -50,6 +51,7 @@ struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev);
 void fp_dev_close(struct fp_dev *dev);
 struct fp_driver *fp_dev_get_driver(struct fp_dev *dev);
 int fp_dev_get_nr_enroll_stages(struct fp_dev *dev);
+struct fp_img_dev *fp_dev_to_img_dev(struct fp_dev *dev);
 
 /* Drivers */
 const char *fp_driver_get_name(struct fp_driver *drv);
@@ -86,6 +88,16 @@ int fp_print_data_load(struct fp_dev *dev, enum fp_finger finger,
 int fp_print_data_save(struct fp_print_data *data, enum fp_finger finger);
 void fp_print_data_free(struct fp_print_data *data);
 
+/* Imaging devices */
+int fp_imgdev_capture(struct fp_img_dev *imgdev, int unconditional,
+	struct fp_img **image);
+
+/* Image handling */
+int fp_img_get_height(struct fp_img *img);
+int fp_img_get_width(struct fp_img *img);
+unsigned char *fp_img_get_data(struct fp_img *img);
+int fp_img_save_to_file(struct fp_img *img, char *path);
+
 /* Library */
 int fp_init(void);
 
diff --git a/libfprint/img.c b/libfprint/img.c
new file mode 100644
index 0000000..664bbea
--- /dev/null
+++ b/libfprint/img.c
@@ -0,0 +1,106 @@
+/*
+ * Image management functions for libfprint
+ * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
+ *
+ * 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
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "fp_internal.h"
+
+struct fp_img *fpi_img_new(size_t length)
+{
+	struct fp_img *img = g_malloc(sizeof(*img) + length);
+	memset(img, 0, sizeof(*img));
+	fp_dbg("length=%zd", length);
+	img->length = length;
+	return img;
+}
+
+struct fp_img *fpi_img_new_dims(int width, int height)
+{
+	struct fp_img *img = fpi_img_new(width * height);
+	img->width = width;
+	img->height = height;
+	return img;
+}
+
+gboolean fpi_img_is_sane(struct fp_img *img)
+{
+	/* basic checks */
+	if (!img->length || !img->width || !img->height)
+		return FALSE;
+
+	/* buffer is big enough? */
+	if ((img->length * img->height) < img->length)
+		return FALSE;
+
+	return TRUE;
+}
+
+struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize)
+{
+	return g_realloc(img, sizeof(*img) + newsize);
+}
+
+API_EXPORTED int fp_img_get_height(struct fp_img *img)
+{
+	return img->height;
+}
+
+API_EXPORTED int fp_img_get_width(struct fp_img *img)
+{
+	return img->width;
+}
+
+API_EXPORTED unsigned char *fp_img_get_data(struct fp_img *img)
+{
+	return img->data;
+}
+
+API_EXPORTED int fp_img_save_to_file(struct fp_img *img, char *path)
+{
+	FILE *fd = fopen(path, "w");
+	size_t write_size = img->width * img->height;
+	int r;
+
+	if (!fd) {
+		fp_dbg("could not open '%s' for writing: %d", path, errno);
+		return -errno;
+	}
+
+	r = fprintf(fd, "P5 %d %d 255\n", img->width, img->height);
+	if (r < 0) {
+		fp_err("pgm header write failed, error %d", r);
+		return r;
+	}
+
+	r = fwrite(img->data, 1, write_size, fd);
+	if (r < write_size) {
+		fp_err("short write (%d)", r);
+		return -EIO;
+	}
+
+	fclose(fd);
+	fp_dbg("written to '%s'", path);
+	return 0;
+}
+
diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c
index 7071f11..be48b86 100644
--- a/libfprint/imgdev.c
+++ b/libfprint/imgdev.c
@@ -1,5 +1,5 @@
 /*
- * Core imaging functions for libfprint
+ * Core imaging device functions for libfprint
  * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
  *
  * This library is free software; you can redistribute it and/or
@@ -17,13 +17,117 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include <errno.h>
 #include <glib.h>
 
 #include "fp_internal.h"
 
+#define driver_to_img_driver(drv) \
+	container_of((drv), struct fp_img_driver, driver)
+
+static int img_dev_init(struct fp_dev *dev, unsigned long driver_data)
+{
+	struct fp_img_dev *imgdev = g_malloc0(sizeof(*imgdev));
+	struct fp_img_driver *imgdrv = driver_to_img_driver(dev->drv);
+	int r = 0;
+
+	imgdev->dev = dev;
+	dev->priv = imgdev;
+	dev->nr_enroll_stages = 1;
+
+	/* for consistency in driver code, allow udev access through imgdev */
+	imgdev->udev = dev->udev;
+
+	if (imgdrv->init) {
+		r = imgdrv->init(imgdev, driver_data);
+		if (r)
+			goto err;
+	}
+
+	return 0;
+err:
+	g_free(imgdev);
+	return r;
+}
+
+static void img_dev_exit(struct fp_dev *dev)
+{
+	struct fp_img_dev *imgdev = dev->priv;
+	struct fp_img_driver *imgdrv = driver_to_img_driver(dev->drv);
+
+	if (imgdrv->exit)
+		imgdrv->exit(imgdev);
+
+	g_free(imgdev);
+}
+
+API_EXPORTED int fp_imgdev_capture(struct fp_img_dev *imgdev,
+	int unconditional, struct fp_img **image)
+{
+	struct fp_driver *drv = imgdev->dev->drv;
+	struct fp_img_driver *imgdrv = driver_to_img_driver(drv);
+	int r;
+
+	if (!image) {
+		fp_err("no image pointer given");
+		return -EINVAL;
+	}
+
+	if (!imgdrv->capture) {
+		fp_err("img driver %s has no capture func", drv->name);
+		return -ENOTSUP;
+	}
+
+	if (unconditional) {
+		if (!(imgdrv->flags & FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE)) {
+			fp_dbg("requested unconditional capture, but driver %s does not "
+				"support it", drv->name);
+			return -ENOTSUP;
+		}
+	}
+
+	fp_dbg("%s will handle capture request", drv->name);
+
+	if (!unconditional && imgdrv->await_finger_on) {
+		r = imgdrv->await_finger_on(imgdev);
+		if (r) {
+			fp_err("await_finger_on failed with error %d", r);
+			return r;
+		}
+	}
+
+	r = imgdrv->capture(imgdev, unconditional, image);
+	if (r) {
+		fp_err("capture failed with error %d", r);
+		return r;
+	}
+
+	if (!unconditional && imgdrv->await_finger_off) {
+		r = imgdrv->await_finger_off(imgdev);
+		if (r) {
+			fp_err("await_finger_off failed with error %d", r);
+			return r;
+		}
+	}
+
+	if (r == 0) {
+		if (*image == NULL) {
+			fp_err("capture succeeded but no image returned?");
+			return -ENODATA;
+		}
+		if (!fpi_img_is_sane(*image)) {
+			fp_err("image is not sane!");
+			return -EIO;
+		}
+	}
+
+	return r;
+}
+
 void fpi_img_driver_setup(struct fp_img_driver *idriver)
 {
 	idriver->driver.type = DRIVER_IMAGING;
-	/* FIXME fill in primitive operations */
+	idriver->driver.init = img_dev_init;
+	idriver->driver.exit = img_dev_exit;
 }