diff --git a/libfprint/async.c b/libfprint/async.c index 67e3481..128b7e9 100644 --- a/libfprint/async.c +++ b/libfprint/async.c @@ -412,3 +412,101 @@ void fpi_drvcb_identify_stopped(struct fp_dev *dev) dev->identify_stop_cb(dev, dev->identify_stop_cb_data); } +API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional, + fp_capture_cb callback, void *user_data) +{ + struct fp_driver *drv = dev->drv; + int r; + + fp_dbg(""); + if (!drv->capture_start) + return -ENOTSUP; + + dev->state = DEV_STATE_CAPTURE_STARTING; + dev->capture_cb = callback; + dev->capture_cb_data = user_data; + dev->unconditional_capture = unconditional; + + r = drv->capture_start(dev); + if (r < 0) { + dev->capture_cb = NULL; + dev->state = DEV_STATE_ERROR; + fp_err("failed to start verification, error %d", r); + } + return r; +} + +/* Drivers call this when capture has started */ +void fpi_drvcb_capture_started(struct fp_dev *dev, int status) +{ + fp_dbg(""); + BUG_ON(dev->state != DEV_STATE_CAPTURE_STARTING); + if (status) { + if (status > 0) { + status = -status; + fp_dbg("adjusted to %d", status); + } + dev->state = DEV_STATE_ERROR; + if (dev->capture_cb) + dev->capture_cb(dev, status, NULL, dev->capture_cb_data); + } else { + dev->state = DEV_STATE_CAPTURING; + } +} + +/* Drivers call this to report a capture result (which might mark completion) */ +void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result, + struct fp_img *img) +{ + fp_dbg("result %d", result); + BUG_ON(dev->state != DEV_STATE_CAPTURING); + if (result < 0 || result == FP_CAPTURE_COMPLETE) + dev->state = DEV_STATE_CAPTURE_DONE; + + if (dev->capture_cb) + dev->capture_cb(dev, result, img, dev->capture_cb_data); + else + fp_dbg("ignoring capture result as no callback is subscribed"); +} + +/* Drivers call this when capture has stopped */ +void fpi_drvcb_capture_stopped(struct fp_dev *dev) +{ + fp_dbg(""); + BUG_ON(dev->state != DEV_STATE_CAPTURE_STOPPING); + dev->state = DEV_STATE_INITIALIZED; + if (dev->capture_stop_cb) + dev->capture_stop_cb(dev, dev->capture_stop_cb_data); +} + +API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev, + fp_capture_stop_cb callback, void *user_data) +{ + struct fp_driver *drv = dev->drv; + int r; + + fp_dbg(""); + BUG_ON(dev->state != DEV_STATE_ERROR + && dev->state != DEV_STATE_CAPTURING + && dev->state != DEV_STATE_CAPTURE_DONE); + + dev->capture_cb = NULL; + dev->capture_stop_cb = callback; + dev->capture_stop_cb_data = user_data; + dev->state = DEV_STATE_CAPTURE_STOPPING; + + if (!drv->capture_start) + return -ENOTSUP; + if (!drv->capture_stop) { + dev->state = DEV_STATE_INITIALIZED; + fpi_drvcb_capture_stopped(dev); + return 0; + } + + r = drv->capture_stop(dev); + if (r < 0) { + fp_err("failed to stop verification"); + dev->capture_stop_cb = NULL; + } + return r; +} diff --git a/libfprint/core.c b/libfprint/core.c index 584e762..f6faab2 100644 --- a/libfprint/core.c +++ b/libfprint/core.c @@ -804,7 +804,7 @@ static struct fp_img_dev *dev_to_img_dev(struct fp_dev *dev) */ API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev) { - return dev->drv->type == DRIVER_IMAGING; + return dev->drv->capture_start != NULL; } /** \ingroup dev @@ -819,38 +819,6 @@ API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev) return dev->drv->identify_start != NULL; } -/** \ingroup dev - * Captures an \ref img "image" from a device. The returned image is the raw - * image provided by the device, you may wish to \ref img_std "standardize" it. - * - * If set, the unconditional flag indicates that the device should - * capture an image unconditionally, regardless of whether a finger is there - * or not. If unset, this function will block until a finger is detected on - * the sensor. - * - * \param dev the device - * \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected - * \param image a location to return the captured image. Must be freed with - * fp_img_free() after use. - * \return 0 on success, non-zero on error. -ENOTSUP indicates that either the - * unconditional flag was set but the device does not support this, or that the - * device does not support imaging. - * \sa fp_dev_supports_imaging() - */ -API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional, - struct fp_img **image) -{ - struct fp_img_dev *imgdev = dev_to_img_dev(dev); - if (!imgdev) { - fp_dbg("image capture on non-imaging device"); - return -ENOTSUP; - } - - //return fpi_imgdev_capture(imgdev, unconditional, image); - /* FIXME reimplement async */ - return -ENOTSUP; -} - /** \ingroup dev * Gets the expected width of images that will be captured from the device. * This function will return -1 for devices that are not diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index a8a26a9..c383c66 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -91,6 +91,10 @@ enum fp_dev_state { DEV_STATE_IDENTIFYING, DEV_STATE_IDENTIFY_DONE, DEV_STATE_IDENTIFY_STOPPING, + DEV_STATE_CAPTURE_STARTING, + DEV_STATE_CAPTURING, + DEV_STATE_CAPTURE_DONE, + DEV_STATE_CAPTURE_STOPPING, }; struct fp_driver **fprint_get_drivers (void); @@ -108,8 +112,8 @@ struct fp_dev { /* drivers should not mess with any of the below */ enum fp_dev_state state; - int __enroll_stage; + int unconditional_capture; /* async I/O callbacks and data */ /* FIXME: convert this to generic state operational data mechanism? */ @@ -129,6 +133,10 @@ struct fp_dev { void *identify_cb_data; fp_identify_stop_cb identify_stop_cb; void *identify_stop_cb_data; + fp_capture_cb capture_cb; + void *capture_cb_data; + fp_capture_stop_cb capture_stop_cb; + void *capture_stop_cb_data; /* FIXME: better place to put this? */ struct fp_print_data **identify_gallery; @@ -146,6 +154,7 @@ enum fp_imgdev_action { IMG_ACTION_ENROLL, IMG_ACTION_VERIFY, IMG_ACTION_IDENTIFY, + IMG_ACTION_CAPTURE, }; enum fp_imgdev_enroll_state { @@ -179,8 +188,6 @@ struct fp_img_dev { void *priv; }; -int fpi_imgdev_capture(struct fp_img_dev *imgdev, int unconditional, - struct fp_img **image); int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev); int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev); @@ -215,6 +222,8 @@ struct fp_driver { int (*verify_stop)(struct fp_dev *dev, gboolean iterating); int (*identify_start)(struct fp_dev *dev); int (*identify_stop)(struct fp_dev *dev, gboolean iterating); + int (*capture_start)(struct fp_dev *dev); + int (*capture_stop)(struct fp_dev *dev); }; enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv); @@ -439,6 +448,11 @@ void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result, size_t match_offset, struct fp_img *img); void fpi_drvcb_identify_stopped(struct fp_dev *dev); +void fpi_drvcb_capture_started(struct fp_dev *dev, int status); +void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result, + struct fp_img *img); +void fpi_drvcb_capture_stopped(struct fp_dev *dev); + /* for image drivers */ void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status); void fpi_imgdev_close_complete(struct fp_img_dev *imgdev); diff --git a/libfprint/fprint.h b/libfprint/fprint.h index 99fa1e1..af1d686 100644 --- a/libfprint/fprint.h +++ b/libfprint/fprint.h @@ -107,6 +107,17 @@ uint32_t fp_dev_get_devtype(struct fp_dev *dev); int fp_dev_supports_print_data(struct fp_dev *dev, struct fp_print_data *data); int fp_dev_supports_dscv_print(struct fp_dev *dev, struct fp_dscv_print *print); +/** \ingroup dev + * Image capture result codes returned from fp_dev_img_capture(). + */ +enum fp_capture_result { + /** Capture completed successfully, the capture data has been + * returned to the caller. */ + FP_CAPTURE_COMPLETE = 0, + /** Capture failed for some reason */ + FP_CAPTURE_FAIL, +}; + int fp_dev_supports_imaging(struct fp_dev *dev); int fp_dev_img_capture(struct fp_dev *dev, int unconditional, struct fp_img **image); @@ -340,6 +351,13 @@ typedef void (*fp_identify_stop_cb)(struct fp_dev *dev, void *user_data); int fp_async_identify_stop(struct fp_dev *dev, fp_identify_stop_cb callback, void *user_data); +typedef void (*fp_capture_cb)(struct fp_dev *dev, int result, + struct fp_img *img, void *user_data); +int fp_async_capture_start(struct fp_dev *dev, int unconditional, fp_capture_cb callback, void *user_data); + +typedef void (*fp_capture_stop_cb)(struct fp_dev *dev, void *user_data); +int fp_async_capture_stop(struct fp_dev *dev, fp_capture_stop_cb callback, void *user_data); + #ifdef __cplusplus } #endif diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c index 1ed3f6d..f83ea11 100644 --- a/libfprint/imgdev.c +++ b/libfprint/imgdev.c @@ -163,6 +163,9 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev, imgdev->identify_match_offset, img); fp_print_data_free(data); break; + case IMG_ACTION_CAPTURE: + fpi_drvcb_report_capture_result(imgdev->dev, r, img); + break; default: fp_err("unhandled action %d", imgdev->action); break; @@ -231,18 +234,20 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img) fp_img_standardize(img); imgdev->acquire_img = img; - r = fpi_img_to_print_data(imgdev, img, &print); - if (r < 0) { - fp_dbg("image to print data conversion error: %d", r); - imgdev->action_result = FP_ENROLL_RETRY; - goto next_state; - } else if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) { - fp_dbg("not enough minutiae, %d/%d", img->minutiae->num, - MIN_ACCEPTABLE_MINUTIAE); - fp_print_data_free(print); - /* depends on FP_ENROLL_RETRY == FP_VERIFY_RETRY */ - imgdev->action_result = FP_ENROLL_RETRY; - goto next_state; + if (imgdev->action != IMG_ACTION_CAPTURE) { + r = fpi_img_to_print_data(imgdev, img, &print); + if (r < 0) { + fp_dbg("image to print data conversion error: %d", r); + imgdev->action_result = FP_ENROLL_RETRY; + goto next_state; + } else if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) { + fp_dbg("not enough minutiae, %d/%d", img->minutiae->num, + MIN_ACCEPTABLE_MINUTIAE); + fp_print_data_free(print); + /* depends on FP_ENROLL_RETRY == FP_VERIFY_RETRY */ + imgdev->action_result = FP_ENROLL_RETRY; + goto next_state; + } } imgdev->acquire_data = print; @@ -256,6 +261,9 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img) case IMG_ACTION_IDENTIFY: identify_process_img(imgdev); break; + case IMG_ACTION_CAPTURE: + imgdev->action_result = FP_CAPTURE_COMPLETE; + break; default: BUG(); break; @@ -280,6 +288,9 @@ void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error) case IMG_ACTION_IDENTIFY: fpi_drvcb_report_identify_result(imgdev->dev, error, 0, NULL); break; + case IMG_ACTION_CAPTURE: + fpi_drvcb_report_capture_result(imgdev->dev, error, NULL); + break; default: fp_err("unhandled action %d", imgdev->action); break; @@ -300,6 +311,9 @@ void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status) case IMG_ACTION_IDENTIFY: fpi_drvcb_identify_started(imgdev->dev, status); break; + case IMG_ACTION_CAPTURE: + fpi_drvcb_capture_started(imgdev->dev, status); + break; default: fp_err("unhandled action %d", imgdev->action); return; @@ -325,6 +339,9 @@ void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev) case IMG_ACTION_IDENTIFY: fpi_drvcb_identify_stopped(imgdev->dev); break; + case IMG_ACTION_CAPTURE: + fpi_drvcb_capture_stopped(imgdev->dev); + break; default: fp_err("unhandled action %d", imgdev->action); break; @@ -421,6 +438,14 @@ static int img_dev_identify_start(struct fp_dev *dev) return generic_acquire_start(dev, IMG_ACTION_IDENTIFY); } +static int img_dev_capture_start(struct fp_dev *dev) +{ + /* Unconditional capture is not supported yet */ + if (dev->unconditional_capture) + return -ENOTSUP; + return generic_acquire_start(dev, IMG_ACTION_CAPTURE); +} + static int img_dev_enroll_stop(struct fp_dev *dev) { struct fp_img_dev *imgdev = dev->priv; @@ -446,6 +471,14 @@ static int img_dev_identify_stop(struct fp_dev *dev, gboolean iterating) return 0; } +static int img_dev_capture_stop(struct fp_dev *dev) +{ + struct fp_img_dev *imgdev = dev->priv; + BUG_ON(imgdev->action != IMG_ACTION_CAPTURE); + generic_acquire_stop(imgdev); + return 0; +} + void fpi_img_driver_setup(struct fp_img_driver *idriver) { idriver->driver.type = DRIVER_IMAGING; @@ -457,5 +490,7 @@ void fpi_img_driver_setup(struct fp_img_driver *idriver) idriver->driver.verify_stop = img_dev_verify_stop; idriver->driver.identify_start = img_dev_identify_start; idriver->driver.identify_stop = img_dev_identify_stop; + idriver->driver.capture_start = img_dev_capture_start; + idriver->driver.capture_stop = img_dev_capture_stop; } diff --git a/libfprint/sync.c b/libfprint/sync.c index ca2f302..b3b2898 100644 --- a/libfprint/sync.c +++ b/libfprint/sync.c @@ -512,3 +512,100 @@ err: return r; } +struct sync_capture_data { + gboolean populated; + int result; + struct fp_img *img; +}; + +static void sync_capture_cb(struct fp_dev *dev, int result, struct fp_img *img, + void *user_data) +{ + struct sync_capture_data *vdata = user_data; + vdata->result = result; + vdata->img = img; + vdata->populated = TRUE; +} + +static void capture_stop_cb(struct fp_dev *dev, void *user_data) +{ + gboolean *stopped = user_data; + fp_dbg(""); + *stopped = TRUE; +} +/** \ingroup dev + * Captures an \ref img "image" from a device. The returned image is the raw + * image provided by the device, you may wish to \ref img_std "standardize" it. + * + * If set, the unconditional flag indicates that the device should + * capture an image unconditionally, regardless of whether a finger is there + * or not. If unset, this function will block until a finger is detected on + * the sensor. + * + * \param dev the device + * \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected + * \param img a location to return the captured image. Must be freed with + * fp_img_free() after use. + * \return 0 on success, non-zero on error. -ENOTSUP indicates that either the + * unconditional flag was set but the device does not support this, or that the + * device does not support imaging. + * \sa fp_dev_supports_imaging() + */ +API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional, + struct fp_img **img) +{ + struct sync_capture_data *vdata; + gboolean stopped = FALSE; + int r; + + if (!dev->drv->capture_start) { + fp_dbg("image capture is not supported on %s device", dev->drv->name); + return -ENOTSUP; + } + + fp_dbg("to be handled by %s", dev->drv->name); + vdata = g_malloc0(sizeof(struct sync_capture_data)); + r = fp_async_capture_start(dev, unconditional, sync_capture_cb, vdata); + if (r < 0) { + fp_dbg("capture_start error %d", r); + g_free(vdata); + return r; + } + + while (!vdata->populated) { + r = fp_handle_events(); + if (r < 0) { + g_free(vdata); + goto err; + } + } + + if (img) + *img = vdata->img; + else + fp_img_free(vdata->img); + + r = vdata->result; + g_free(vdata); + switch (r) { + case FP_CAPTURE_COMPLETE: + fp_dbg("result: complete"); + break; + case FP_CAPTURE_FAIL: + fp_dbg("result: fail"); + break; + default: + fp_err("unrecognised return code %d", r); + r = -EINVAL; + } + +err: + fp_dbg("ending capture"); + if (fp_async_capture_stop(dev, capture_stop_cb, &stopped) == 0) + while (!stopped) + if (fp_handle_events() < 0) + break; + + return r; +} +