/* * Core functions for libfprint * Copyright (C) 2007-2008 Daniel Drake * * 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 #include #include #include #include #include #include "fp_internal.h" libusb_context *fpi_usb_ctx = NULL; GSList *opened_devices = NULL; /** * SECTION:discovery * @title: Device discovery * * These functions allow you to scan the system for supported fingerprint * scanning hardware. This is your starting point when integrating libfprint * into your software. * * When you've identified a discovered device that you would like to control, * you can open it with fp_dev_open(). Note that discovered devices may no * longer be available at the time when you want to open them, for example * the user may have unplugged the device. */ /** * SECTION:drv * @title: Driver operations * * Internally, libfprint is abstracted into various drivers to communicate * with the different types of supported fingerprint readers. libfprint works * hard so that you don't have to care about these internal abstractions, * however there are some situations where you may be interested in a little * behind-the-scenes driver info. * * You can obtain the driver for a device using fp_dev_get_driver(), which * you can pass to the functions documented on this page. */ /** * SECTION:dev * @title: Devices operations * * In order to interact with fingerprint scanners, your software will * interface primarily with libfprint's representation of devices, detailed * on this page. * * # Enrolling # {#enrolling} * * Enrolling is represented within libfprint as a multi-stage process. This * slightly complicates things for application developers, but is required * for a smooth process. * * Some devices require the user to scan their finger multiple times in * order to complete the enrollment process. libfprint must return control * to your application in-between each scan in order for your application to * instruct the user to swipe their finger again. Each scan is referred to * as a stage, so a device that requires 3 scans for enrollment corresponds * to you running 3 enrollment stages using libfprint. * * The fp_dev_get_nr_enroll_stages() function can be used to find out how * many enroll stages are needed. * * In order to complete an enroll stage, you call an enroll function such * as fp_enroll_finger(). The return of this function does not necessarily * indicate that a stage has completed though, as the user may not have * produced a good enough scan. Each stage may have to be retried several * times. * * The exact semantics of the enroll functions are described in the * fp_enroll_finger() documentation. You should pay careful attention to the * details. * * # Imaging # {#imaging} * * libfprint provides you with some ways to retrieve images of scanned * fingers, such as the fp_dev_img_capture() function, or some enroll/verify * function variants which provide images. You may wish to do something with * such images in your application. * * However, you must be aware that not all hardware supported by libfprint * operates like this. Most hardware does operate simply by sending * fingerprint images to the host computer for further processing, but some * devices do all fingerprint processing in hardware and do not present images * to the host computer. * * You can use fp_dev_supports_imaging() to see if image capture is possible * on a particular device. Your application must be able to cope with the * fact that libfprint does support regular operations (e.g. enrolling and * verification) on some devices which do not provide images. */ static GSList *registered_drivers = NULL; static void register_driver(struct fp_driver *drv) { if (drv->id == 0) { fp_err("not registering driver %s: driver ID is 0", drv->name); return; } registered_drivers = g_slist_prepend(registered_drivers, (gpointer) drv); fp_dbg("registered driver %s", drv->name); } #include "drivers_arrays.h" static void register_drivers(void) { unsigned int i; for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++) register_driver(primitive_drivers[i]); for (i = 0; i < G_N_ELEMENTS(img_drivers); i++) { struct fp_img_driver *imgdriver = img_drivers[i]; fpi_img_driver_setup(imgdriver); register_driver(&imgdriver->driver); } } API_EXPORTED struct fp_driver **fprint_get_drivers (void) { GPtrArray *array; unsigned int i; array = g_ptr_array_new (); for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++) g_ptr_array_add (array, primitive_drivers[i]); for (i = 0; i < G_N_ELEMENTS(img_drivers); i++) g_ptr_array_add (array, &(img_drivers[i]->driver)); /* Add a null item terminating the array */ g_ptr_array_add (array, NULL); return (struct fp_driver **) g_ptr_array_free (array, FALSE); } static struct fp_driver *find_supporting_driver(libusb_device *udev, const struct usb_id **usb_id, uint32_t *devtype) { int ret; GSList *elem = registered_drivers; struct libusb_device_descriptor dsc; const struct usb_id *best_usb_id; struct fp_driver *best_drv; uint32_t best_devtype; int drv_score = 0; ret = libusb_get_device_descriptor(udev, &dsc); if (ret < 0) { fp_err("Failed to get device descriptor"); return NULL; } best_drv = NULL; best_devtype = 0; do { struct fp_driver *drv = elem->data; uint32_t type = 0; const struct usb_id *id; for (id = drv->id_table; id->vendor; id++) { if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) { if (drv->discover) { int r = drv->discover(&dsc, &type); if (r < 0) fp_err("%s discover failed, code %d", drv->name, r); if (r <= 0) continue; /* Has a discover function, and matched our device */ drv_score = 100; } else { /* Already got a driver as good */ if (drv_score >= 50) continue; drv_score = 50; } fp_dbg("driver %s supports USB device %04x:%04x", drv->name, id->vendor, id->product); best_usb_id = id; best_drv = drv; best_devtype = type; /* We found the best possible driver */ if (drv_score == 100) break; } } } while ((elem = g_slist_next(elem))); if (best_drv != NULL) { fp_dbg("selected driver %s supports USB device %04x:%04x", best_drv->name, dsc.idVendor, dsc.idProduct); *devtype = best_devtype; *usb_id = best_usb_id; } return best_drv; } static struct fp_dscv_dev *discover_dev(libusb_device *udev) { const struct usb_id *usb_id; struct fp_driver *drv; struct fp_dscv_dev *ddev; uint32_t devtype; drv = find_supporting_driver(udev, &usb_id, &devtype); if (!drv) return NULL; ddev = g_malloc0(sizeof(*ddev)); ddev->drv = drv; ddev->udev = udev; ddev->driver_data = usb_id->driver_data; ddev->devtype = devtype; return ddev; } /** * fp_discover_devs: * * Scans the system and returns a list of discovered devices. This is your * entry point into finding a fingerprint reader to operate. * * Returns: a %NULL-terminated list of discovered devices. Must be freed with * fp_dscv_devs_free() after use. */ API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void) { GSList *tmplist = NULL; struct fp_dscv_dev **list; libusb_device *udev; libusb_device **devs; int dscv_count = 0; int r; int i = 0; if (registered_drivers == NULL) return NULL; r = libusb_get_device_list(fpi_usb_ctx, &devs); if (r < 0) { fp_err("couldn't enumerate USB devices, error %d", r); return NULL; } /* Check each device against each driver, temporarily storing successfully * discovered devices in a GSList. * * Quite inefficient but excusable as we'll only be dealing with small * sets of drivers against small sets of USB devices */ while ((udev = devs[i++]) != NULL) { struct fp_dscv_dev *ddev = discover_dev(udev); if (!ddev) continue; /* discover_dev() doesn't hold a reference to the udev, * so increase the reference for ddev to hold this ref */ libusb_ref_device(udev); tmplist = g_slist_prepend(tmplist, (gpointer) ddev); dscv_count++; } libusb_free_device_list(devs, 1); /* Convert our temporary GSList into a standard NULL-terminated pointer * array. */ list = g_malloc(sizeof(*list) * (dscv_count + 1)); if (dscv_count > 0) { GSList *elem = tmplist; i = 0; do { list[i++] = elem->data; } while ((elem = g_slist_next(elem))); } list[dscv_count] = NULL; /* NULL-terminate */ g_slist_free(tmplist); return list; } /** * fp_dscv_devs_free: * @devs: the list of discovered devices. If %NULL, function simply * returns. * * Free a list of discovered devices. This function destroys the list and all * discovered devices that it included, so make sure you have opened your * discovered device before freeing the list. */ API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs) { int i; if (!devs) return; for (i = 0; devs[i]; i++) { libusb_unref_device(devs[i]->udev); g_free(devs[i]); } g_free(devs); } /** * fp_dscv_dev_get_driver: * @dev: the discovered device * * Gets the #fp_driver for a discovered device. * * Returns: the driver backing the device */ API_EXPORTED struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev) { return dev->drv; } /** * fp_dscv_dev_get_driver_id: * @dev: a discovered fingerprint device * * Returns: the ID for the underlying driver for that device */ API_EXPORTED uint16_t fp_dscv_dev_get_driver_id(struct fp_dscv_dev *dev) { return fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev)); } /** * fp_dscv_dev_get_devtype: * @dev: the discovered device * * Gets the [devtype](advanced-topics.html#device-types) for a discovered device. * * Returns: the devtype of the device */ API_EXPORTED uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev) { return dev->devtype; } enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv) { switch (drv->type) { case DRIVER_PRIMITIVE: return PRINT_DATA_RAW; case DRIVER_IMAGING: return PRINT_DATA_NBIS_MINUTIAE; default: fp_err("unrecognised drv type %d", drv->type); return PRINT_DATA_RAW; } } /** * fp_dscv_dev_supports_print_data: * @dev: the discovered device * @print: the print for compatibility checking * * Determines if a specific #fp_print_data stored print appears to be * compatible with a discovered device. * * Returns: 1 if the print is compatible with the device, 0 otherwise */ API_EXPORTED int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev, struct fp_print_data *print) { return fpi_print_data_compatible(dev->drv->id, dev->devtype, fpi_driver_get_data_type(dev->drv), print->driver_id, print->devtype, print->type); } /** * fp_dscv_dev_supports_dscv_print: * @dev: the discovered device * @print: the discovered print for compatibility checking * * Determines if a specific #fp_dscv_print discovered print appears to be * compatible with a discovered device. * * Returns: 1 if the print is compatible with the device, 0 otherwise */ API_EXPORTED int fp_dscv_dev_supports_dscv_print(struct fp_dscv_dev *dev, struct fp_dscv_print *print) { return fpi_print_data_compatible(dev->drv->id, dev->devtype, 0, print->driver_id, print->devtype, 0); } /** * fp_dscv_dev_for_print_data: * @devs: a list of discovered devices * @print: the print under inspection * * Searches a list of discovered devices for a device that appears to be * compatible with a #fp_print_data stored print. * * Returns: the first discovered device that appears to support the print, or * %NULL if no apparently compatible devices could be found */ API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_print_data(struct fp_dscv_dev **devs, struct fp_print_data *print) { struct fp_dscv_dev *ddev; int i; for (i = 0; (ddev = devs[i]); i++) if (fp_dscv_dev_supports_print_data(ddev, print)) return ddev; return NULL; } /** * fp_dscv_dev_for_dscv_print: * @devs: a list of discovered devices * @print: the print under inspection * * Searches a list of discovered devices for a device that appears to be * compatible with a #fp_dscv_print discovered print. * * Returns: the first discovered device that appears to support the print, or * %NULL if no apparently compatible devices could be found */ API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_dscv_print(struct fp_dscv_dev **devs, struct fp_dscv_print *print) { struct fp_dscv_dev *ddev; int i; for (i = 0; (ddev = devs[i]); i++) if (fp_dscv_dev_supports_dscv_print(ddev, print)) return ddev; return NULL; } /** * fp_dev_get_driver: * @dev: the device * * Get the #fp_driver for a fingerprint device. * * Returns: the driver controlling the device */ API_EXPORTED struct fp_driver *fp_dev_get_driver(struct fp_dev *dev) { return dev->drv; } /** * fp_dev_get_nr_enroll_stages: * @dev: the device * * Gets the number of [enroll stages](intro.html#enrollment) required to enroll a * fingerprint with the device. * * Returns: the number of enroll stages */ API_EXPORTED int fp_dev_get_nr_enroll_stages(struct fp_dev *dev) { return dev->nr_enroll_stages; } /** * fp_dev_get_devtype: * @dev: the device * * Gets the [devtype](advanced-topics.html#device-types) for a device. * * Returns: the devtype */ API_EXPORTED uint32_t fp_dev_get_devtype(struct fp_dev *dev) { return dev->devtype; } /** * fp_dev_supports_print_data: * @dev: the device * @data: the stored print * * Determines if a stored print is compatible with a certain device. * * Returns: 1 if the print is compatible with the device, 0 if not */ API_EXPORTED int fp_dev_supports_print_data(struct fp_dev *dev, struct fp_print_data *data) { return fpi_print_data_compatible(dev->drv->id, dev->devtype, fpi_driver_get_data_type(dev->drv), data->driver_id, data->devtype, data->type); } /** * fp_dev_supports_dscv_print: * @dev: the device * @print: the discovered print * * Determines if a #fp_dscv_print discovered print appears to be compatible * with a certain device. * * Returns: 1 if the print is compatible with the device, 0 if not */ API_EXPORTED int fp_dev_supports_dscv_print(struct fp_dev *dev, struct fp_dscv_print *print) { return fpi_print_data_compatible(dev->drv->id, dev->devtype, 0, print->driver_id, print->devtype, 0); } /** * fp_driver_get_name: * @drv: the driver * * Retrieves the name of the driver. For example: "upekts" * * Returns: the driver name. Must not be modified or freed. */ API_EXPORTED const char *fp_driver_get_name(struct fp_driver *drv) { return drv->name; } /** * fp_driver_get_full_name: * @drv: the driver * * Retrieves a descriptive name of the driver. For example: "UPEK TouchStrip" * * Returns: the descriptive name. Must not be modified or freed. */ API_EXPORTED const char *fp_driver_get_full_name(struct fp_driver *drv) { return drv->full_name; } /** * fp_driver_get_driver_id: * @drv: the driver * * Retrieves the driver ID code for a driver. * * Returns: the driver ID */ API_EXPORTED uint16_t fp_driver_get_driver_id(struct fp_driver *drv) { return drv->id; } /** * fp_driver_get_scan_type: * @drv: the driver * * Retrieves the scan type for the devices associated with the driver. * * Returns: the scan type */ API_EXPORTED enum fp_scan_type fp_driver_get_scan_type(struct fp_driver *drv) { return drv->scan_type; } static struct fp_img_dev *dev_to_img_dev(struct fp_dev *dev) { if (dev->drv->type != DRIVER_IMAGING) return NULL; return dev->priv; } /** * fp_dev_supports_imaging: * @dev: the fingerprint device * * Determines if a device has imaging capabilities. If a device has imaging * capabilities you are able to perform imaging operations such as retrieving * scan images using fp_dev_img_capture(). However, not all devices are * imaging devices – some do all processing in hardware. This function will * indicate which class a device in question falls into. * * Returns: 1 if the device is an imaging device, 0 if the device does not * provide images to the host computer */ API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev) { return dev->drv->capture_start != NULL; } /** * fp_dev_supports_identification: * @dev: the fingerprint device * * Determines if a device is capable of [identification](intro.html#identification) * through fp_identify_finger() and similar. Not all devices support this * functionality. * * Returns: 1 if the device is capable of identification, 0 otherwise. */ API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev) { return dev->drv->identify_start != NULL; } /** * fp_dev_get_img_width: * @dev: the fingerprint device * * Gets the expected width of images that will be captured from the device. * This function will return -1 for devices that are not * [imaging devices](libfprint-Devices-operations.html#imaging). If the width of images from this device * can vary, 0 will be returned. * * Returns: the expected image width, or 0 for variable, or -1 for non-imaging * devices. */ API_EXPORTED int fp_dev_get_img_width(struct fp_dev *dev) { struct fp_img_dev *imgdev = dev_to_img_dev(dev); if (!imgdev) { fp_dbg("get image width for non-imaging device"); return -1; } return fpi_imgdev_get_img_width(imgdev); } /** * fp_dev_get_img_height: * @dev: the fingerprint device * * Gets the expected height of images that will be captured from the device. * This function will return -1 for devices that are not * [imaging devices](libfprint-Devices-operations.html#imaging). If the height of images from this device * can vary, 0 will be returned. * * Returns: the expected image height, or 0 for variable, or -1 for non-imaging * devices. */ API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev) { struct fp_img_dev *imgdev = dev_to_img_dev(dev); if (!imgdev) { fp_dbg("get image height for non-imaging device"); return -1; } return fpi_imgdev_get_img_height(imgdev); } /** * fp_set_debug: * @level: the verbosity level * * This call does nothing, see fp_init() for details. */ API_EXPORTED void fp_set_debug(int level) { /* Nothing */ } /** * fp_init: * * Initialise libfprint. This function must be called before you attempt to * use the library in any way. * * To enable debug output of libfprint specifically, use GLib's `G_MESSAGES_DEBUG` * environment variable as explained in [Running and debugging GLib Applications](https://developer.gnome.org/glib/stable/glib-running.html#G_MESSAGES_DEBUG). * * The log domains used in libfprint are either `libfprint` or `libfprint-FP_COMPONENT` * where `FP_COMPONENT` is defined in the source code for each driver, or component * of the library. Starting with `all` and trimming down is advised. * * To enable debugging of libusb, for USB-based fingerprint reader drivers, use * libusb's `LIBUSB_DEBUG` environment variable as explained in the * [libusb-1.0 API Reference](http://libusb.sourceforge.net/api-1.0/#msglog). * * Example: * * ``` * LIBUSB_DEBUG=4 G_MESSAGES_DEBUG=all my-libfprint-application * ``` * * Returns: 0 on success, non-zero on error. */ API_EXPORTED int fp_init(void) { int r; G_DEBUG_HERE(); r = libusb_init(&fpi_usb_ctx); if (r < 0) return r; register_drivers(); fpi_poll_init(); return 0; } /** * fp_exit: * * Deinitialise libfprint. This function should be called during your program * exit sequence. You must not use any libfprint functions after calling this * function, unless you call fp_init() again. */ API_EXPORTED void fp_exit(void) { G_DEBUG_HERE(); if (opened_devices) { GSList *copy = g_slist_copy(opened_devices); GSList *elem = copy; fp_dbg("naughty app left devices open on exit!"); do fp_dev_close((struct fp_dev *) elem->data); while ((elem = g_slist_next(elem))); g_slist_free(copy); g_slist_free(opened_devices); opened_devices = NULL; } fpi_data_exit(); fpi_poll_exit(); g_slist_free(registered_drivers); registered_drivers = NULL; libusb_exit(fpi_usb_ctx); }