lib: Major rewrite of the libfprint core and API
This is a rewrite of the core based on GObject and Gio. This commit breaks the build in a lot of ways, but basic functionality will start working again with the next commits.
This commit is contained in:
parent
30a449841c
commit
689aff0232
53 changed files with 8358 additions and 6759 deletions
|
@ -60,21 +60,18 @@
|
|||
<para>
|
||||
In summary, libfprint represents fingerprints in several internal structures
|
||||
and each representation will offer you a way of determining the
|
||||
<ulink url="#driver_id">driver ID</ulink> and <ulink url="#device-types">devtype</ulink> of the print in
|
||||
<ulink url="#driver">driver</ulink> and <ulink url="#device-id">device ID</ulink> of the print in
|
||||
question. Prints are only compatible if the driver ID <emphasis role="strong">and</emphasis> devtypes
|
||||
match. libfprint does offer you some "is this print compatible?" helper
|
||||
functions, so you don't have to worry about these details too much.
|
||||
</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2 id="driver_id">
|
||||
<title>Driver IDs</title>
|
||||
<refsect2 id="driver">
|
||||
<title>Driver</title>
|
||||
|
||||
<para>
|
||||
Each driver is assigned a unique ID by the project maintainer. These
|
||||
assignments are
|
||||
<ulink url="https://gitlab.freedesktop.org/libfprint/libfprint/blob/master/libfprint/drivers/driver_ids.h">
|
||||
documented in the sources</ulink> and will never change.
|
||||
Each driver is assigned a unique string identifier by the project maintainer.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
@ -89,22 +86,23 @@
|
|||
</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2 id="device-types">
|
||||
<title>Device types</title>
|
||||
<refsect2 id="device-id">
|
||||
<title>Device ID</title>
|
||||
|
||||
<para>
|
||||
Internally, the <ulink url="libfprint-Driver-operations.html#libfprint-Driver-operations.description">driver</ulink> behind a device assigns a 32-bit
|
||||
<emphasis>devtype</emphasis> identifier to the device. This cannot be used as a unique
|
||||
ID for a specific device as many devices under the same range may share
|
||||
the same devtype. The devtype may even be 0 in all cases.
|
||||
Internally, the behind a device assigns a string identifier to the device
|
||||
This cannot be used as a unique ID for a specific device as many devices
|
||||
under the same range may share the same devtype. The device ID may even
|
||||
be the same string in all cases. It is guaranteed to have a non-zero length
|
||||
and be a valid file name. It defaults to "0".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The only reason you may be interested in retrieving the devtype for a
|
||||
The only reason you may be interested in retrieving the device ID for a
|
||||
device is for the purpose of checking if some print data is compatible
|
||||
with a device. libfprint uses the devtype as one way of checking that the
|
||||
with a device. libfprint uses the device ID as one way of checking that the
|
||||
print you are verifying is compatible with the device in question - the
|
||||
devtypes must be equal. This effectively allows drivers to support more
|
||||
device ID must be equal. This effectively allows drivers to support more
|
||||
than one type of device where the data from each one is not compatible with
|
||||
the other. Note that libfprint does provide you with helper functions to
|
||||
determine whether a print is compatible with a device, so under most
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
|
||||
<para>
|
||||
Usually the first thing you want to do is determine which fingerprint
|
||||
devices are present. This is done through <ulink url="libfprint-Device-discovery.html">device discovery</ulink>.
|
||||
devices are present. This is done using the <ulink url="fp-context.html">FpContext</ulink> object.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once you have found a device you would like to operate, you should open it.
|
||||
Refer to <ulink url="libfprint-Devices-operations.html">device operations</ulink>. This section also details enrollment,
|
||||
Refer to <ulink url="fp-context.html">device operations</ulink>. This section also details enrollment,
|
||||
image capture, and verification.
|
||||
</para>
|
||||
|
||||
|
|
|
@ -25,39 +25,43 @@
|
|||
|
||||
<part>
|
||||
<title>Library API Documentation</title>
|
||||
<xi:include href="xml/events.xml"/>
|
||||
<xi:include href="xml/discovery.xml"/>
|
||||
|
||||
<xi:include href="xml/drv.xml"/>
|
||||
<xi:include href="xml/dev.xml"/>
|
||||
<xi:include href="xml/print_data.xml"/>
|
||||
<xi:include href="xml/img.xml"/>
|
||||
<xi:include href="xml/fp-context.xml"/>
|
||||
<xi:include href="xml/fp-device.xml"/>
|
||||
<xi:include href="xml/fp-image-device.xml"/>
|
||||
<xi:include href="xml/fp-print.xml"/>
|
||||
<xi:include href="xml/fp-image.xml"/>
|
||||
</part>
|
||||
|
||||
<part>
|
||||
<title>Writing Drivers</title>
|
||||
<chapter id="driver-helpers">
|
||||
<title>Logging, and async machinery</title>
|
||||
<xi:include href="xml/fpi-log.xml"/>
|
||||
<xi:include href="xml/fpi-ssm.xml"/>
|
||||
<xi:include href="xml/fpi-poll.xml"/>
|
||||
<chapter id="driver-dev">
|
||||
<title>Device methods for drivers</title>
|
||||
<xi:include href="xml/fpi-device.xml"/>
|
||||
<xi:include href="xml/fpi-image-device.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="driver-dev">
|
||||
<title>Device and driver structures</title>
|
||||
<xi:include href="xml/fpi-dev.xml"/>
|
||||
<xi:include href="xml/fpi-dev-img.xml"/>
|
||||
<xi:include href="xml/fpi-core.xml"/>
|
||||
<xi:include href="xml/fpi-core-img.xml"/>
|
||||
<chapter id="driver-helpers">
|
||||
<title>USB and State Machine helpers</title>
|
||||
<xi:include href="xml/fpi-usb-transfer.xml"/>
|
||||
<xi:include href="xml/fpi-ssm.xml"/>
|
||||
<xi:include href="xml/fpi-log.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="driver-img">
|
||||
<title>Image manipulation</title>
|
||||
<xi:include href="xml/fpi-img.xml"/>
|
||||
<xi:include href="xml/fpi-image.xml"/>
|
||||
<xi:include href="xml/fpi-assembling.xml"/>
|
||||
</chapter>
|
||||
|
||||
<xi:include href="xml/fpi-usb.xml"/>
|
||||
<chapter id="driver-print">
|
||||
<title>Print handling</title>
|
||||
<xi:include href="xml/fpi-print.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="driver-misc">
|
||||
<title>Listing drivers</title>
|
||||
<xi:include href="xml/fpi-context.xml"/>
|
||||
</chapter>
|
||||
</part>
|
||||
|
||||
<index id="api-index">
|
||||
|
|
|
@ -1,125 +1,191 @@
|
|||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>events</FILE>
|
||||
<TITLE>Initialisation and events handling</TITLE>
|
||||
LIBFPRINT_DEPRECATED
|
||||
fp_init
|
||||
fp_exit
|
||||
fp_pollfd
|
||||
fp_handle_events_timeout
|
||||
fp_handle_events
|
||||
fp_get_next_timeout
|
||||
fp_get_pollfds
|
||||
fp_pollfd_added_cb
|
||||
fp_pollfd_removed_cb
|
||||
fp_set_pollfd_notifiers
|
||||
<FILE>drivers_api</FILE>
|
||||
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>discovery</FILE>
|
||||
<TITLE>Device discovery</TITLE>
|
||||
fp_dscv_dev
|
||||
fp_discover_devs
|
||||
fp_dscv_devs_free
|
||||
fp_dscv_dev_get_driver
|
||||
fp_dscv_dev_get_devtype
|
||||
fp_dscv_dev_get_driver_id
|
||||
fp_dscv_dev_supports_print_data
|
||||
<FILE>fp-context</FILE>
|
||||
<TITLE>FpContext</TITLE>
|
||||
FP_TYPE_CONTEXT
|
||||
FpContextClass
|
||||
fp_context_new
|
||||
fp_context_enumerate
|
||||
fp_context_get_devices
|
||||
FpContext
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>drv</FILE>
|
||||
fp_driver
|
||||
fp_driver_get_name
|
||||
fp_driver_get_full_name
|
||||
fp_driver_get_driver_id
|
||||
fp_driver_get_scan_type
|
||||
fp_driver_supports_imaging
|
||||
<FILE>fp-device</FILE>
|
||||
FP_TYPE_DEVICE
|
||||
FP_DEVICE_RETRY
|
||||
FP_DEVICE_ERROR
|
||||
FpDeviceType
|
||||
FpScanType
|
||||
FpDeviceRetry
|
||||
FpDeviceError
|
||||
fp_device_retry_quark
|
||||
fp_device_error_quark
|
||||
FpEnrollProgress
|
||||
fp_device_get_driver
|
||||
fp_device_get_device_id
|
||||
fp_device_get_name
|
||||
fp_device_get_scan_type
|
||||
fp_device_get_nr_enroll_stages
|
||||
fp_device_has_storage
|
||||
fp_device_supports_identify
|
||||
fp_device_supports_capture
|
||||
fp_device_open
|
||||
fp_device_close
|
||||
fp_device_enroll
|
||||
fp_device_verify
|
||||
fp_device_identify
|
||||
fp_device_capture
|
||||
fp_device_delete_print
|
||||
fp_device_list_prints
|
||||
fp_device_open_finish
|
||||
fp_device_close_finish
|
||||
fp_device_enroll_finish
|
||||
fp_device_verify_finish
|
||||
fp_device_identify_finish
|
||||
fp_device_capture_finish
|
||||
fp_device_delete_print_finish
|
||||
fp_device_list_prints_finish
|
||||
fp_device_open_sync
|
||||
fp_device_close_sync
|
||||
fp_device_enroll_sync
|
||||
fp_device_verify_sync
|
||||
fp_device_identify_sync
|
||||
fp_device_capture_sync
|
||||
fp_device_delete_print_sync
|
||||
fp_device_list_prints_sync
|
||||
FpDevice
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>dev</FILE>
|
||||
fp_dev
|
||||
fp_scan_type
|
||||
fp_capture_result
|
||||
fp_enroll_result
|
||||
fp_verify_result
|
||||
|
||||
fp_dev_get_driver
|
||||
fp_dev_get_nr_enroll_stages
|
||||
fp_dev_get_devtype
|
||||
fp_dev_supports_print_data
|
||||
fp_dev_supports_imaging
|
||||
fp_dev_supports_identification
|
||||
fp_dev_get_img_width
|
||||
fp_dev_get_img_height
|
||||
|
||||
fp_operation_stop_cb
|
||||
fp_img_operation_cb
|
||||
fp_dev_open_cb
|
||||
fp_enroll_stage_cb
|
||||
fp_identify_cb
|
||||
|
||||
fp_dev_open
|
||||
fp_async_dev_open
|
||||
|
||||
fp_dev_close
|
||||
fp_async_dev_close
|
||||
|
||||
fp_enroll_finger
|
||||
fp_enroll_finger_img
|
||||
fp_async_enroll_start
|
||||
fp_async_enroll_stop
|
||||
|
||||
fp_verify_finger
|
||||
fp_verify_finger_img
|
||||
fp_async_verify_start
|
||||
fp_async_verify_stop
|
||||
|
||||
fp_identify_finger
|
||||
fp_identify_finger_img
|
||||
fp_async_identify_start
|
||||
fp_async_identify_stop
|
||||
|
||||
fp_dev_img_capture
|
||||
fp_async_capture_start
|
||||
fp_async_capture_stop
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>print_data</FILE>
|
||||
fp_finger
|
||||
fp_print_data
|
||||
fp_print_data_get_data
|
||||
fp_print_data_from_data
|
||||
fp_print_data_free
|
||||
fp_print_data_get_driver_id
|
||||
fp_print_data_get_devtype
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>img</FILE>
|
||||
fp_img
|
||||
fp_minutia
|
||||
fp_img_free
|
||||
fp_img_get_height
|
||||
fp_img_get_width
|
||||
fp_img_get_data
|
||||
fp_img_save_to_file
|
||||
fp_img_standardize
|
||||
fp_img_binarize
|
||||
fp_img_get_minutiae
|
||||
<FILE>fp-image</FILE>
|
||||
FP_TYPE_IMAGE
|
||||
FpMinutia
|
||||
fp_image_new
|
||||
fp_image_get_width
|
||||
fp_image_get_height
|
||||
fp_image_get_ppmm
|
||||
fp_image_get_minutiae
|
||||
fp_image_detect_minutiae
|
||||
fp_image_detect_minutiae_finish
|
||||
fp_image_get_data
|
||||
fp_image_get_binarized
|
||||
fp_minutia_get_coords
|
||||
FpImage
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fp-image-device</FILE>
|
||||
FP_TYPE_IMAGE_DEVICE
|
||||
FpImageDevice
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fp-print</FILE>
|
||||
FP_TYPE_PRINT
|
||||
FpFinger
|
||||
FpPrint
|
||||
fp_print_new
|
||||
fp_print_new_from_data
|
||||
fp_print_to_data
|
||||
fp_print_get_driver
|
||||
fp_print_get_device_id
|
||||
fp_print_get_device_stored
|
||||
fp_print_get_image
|
||||
fp_print_get_finger
|
||||
fp_print_get_username
|
||||
fp_print_get_description
|
||||
fp_print_get_enroll_date
|
||||
fp_print_set_finger
|
||||
fp_print_set_username
|
||||
fp_print_set_description
|
||||
fp_print_set_enroll_date
|
||||
fp_print_compatible
|
||||
fp_print_equal
|
||||
fp_print_serialize
|
||||
fp_print_deserialize
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-assembling</FILE>
|
||||
fpi_frame
|
||||
fpi_frame_asmbl_ctx
|
||||
fpi_do_movement_estimation
|
||||
fpi_assemble_frames
|
||||
fpi_line_asmbl_ctx
|
||||
fpi_assemble_lines
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-context</FILE>
|
||||
fpi_get_driver_types
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-device</FILE>
|
||||
FpDeviceClass
|
||||
FpTimeoutFunc
|
||||
FpDeviceAction
|
||||
FpIdEntry
|
||||
fpi_device_get_usb_device
|
||||
fpi_device_get_virtual_env
|
||||
fpi_device_get_current_action
|
||||
fpi_device_retry_new
|
||||
fpi_device_error_new
|
||||
fpi_device_retry_new_msg
|
||||
fpi_device_error_new_msg
|
||||
fpi_device_get_driver_data
|
||||
fpi_device_get_enroll_data
|
||||
fpi_device_get_capture_data
|
||||
fpi_device_get_verify_data
|
||||
fpi_device_get_identify_data
|
||||
fpi_device_get_delete_data
|
||||
fpi_device_get_cancellable
|
||||
fpi_device_action_is_cancelled
|
||||
fpi_device_add_timeout
|
||||
fpi_device_set_nr_enroll_stages
|
||||
fpi_device_set_scan_type
|
||||
fpi_device_action_error
|
||||
fpi_device_probe_complete
|
||||
fpi_device_open_complete
|
||||
fpi_device_close_complete
|
||||
fpi_device_enroll_complete
|
||||
fpi_device_verify_complete
|
||||
fpi_device_identify_complete
|
||||
fpi_device_capture_complete
|
||||
fpi_device_delete_complete
|
||||
fpi_device_enroll_progress
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-image</FILE>
|
||||
FpiImageFlags
|
||||
FpImage
|
||||
fpi_std_sq_dev
|
||||
fpi_mean_sq_diff_norm
|
||||
fpi_image_resize
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-image-device</FILE>
|
||||
<TITLE>FpImageDevice</TITLE>
|
||||
FpImageDeviceState
|
||||
FpImageDeviceClass
|
||||
fpi_image_device_session_error
|
||||
fpi_image_device_open_complete
|
||||
fpi_image_device_close_complete
|
||||
fpi_image_device_activate_complete
|
||||
fpi_image_device_deactivate_complete
|
||||
fpi_image_device_report_finger_status
|
||||
fpi_image_device_image_captured
|
||||
fpi_image_device_retry_scan
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-log.h</INCLUDE>
|
||||
<FILE>fpi-log</FILE>
|
||||
fp_dbg
|
||||
fp_info
|
||||
|
@ -130,121 +196,57 @@ BUG
|
|||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-ssm.h</INCLUDE>
|
||||
<FILE>fpi-ssm</FILE>
|
||||
fpi_ssm
|
||||
ssm_completed_fn
|
||||
ssm_handler_fn
|
||||
<FILE>fpi-print</FILE>
|
||||
FpPrintType
|
||||
FpiMatchResult
|
||||
fpi_print_add_print
|
||||
fpi_print_set_type
|
||||
fpi_print_set_device_stored
|
||||
fpi_print_add_from_image
|
||||
fpi_print_bz3_match
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>fpi-ssm</FILE>
|
||||
FpiSsmCompletedCallback
|
||||
FpiSsmHandlerCallback
|
||||
fpi_ssm_new
|
||||
fpi_ssm_free
|
||||
fpi_ssm_start
|
||||
fpi_ssm_start_subsm
|
||||
|
||||
fpi_ssm_next_state
|
||||
fpi_ssm_next_state_timeout_cb
|
||||
fpi_ssm_jump_to_state
|
||||
fpi_ssm_mark_completed
|
||||
fpi_ssm_mark_failed
|
||||
fpi_ssm_get_user_data
|
||||
fpi_ssm_get_error
|
||||
fpi_ssm_dup_error
|
||||
fpi_ssm_get_cur_state
|
||||
fpi_ssm_next_state_timeout_cb
|
||||
fpi_ssm_usb_transfer_cb
|
||||
FpiSsm
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-poll.h</INCLUDE>
|
||||
<FILE>fpi-poll</FILE>
|
||||
fpi_timeout
|
||||
fpi_timeout_fn
|
||||
fpi_timeout_add
|
||||
fpi_timeout_set_name
|
||||
fpi_timeout_cancel
|
||||
<FILE>fpi-usb-transfer</FILE>
|
||||
FPI_USB_ENDPOINT_IN
|
||||
FPI_USB_ENDPOINT_OUT
|
||||
FpiUsbTransferCallback
|
||||
FpiTransferType
|
||||
FpiUsbTransfer
|
||||
fpi_usb_transfer_new
|
||||
fpi_usb_transfer_ref
|
||||
fpi_usb_transfer_unref
|
||||
fpi_usb_transfer_set_short_error
|
||||
fpi_usb_transfer_fill_bulk
|
||||
fpi_usb_transfer_fill_bulk_full
|
||||
fpi_usb_transfer_fill_control
|
||||
fpi_usb_transfer_fill_interrupt
|
||||
fpi_usb_transfer_fill_interrupt_full
|
||||
fpi_usb_transfer_submit
|
||||
fpi_usb_transfer_submit_sync
|
||||
<SUBSECTION Standard>
|
||||
FPI_TYPE_USB_TRANSFER
|
||||
fpi_usb_transfer_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-dev.h</INCLUDE>
|
||||
<FILE>fpi-dev</FILE>
|
||||
fp_img_dev
|
||||
|
||||
FP_DEV
|
||||
FP_IMG_DEV
|
||||
fp_dev_set_instance_data
|
||||
FP_INSTANCE_DATA
|
||||
|
||||
fpi_dev_get_usb_dev
|
||||
fpi_dev_get_verify_data
|
||||
fpi_dev_set_nr_enroll_stages
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-dev-img.h</INCLUDE>
|
||||
<FILE>fpi-dev-img</FILE>
|
||||
fp_imgdev_action
|
||||
fp_imgdev_state
|
||||
fp_imgdev_enroll_state
|
||||
|
||||
fpi_imgdev_abort_scan
|
||||
fpi_imgdev_activate_complete
|
||||
fpi_imgdev_close_complete
|
||||
fpi_imgdev_deactivate_complete
|
||||
fpi_imgdev_get_action
|
||||
fpi_imgdev_get_action_result
|
||||
fpi_imgdev_get_action_state
|
||||
fpi_imgdev_image_captured
|
||||
fpi_imgdev_open_complete
|
||||
fpi_imgdev_report_finger_status
|
||||
fpi_imgdev_session_error
|
||||
fpi_imgdev_set_action_result
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-core.h</INCLUDE>
|
||||
<FILE>fpi-core</FILE>
|
||||
usb_id
|
||||
fp_driver_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-core.h</INCLUDE>
|
||||
<FILE>fpi-core-img</FILE>
|
||||
FpiImgDriverFlags
|
||||
fp_img_driver
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-img.h</INCLUDE>
|
||||
<FILE>fpi-img</FILE>
|
||||
FpiImgFlags
|
||||
|
||||
fpi_img_new
|
||||
fpi_img_new_for_imgdev
|
||||
fpi_img_realloc
|
||||
fpi_img_resize
|
||||
|
||||
fpi_std_sq_dev
|
||||
fpi_mean_sq_diff_norm
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-assembling.h</INCLUDE>
|
||||
<FILE>fpi-assembling</FILE>
|
||||
fpi_frame
|
||||
fpi_frame_asmbl_ctx
|
||||
fpi_line_asmbl_ctx
|
||||
|
||||
fpi_do_movement_estimation
|
||||
fpi_assemble_frames
|
||||
fpi_assemble_lines
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-usb.h</INCLUDE>
|
||||
<FILE>fpi-usb</FILE>
|
||||
fpi_usb_transfer
|
||||
|
||||
fpi_usb_transfer_cb_fn
|
||||
fpi_usb_alloc
|
||||
fpi_usb_fill_bulk_transfer
|
||||
fpi_usb_submit_transfer
|
||||
fpi_usb_cancel_transfer
|
||||
</SECTION>
|
||||
|
|
118
doc/libfprint-sections.txt-new-manual
Normal file
118
doc/libfprint-sections.txt-new-manual
Normal file
|
@ -0,0 +1,118 @@
|
|||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>context</FILE>
|
||||
<TITLE>Device discovery and hotplugging</TITLE>
|
||||
FP_TYPE_CONTEXT
|
||||
FpContext
|
||||
fp_context_new
|
||||
fp_context_enumerate
|
||||
fp_context_get_devices
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>device</FILE>
|
||||
<TITLE>Device</TITLE>
|
||||
FP_TYPE_DEVICE
|
||||
FpDevice
|
||||
FpDeviceType
|
||||
FpScanType
|
||||
FpDeviceRetry
|
||||
FpDeviceError
|
||||
FP_DEVICE_ERROR
|
||||
FP_DEVICE_RETRY
|
||||
fp_device_get_driver
|
||||
fp_device_get_device_id
|
||||
fp_device_get_name
|
||||
fp_device_get_scan_type
|
||||
|
||||
fp_device_open
|
||||
fp_device_open_finish
|
||||
fp_device_open_sync
|
||||
|
||||
fp_device_close
|
||||
fp_device_close_finish
|
||||
fp_device_close_sync
|
||||
|
||||
fp_device_enroll
|
||||
fp_device_enroll_finish
|
||||
fp_device_enroll_sync
|
||||
|
||||
fp_device_identify
|
||||
fp_device_identify_finish
|
||||
fp_device_identify_sync
|
||||
|
||||
fp_device_capture
|
||||
fp_device_capture_finish
|
||||
fp_device_capture_sync
|
||||
|
||||
fp_device_verify
|
||||
fp_device_verify_finish
|
||||
fp_device_verify_sync
|
||||
|
||||
_fp_device_get_cancellable
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>print</FILE>
|
||||
<TITLE>Fingerprint handling</TITLE>
|
||||
FpPrint
|
||||
fp_print_new
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>image</FILE>
|
||||
<TITLE>Image handling</TITLE>
|
||||
FP_TYPE_IMAGE
|
||||
FpImage
|
||||
fp_image_new
|
||||
fp_image_detect_minutiae
|
||||
fp_image_detect_minutiae_finish
|
||||
fp_image_device_new
|
||||
fp_image_get_binarized
|
||||
fp_image_get_data
|
||||
fp_image_get_height
|
||||
fp_image_get_minutiae
|
||||
fp_image_get_ppmm
|
||||
fp_image_get_width
|
||||
</SECTION>
|
||||
|
||||
|
||||
<SECTION>
|
||||
<FILE>internal-image-device</FILE>
|
||||
<INCLUDE>drivers_api.h</INCLUDE>
|
||||
<TITLE>Base class for image devices</TITLE>
|
||||
FpImageDevice
|
||||
FpImageDeviceClass
|
||||
FpImageDeviceState
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>internal-usb-transfers</FILE>
|
||||
<INCLUDE>drivers_api.h</INCLUDE>
|
||||
<TITLE>USB Transfers</TITLE>
|
||||
FpUsbTransfer
|
||||
fp_usb_transfer_fill_bulk
|
||||
fp_usb_transfer_fill_bulk_full
|
||||
fp_usb_transfer_fill_control
|
||||
fp_usb_transfer_fill_interrupt
|
||||
fp_usb_transfer_fill_interrupt_full
|
||||
fp_usb_transfer_get_type
|
||||
fp_usb_transfer_new
|
||||
fp_usb_transfer_ref
|
||||
fp_usb_transfer_set_short_error
|
||||
fp_usb_transfer_submit
|
||||
fp_usb_transfer_submit_sync
|
||||
fp_usb_transfer_unref
|
||||
FpUsbTransferCallback
|
||||
FP_USB_ENDPOINT_IN
|
||||
FP_USB_ENDPOINT_OUT
|
||||
</SECTION>
|
||||
|
||||
|
|
@ -2,41 +2,13 @@ subdir('xml')
|
|||
|
||||
private_headers = [
|
||||
'config.h',
|
||||
|
||||
'aeslib.h',
|
||||
'assembling.h',
|
||||
'fp_internal.h',
|
||||
'nbis-helpers.h',
|
||||
'fpi-async.h',
|
||||
'fpi-data.h',
|
||||
'fprint.h',
|
||||
'fp_internal.h',
|
||||
|
||||
# Drivers
|
||||
'aes1660.h',
|
||||
'aes2501.h',
|
||||
'aes2550.h',
|
||||
'aes2660.h',
|
||||
'aes3k.h',
|
||||
'aesx660.h',
|
||||
'driver_ids.h',
|
||||
'elan.h',
|
||||
'upek_proto.h',
|
||||
'upeksonly.h',
|
||||
'upektc.h',
|
||||
'upektc_img.h',
|
||||
'vfs0050.h',
|
||||
'vfs301_proto_fragments.h',
|
||||
'vfs301_proto.h',
|
||||
'vfs5011_proto.h',
|
||||
|
||||
# NBIS
|
||||
'morph.h',
|
||||
'sunrast.h',
|
||||
'bozorth.h',
|
||||
'defs.h',
|
||||
'log.h',
|
||||
'bz_array.h',
|
||||
'lfs.h',
|
||||
'mytime.h',
|
||||
# Subdirectories to ignore
|
||||
'drivers',
|
||||
'nbis',
|
||||
]
|
||||
|
||||
html_images = [
|
||||
|
@ -59,6 +31,7 @@ gnome.gtkdoc('libfprint',
|
|||
content_files: content_files,
|
||||
expand_content_files: expand_content_files,
|
||||
scan_args: [
|
||||
#'--rebuild-sections',
|
||||
'--ignore-decorators=API_EXPORTED',
|
||||
'--ignore-headers=' + ' '.join(private_headers),
|
||||
],
|
||||
|
|
|
@ -23,17 +23,12 @@
|
|||
|
||||
#include <config.h>
|
||||
|
||||
#include "fprint.h"
|
||||
#include "fp_internal.h"
|
||||
|
||||
#include "fpi-log.h"
|
||||
#include "fpi-dev.h"
|
||||
#include "fpi-dev-img.h"
|
||||
#include "fpi-core.h"
|
||||
#include "fpi-usb-transfer.h"
|
||||
#include "fpi-ssm.h"
|
||||
#include "fpi-poll.h"
|
||||
#include "fpi-dev.h"
|
||||
#include "fpi-usb.h"
|
||||
#include "fpi-img.h"
|
||||
#include "fpi-assembling.h"
|
||||
#include "drivers/driver_ids.h"
|
||||
#include "fpi-image-device.h"
|
||||
|
||||
#endif
|
||||
|
|
364
libfprint/fp-context.c
Normal file
364
libfprint/fp-context.c
Normal file
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* FpContext - A FPrint context
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "context"
|
||||
#include <fpi-log.h>
|
||||
|
||||
#include "fpi-context.h"
|
||||
#include "fpi-device.h"
|
||||
#include <gusb.h>
|
||||
|
||||
/**
|
||||
* SECTION: fp-context
|
||||
* @title: FpContext
|
||||
* @short_description: Discover fingerprint devices
|
||||
*
|
||||
* The #FpContext allows you to discover fingerprint scanning hardware. This
|
||||
* is the starting point when integrating libfprint into your software.
|
||||
*
|
||||
* The <link linkend="device-added">device-added</link> and device-removed signals allow you to handle devices
|
||||
* that may be hotplugged at runtime.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GUsbContext *usb_ctx;
|
||||
GCancellable *cancellable;
|
||||
|
||||
gint pending_devices;
|
||||
gboolean enumerated;
|
||||
|
||||
GArray *drivers;
|
||||
GPtrArray *devices;
|
||||
} FpContextPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (FpContext, fp_context, G_TYPE_OBJECT)
|
||||
|
||||
enum {
|
||||
DEVICE_ADDED_SIGNAL,
|
||||
DEVICE_REMOVED_SIGNAL,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
async_device_init_done_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpDevice *device;
|
||||
FpContext *context;
|
||||
FpContextPrivate *priv;
|
||||
|
||||
device = (FpDevice *) g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error);
|
||||
if (!device)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
return;
|
||||
|
||||
context = FP_CONTEXT (user_data);
|
||||
priv = fp_context_get_instance_private (context);
|
||||
priv->pending_devices--;
|
||||
g_message ("Ignoring device due to initialization error: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
context = FP_CONTEXT (user_data);
|
||||
priv = fp_context_get_instance_private (context);
|
||||
priv->pending_devices--;
|
||||
g_ptr_array_add (priv->devices, device);
|
||||
g_signal_emit (context, signals[DEVICE_ADDED_SIGNAL], 0, device);
|
||||
}
|
||||
|
||||
static void
|
||||
usb_device_added_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx)
|
||||
{
|
||||
FpContextPrivate *priv = fp_context_get_instance_private (self);
|
||||
GType found_driver = G_TYPE_NONE;
|
||||
const FpIdEntry *found_entry = NULL;
|
||||
gint found_score = 0;
|
||||
gint i;
|
||||
guint16 pid, vid;
|
||||
|
||||
pid = g_usb_device_get_pid (device);
|
||||
vid = g_usb_device_get_vid (device);
|
||||
|
||||
/* Find the best driver to handle this USB device. */
|
||||
for (i = 0; i < priv->drivers->len; i++)
|
||||
{
|
||||
GType driver = g_array_index (priv->drivers, GType, i);
|
||||
FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver));
|
||||
const FpIdEntry *entry;
|
||||
|
||||
if (cls->type != FP_DEVICE_TYPE_USB)
|
||||
{
|
||||
g_type_class_unref (cls);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (entry = cls->id_table; entry->pid; entry++)
|
||||
{
|
||||
gint driver_score = 50;
|
||||
|
||||
if (entry->pid != pid || entry->vid != vid)
|
||||
continue;
|
||||
|
||||
if (cls->usb_discover)
|
||||
driver_score = cls->usb_discover (device);
|
||||
|
||||
/* Is this driver better than the one we had? */
|
||||
if (driver_score <= found_score)
|
||||
continue;
|
||||
|
||||
found_score = driver_score;
|
||||
found_driver = driver;
|
||||
found_entry = entry;
|
||||
}
|
||||
|
||||
g_type_class_unref (cls);
|
||||
}
|
||||
|
||||
if (found_driver == G_TYPE_NONE)
|
||||
{
|
||||
g_debug ("No driver found for USB device %04X:%04X", pid, vid);
|
||||
return;
|
||||
}
|
||||
|
||||
priv->pending_devices++;
|
||||
g_async_initable_new_async (found_driver,
|
||||
G_PRIORITY_LOW,
|
||||
priv->cancellable,
|
||||
async_device_init_done_cb,
|
||||
self,
|
||||
"fp-usb-device", device,
|
||||
"fp-driver-data", found_entry->driver_data,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
usb_device_removed_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx)
|
||||
{
|
||||
FpContextPrivate *priv = fp_context_get_instance_private (self);
|
||||
gint i;
|
||||
|
||||
/* Do the lazy way and just look at each device. */
|
||||
for (i = 0; i < priv->devices->len; i++)
|
||||
{
|
||||
FpDevice *dev = g_ptr_array_index (priv->devices, i);
|
||||
FpDeviceClass *cls = FP_DEVICE_GET_CLASS (dev);
|
||||
|
||||
if (cls->type != FP_DEVICE_TYPE_USB)
|
||||
continue;
|
||||
|
||||
if (fpi_device_get_usb_device (dev) == device)
|
||||
{
|
||||
g_signal_emit (self, signals[DEVICE_REMOVED_SIGNAL], 0, dev);
|
||||
g_ptr_array_remove_index_fast (priv->devices, i);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fp_context_finalize (GObject *object)
|
||||
{
|
||||
FpContext *self = (FpContext *) object;
|
||||
FpContextPrivate *priv = fp_context_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->devices, g_ptr_array_unref);
|
||||
|
||||
g_cancellable_cancel (priv->cancellable);
|
||||
g_clear_object (&priv->cancellable);
|
||||
g_clear_pointer (&priv->drivers, g_array_unref);
|
||||
g_clear_object (&priv->usb_ctx);
|
||||
|
||||
G_OBJECT_CLASS (fp_context_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_context_class_init (FpContextClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = fp_context_finalize;
|
||||
|
||||
/**
|
||||
* FpContext::device-added:
|
||||
* @context: the #FpContext instance that emitted the signal
|
||||
* @device: A #FpDevice
|
||||
*
|
||||
* This signal is emitted when a fingerprint reader is added.
|
||||
**/
|
||||
signals[DEVICE_ADDED_SIGNAL] = g_signal_new ("device-added",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (FpContextClass, device_added),
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
FP_TYPE_DEVICE);
|
||||
|
||||
/**
|
||||
* FpContext::device-removed:
|
||||
* @context: the #FpContext instance that emitted the signal
|
||||
* @device: A #FpDevice
|
||||
*
|
||||
* This signal is emitted when a fingerprint reader is removed.
|
||||
**/
|
||||
signals[DEVICE_REMOVED_SIGNAL] = g_signal_new ("device-removed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (FpContextClass, device_removed),
|
||||
NULL,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
FP_TYPE_DEVICE);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_context_init (FpContext *self)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
FpContextPrivate *priv = fp_context_get_instance_private (self);
|
||||
|
||||
priv->drivers = g_array_new (TRUE, FALSE, sizeof (GType));
|
||||
fpi_get_driver_types (priv->drivers);
|
||||
|
||||
priv->devices = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
priv->cancellable = g_cancellable_new ();
|
||||
priv->usb_ctx = g_usb_context_new (&error);
|
||||
if (!priv->usb_ctx)
|
||||
{
|
||||
fp_warn ("Could not initialise USB Subsystem: %s", error->message);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_usb_context_set_debug (priv->usb_ctx, G_LOG_LEVEL_INFO);
|
||||
g_signal_connect_object (priv->usb_ctx,
|
||||
"device-added",
|
||||
G_CALLBACK (usb_device_added_cb),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
g_signal_connect_object (priv->usb_ctx,
|
||||
"device-removed",
|
||||
G_CALLBACK (usb_device_removed_cb),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_context_new:
|
||||
*
|
||||
* Create a new #FpContext.
|
||||
*
|
||||
* Returns: (transfer full): a newly created #FpContext
|
||||
*/
|
||||
FpContext *
|
||||
fp_context_new (void)
|
||||
{
|
||||
return g_object_new (FP_TYPE_CONTEXT, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_context_enumerate:
|
||||
* @context: a #FpContext
|
||||
*
|
||||
* Enumerate all devices. You should call this function exactly once
|
||||
* at startup. Please note that it iterates the mainloop until all
|
||||
* devices are enumerated.
|
||||
*/
|
||||
void
|
||||
fp_context_enumerate (FpContext *context)
|
||||
{
|
||||
FpContextPrivate *priv = fp_context_get_instance_private (context);
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (FP_IS_CONTEXT (context));
|
||||
|
||||
if (priv->enumerated)
|
||||
return;
|
||||
|
||||
priv->enumerated = TRUE;
|
||||
|
||||
/* USB devices are handled from callbacks */
|
||||
g_usb_context_enumerate (priv->usb_ctx);
|
||||
|
||||
/* Handle Virtual devices based on environment variables */
|
||||
for (i = 0; i < priv->drivers->len; i++)
|
||||
{
|
||||
GType driver = g_array_index (priv->drivers, GType, i);
|
||||
FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver));
|
||||
const FpIdEntry *entry;
|
||||
|
||||
if (cls->type != FP_DEVICE_TYPE_VIRTUAL)
|
||||
continue;
|
||||
|
||||
for (entry = cls->id_table; entry->pid; entry++)
|
||||
{
|
||||
const gchar *val;
|
||||
|
||||
val = g_getenv (entry->virtual_envvar);
|
||||
if (!val || val[0] == '\0')
|
||||
continue;
|
||||
|
||||
g_debug ("Found virtual environment device: %s, %s", entry->virtual_envvar, val);
|
||||
priv->pending_devices++;
|
||||
g_async_initable_new_async (driver,
|
||||
G_PRIORITY_LOW,
|
||||
priv->cancellable,
|
||||
async_device_init_done_cb,
|
||||
context,
|
||||
"fp-environ", val,
|
||||
"fp-driver-data", entry->driver_data,
|
||||
NULL);
|
||||
g_debug ("created");
|
||||
}
|
||||
|
||||
g_type_class_unref (cls);
|
||||
}
|
||||
|
||||
while (priv->pending_devices)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_context_get_devices:
|
||||
* @context: a #FpContext
|
||||
*
|
||||
* Get all devices. fp_context_enumerate() will be called as needed.
|
||||
*
|
||||
* Returns: (transfer none) (element-type FpDevice): a new #GPtrArray of #GUsbDevice's.
|
||||
*/
|
||||
GPtrArray *
|
||||
fp_context_get_devices (FpContext *context)
|
||||
{
|
||||
FpContextPrivate *priv = fp_context_get_instance_private (context);
|
||||
|
||||
g_return_val_if_fail (FP_IS_CONTEXT (context), NULL);
|
||||
|
||||
fp_context_enumerate (context);
|
||||
|
||||
return priv->devices;
|
||||
}
|
52
libfprint/fp-context.h
Normal file
52
libfprint/fp-context.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* FpContext - A FPrint context
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fp-device.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FP_TYPE_CONTEXT (fp_context_get_type ())
|
||||
G_DECLARE_DERIVABLE_TYPE (FpContext, fp_context, FP, CONTEXT, GObject)
|
||||
|
||||
/**
|
||||
* FpContextClass:
|
||||
* @device_added: Called when a new device is added
|
||||
* @device_removed: Called when a device is removed
|
||||
*
|
||||
* Class structure for #FpContext instances.
|
||||
*/
|
||||
struct _FpContextClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*device_added) (FpContext *context,
|
||||
FpDevice *device);
|
||||
void (*device_removed) (FpContext *context,
|
||||
FpDevice *device);
|
||||
};
|
||||
|
||||
FpContext *fp_context_new (void);
|
||||
|
||||
void fp_context_enumerate (FpContext *context);
|
||||
|
||||
GPtrArray *fp_context_get_devices (FpContext *context);
|
||||
|
||||
G_END_DECLS
|
2574
libfprint/fp-device.c
Normal file
2574
libfprint/fp-device.c
Normal file
File diff suppressed because it is too large
Load diff
255
libfprint/fp-device.h
Normal file
255
libfprint/fp-device.h
Normal file
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* FpDevice - A fingerprint reader device
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fp-image.h"
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FP_TYPE_DEVICE (fp_device_get_type ())
|
||||
#define FP_DEVICE_RETRY (fp_device_retry_quark ())
|
||||
#define FP_DEVICE_ERROR (fp_device_error_quark ())
|
||||
G_DECLARE_DERIVABLE_TYPE (FpDevice, fp_device, FP, DEVICE, GObject)
|
||||
|
||||
#include "fp-print.h"
|
||||
|
||||
/* NOTE: We keep the class struct private! */
|
||||
|
||||
/**
|
||||
* FpDeviceType:
|
||||
* @FP_DEVICE_TYPE_VIRTUAL: The device is a virtual device
|
||||
* @FP_DEVICE_TYPE_USB: The device is a USB device
|
||||
*/
|
||||
typedef enum {
|
||||
FP_DEVICE_TYPE_VIRTUAL,
|
||||
FP_DEVICE_TYPE_USB,
|
||||
} FpDeviceType;
|
||||
|
||||
/**
|
||||
* FpScanType:
|
||||
* @FP_SCAN_TYPE_SWIPE: Sensor requires swiping the finger.
|
||||
* @FP_SCAN_TYPE_PRESS: Sensor requires placing/pressing down the finger.
|
||||
*/
|
||||
typedef enum {
|
||||
FP_SCAN_TYPE_SWIPE,
|
||||
FP_SCAN_TYPE_PRESS,
|
||||
} FpScanType;
|
||||
|
||||
/**
|
||||
* FpDeviceRetry:
|
||||
* @FP_DEVICE_RETRY_GENERAL: The scan did not succeed due to poor scan quality
|
||||
* or other general user scanning problem.
|
||||
* @FP_DEVICE_RETRY_TOO_SHORT: The scan did not succeed because the finger
|
||||
* swipe was too short.
|
||||
* @FP_DEVICE_RETRY_CENTER_FINGER: The scan did not succeed because the finger
|
||||
* was not centered on the scanner.
|
||||
* @FP_DEVICE_RETRY_REMOVE_FINGER: The scan did not succeed due to quality or
|
||||
* pressure problems; the user should remove their finger from the scanner
|
||||
* before retrying.
|
||||
*
|
||||
* Error codes representing scan failures resulting in the user needing to
|
||||
* retry.
|
||||
*/
|
||||
typedef enum {
|
||||
FP_DEVICE_RETRY_GENERAL,
|
||||
FP_DEVICE_RETRY_TOO_SHORT,
|
||||
FP_DEVICE_RETRY_CENTER_FINGER,
|
||||
FP_DEVICE_RETRY_REMOVE_FINGER,
|
||||
} FpDeviceRetry;
|
||||
|
||||
/**
|
||||
* FpDeviceError:
|
||||
* @FP_DEVICE_ERROR_GENERAL: A general error occured.
|
||||
* @FP_DEVICE_ERROR_NOT_SUPPORTED: The device does not support the requested
|
||||
* operation.
|
||||
* @FP_DEVICE_ERROR_NOT_OPEN: The device needs to be opened to start this
|
||||
* operation.
|
||||
* @FP_DEVICE_ERROR_ALREADY_OPEN: The device has already been opened.
|
||||
* @FP_DEVICE_ERROR_BUSY: The device is busy with another request.
|
||||
* @FP_DEVICE_ERROR_PROTO: Protocol error
|
||||
* @FP_DEVICE_ERROR_DATA_INVALID: The passed data is invalid
|
||||
* @FP_DEVICE_ERROR_DATA_NOT_FOUND: Requested print was not found on device
|
||||
* @FP_DEVICE_ERROR_DATA_FULL: No space on device available for operation
|
||||
*
|
||||
* Error codes for device operations. More specific errors from other domains
|
||||
* such as #G_IO_ERROR or #G_USB_DEVICE_ERROR may also be reported.
|
||||
*/
|
||||
typedef enum {
|
||||
FP_DEVICE_ERROR_GENERAL,
|
||||
FP_DEVICE_ERROR_NOT_SUPPORTED,
|
||||
FP_DEVICE_ERROR_NOT_OPEN,
|
||||
FP_DEVICE_ERROR_ALREADY_OPEN,
|
||||
FP_DEVICE_ERROR_BUSY,
|
||||
FP_DEVICE_ERROR_PROTO,
|
||||
FP_DEVICE_ERROR_DATA_INVALID,
|
||||
FP_DEVICE_ERROR_DATA_NOT_FOUND,
|
||||
FP_DEVICE_ERROR_DATA_FULL,
|
||||
} FpDeviceError;
|
||||
|
||||
GQuark fp_device_retry_quark (void);
|
||||
GQuark fp_device_error_quark (void);
|
||||
|
||||
/**
|
||||
* FpEnrollProgress:
|
||||
* @device: a #FpDevice
|
||||
* @completed_stages: Number of completed stages
|
||||
* @print: (nullable): The last scaned print
|
||||
* @user_data: (nullable): User provided data
|
||||
* @error: (nullable) (transfer none): #GError or %NULL
|
||||
*
|
||||
* The passed error is guaranteed to be of type %FP_DEVICE_RETRY if set.
|
||||
*/
|
||||
typedef void (*FpEnrollProgress) (FpDevice *device,
|
||||
gint completed_stages,
|
||||
FpPrint *print,
|
||||
gpointer user_data,
|
||||
GError *error);
|
||||
|
||||
|
||||
const gchar *fp_device_get_driver (FpDevice *device);
|
||||
const gchar *fp_device_get_device_id (FpDevice *device);
|
||||
const gchar *fp_device_get_name (FpDevice *device);
|
||||
FpScanType fp_device_get_scan_type (FpDevice *device);
|
||||
gint fp_device_get_nr_enroll_stages (FpDevice *device);
|
||||
|
||||
gboolean fp_device_supports_identify (FpDevice *device);
|
||||
gboolean fp_device_supports_capture (FpDevice *device);
|
||||
gboolean fp_device_has_storage (FpDevice *device);
|
||||
|
||||
/* Opening the device */
|
||||
void fp_device_open (FpDevice *device,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
void fp_device_close (FpDevice *device,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
void fp_device_enroll (FpDevice *device,
|
||||
FpPrint *template_print,
|
||||
GCancellable *cancellable,
|
||||
FpEnrollProgress progress_cb,
|
||||
gpointer progress_data,
|
||||
GDestroyNotify progress_destroy,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
void fp_device_verify (FpDevice *device,
|
||||
FpPrint *enrolled_print,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
void fp_device_identify (FpDevice *device,
|
||||
GPtrArray *prints,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
void fp_device_capture (FpDevice *device,
|
||||
gboolean wait_for_finger,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
void fp_device_delete_print (FpDevice *device,
|
||||
FpPrint *enrolled_print,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
void fp_device_list_prints (FpDevice *device,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean fp_device_open_finish (FpDevice *device,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
gboolean fp_device_close_finish (FpDevice *device,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
FpPrint *fp_device_enroll_finish (FpDevice *device,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
gboolean fp_device_verify_finish (FpDevice *device,
|
||||
GAsyncResult *result,
|
||||
gboolean *match,
|
||||
FpPrint **print,
|
||||
GError **error);
|
||||
gboolean fp_device_identify_finish (FpDevice *device,
|
||||
GAsyncResult *result,
|
||||
FpPrint **match,
|
||||
FpPrint **print,
|
||||
GError **error);
|
||||
FpImage * fp_device_capture_finish (FpDevice *device,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
gboolean fp_device_delete_print_finish (FpDevice *device,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
GPtrArray * fp_device_list_prints_finish (FpDevice *device,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
|
||||
gboolean fp_device_open_sync (FpDevice *device,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean fp_device_close_sync (FpDevice *device,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
FpPrint * fp_device_enroll_sync (FpDevice *device,
|
||||
FpPrint *template_print,
|
||||
GCancellable *cancellable,
|
||||
FpEnrollProgress progress_cb,
|
||||
gpointer progress_data,
|
||||
GError **error);
|
||||
gboolean fp_device_verify_sync (FpDevice *device,
|
||||
FpPrint *enrolled_print,
|
||||
GCancellable *cancellable,
|
||||
gboolean *match,
|
||||
FpPrint **print,
|
||||
GError **error);
|
||||
gboolean fp_device_identify_sync (FpDevice *device,
|
||||
GPtrArray *prints,
|
||||
GCancellable *cancellable,
|
||||
FpPrint **match,
|
||||
FpPrint **print,
|
||||
GError **error);
|
||||
FpImage * fp_device_capture_sync (FpDevice *device,
|
||||
gboolean wait_for_finger,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean fp_device_delete_print_sync (FpDevice *device,
|
||||
FpPrint *enrolled_print,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GPtrArray * fp_device_list_prints_sync (FpDevice *device,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_END_DECLS
|
795
libfprint/fp-image-device.c
Normal file
795
libfprint/fp-image-device.c
Normal file
|
@ -0,0 +1,795 @@
|
|||
/*
|
||||
* FpImageDevice - An image based fingerprint reader device
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "image_device"
|
||||
#include "fpi-log.h"
|
||||
|
||||
#include "fpi-image-device.h"
|
||||
#include "fpi-print.h"
|
||||
#include "fpi-image.h"
|
||||
|
||||
#define MIN_ACCEPTABLE_MINUTIAE 10
|
||||
#define BOZORTH3_DEFAULT_THRESHOLD 40
|
||||
#define IMG_ENROLL_STAGES 5
|
||||
|
||||
/**
|
||||
* SECTION: fp-image-device
|
||||
* @title: FpImageDevice
|
||||
* @short_description: Image device subclass
|
||||
*
|
||||
* This is a helper class for the commonly found image based devices.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION: fpi-image-device
|
||||
* @title: Internal FpImageDevice
|
||||
* @short_description: Internal image device routines
|
||||
*
|
||||
* See #FpImageDeviceClass for more details. Also see the public
|
||||
* #FpImageDevice routines.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FpImageDeviceState state;
|
||||
gboolean active;
|
||||
|
||||
gint enroll_stage;
|
||||
|
||||
guint pending_activation_timeout_id;
|
||||
gboolean pending_activation_timeout_waiting_finger_off;
|
||||
|
||||
gint bz3_threshold;
|
||||
} FpImageDevicePrivate;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (FpImageDevice, fp_image_device, FP_TYPE_DEVICE)
|
||||
|
||||
|
||||
/*******************************************************/
|
||||
|
||||
/* TODO:
|
||||
* - sanitize_image seems a bit odd, in particular the sizing stuff.
|
||||
**/
|
||||
|
||||
/* Static helper functions */
|
||||
|
||||
static void
|
||||
fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
|
||||
|
||||
/* Cannot change to inactive using this function. */
|
||||
g_assert (state != FP_IMAGE_DEVICE_STATE_INACTIVE);
|
||||
|
||||
/* We might have been waiting for the finger to go OFF to start the
|
||||
* next operation. */
|
||||
if (priv->pending_activation_timeout_id)
|
||||
{
|
||||
g_source_remove (priv->pending_activation_timeout_id);
|
||||
priv->pending_activation_timeout_id = 0;
|
||||
}
|
||||
|
||||
fp_dbg ("Image device internal state change from %d to %d\n", priv->state, state);
|
||||
|
||||
priv->state = state;
|
||||
|
||||
/* change_state is the only callback which is optional and does not
|
||||
* have a default implementation. */
|
||||
if (cls->change_state)
|
||||
cls->change_state (self, state);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_device_activate (FpImageDevice *self)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
|
||||
|
||||
g_assert (!priv->active);
|
||||
|
||||
/* We don't have a neutral ACTIVE state, but we always will
|
||||
* go into WAIT_FINGER_ON afterwards. */
|
||||
priv->state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON;
|
||||
|
||||
/* We might have been waiting for deactivation to finish before
|
||||
* starting the next operation. */
|
||||
if (priv->pending_activation_timeout_id)
|
||||
{
|
||||
g_source_remove (priv->pending_activation_timeout_id);
|
||||
priv->pending_activation_timeout_id = 0;
|
||||
}
|
||||
|
||||
fp_dbg ("Activating image device\n");
|
||||
cls->activate (self);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_device_deactivate (FpDevice *device)
|
||||
{
|
||||
FpImageDevice *self = FP_IMAGE_DEVICE (device);
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (device);
|
||||
|
||||
if (!priv->active)
|
||||
{
|
||||
/* XXX: We currently deactivate both from minutiae scan result
|
||||
* and finger off report. */
|
||||
fp_dbg ("Already deactivated, ignoring request.");
|
||||
return;
|
||||
}
|
||||
priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE;
|
||||
|
||||
fp_dbg ("Deactivating image device\n");
|
||||
cls->deactivate (self);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pending_activation_timeout (gpointer user_data)
|
||||
{
|
||||
FpImageDevice *self = FP_IMAGE_DEVICE (user_data);
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
|
||||
priv->pending_activation_timeout_id = 0;
|
||||
|
||||
if (priv->pending_activation_timeout_waiting_finger_off)
|
||||
fpi_device_action_error (FP_DEVICE (self),
|
||||
fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER,
|
||||
"Remove finger before requesting another scan operation"));
|
||||
else
|
||||
fpi_device_action_error (FP_DEVICE (self),
|
||||
fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/* Callbacks/vfuncs */
|
||||
static void
|
||||
fp_image_device_open (FpDevice *device)
|
||||
{
|
||||
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (device);
|
||||
|
||||
/* Nothing special about opening an image device, just
|
||||
* forward the request. */
|
||||
cls->img_open (FP_IMAGE_DEVICE (device));
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_device_close (FpDevice *device)
|
||||
{
|
||||
FpImageDevice *self = FP_IMAGE_DEVICE (device);
|
||||
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
|
||||
/* In the close case we may need to wait/force deactivation first.
|
||||
* Three possible cases:
|
||||
* 1. We are inactive
|
||||
* -> immediately close
|
||||
* 2. We are waiting for finger off
|
||||
* -> imediately deactivate
|
||||
* 3. We are deactivating
|
||||
* -> handled by deactivate_complete */
|
||||
|
||||
if (!priv->active)
|
||||
cls->img_close (self);
|
||||
else if (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE)
|
||||
fp_image_device_deactivate (device);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_device_cancel_action (FpDevice *device)
|
||||
{
|
||||
FpImageDevice *self = FP_IMAGE_DEVICE (device);
|
||||
FpDeviceAction action;
|
||||
|
||||
action = fpi_device_get_current_action (device);
|
||||
|
||||
/* We can only cancel capture operations, in that case, deactivate and return
|
||||
* an error immediately. */
|
||||
if (action == FP_DEVICE_ACTION_ENROLL ||
|
||||
action == FP_DEVICE_ACTION_VERIFY ||
|
||||
action == FP_DEVICE_ACTION_IDENTIFY ||
|
||||
action == FP_DEVICE_ACTION_CAPTURE)
|
||||
{
|
||||
fp_image_device_deactivate (FP_DEVICE (self));
|
||||
|
||||
/* XXX: Some nicer way of doing this would be good. */
|
||||
fpi_device_action_error (FP_DEVICE (self),
|
||||
g_error_new (G_IO_ERROR,
|
||||
G_IO_ERROR_CANCELLED,
|
||||
"Device operation was cancelled"));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_device_start_capture_action (FpDevice *device)
|
||||
{
|
||||
FpImageDevice *self = FP_IMAGE_DEVICE (device);
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpDeviceAction action;
|
||||
|
||||
/* There is just one action that we cannot support out
|
||||
* of the box, which is a capture without first waiting
|
||||
* for a finger to be on the device.
|
||||
*/
|
||||
action = fpi_device_get_current_action (device);
|
||||
if (action == FP_DEVICE_ACTION_CAPTURE)
|
||||
{
|
||||
gboolean wait_for_finger;
|
||||
|
||||
fpi_device_get_capture_data (device, &wait_for_finger);
|
||||
|
||||
if (!wait_for_finger)
|
||||
{
|
||||
fpi_device_action_error (device, fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (action == FP_DEVICE_ACTION_ENROLL)
|
||||
{
|
||||
FpPrint *enroll_print = NULL;
|
||||
|
||||
fpi_device_get_enroll_data (device, &enroll_print);
|
||||
fpi_print_set_type (enroll_print, FP_PRINT_NBIS);
|
||||
}
|
||||
|
||||
priv->enroll_stage = 0;
|
||||
|
||||
/* The device might still be deactivating from a previous call.
|
||||
* In that situation, try to wait for a bit before reporting back an
|
||||
* error (which will usually say that the user should remove the
|
||||
* finger).
|
||||
*/
|
||||
if (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE || priv->active)
|
||||
{
|
||||
g_debug ("Got a new request while the device was still active");
|
||||
g_assert (priv->pending_activation_timeout_id == 0);
|
||||
priv->pending_activation_timeout_id =
|
||||
g_timeout_add (100, pending_activation_timeout, device);
|
||||
|
||||
if (priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
|
||||
priv->pending_activation_timeout_waiting_finger_off = TRUE;
|
||||
else
|
||||
priv->pending_activation_timeout_waiting_finger_off = FALSE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* And activate the device; we rely on fpi_image_device_activate_complete()
|
||||
* to be called when done (or immediately). */
|
||||
fp_image_device_activate (self);
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************/
|
||||
|
||||
static void
|
||||
fp_image_device_finalize (GObject *object)
|
||||
{
|
||||
FpImageDevice *self = (FpImageDevice *) object;
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
|
||||
g_assert (priv->active == FALSE);
|
||||
|
||||
G_OBJECT_CLASS (fp_image_device_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_device_default_activate (FpImageDevice *self)
|
||||
{
|
||||
fpi_image_device_activate_complete (self, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_device_default_deactivate (FpImageDevice *self)
|
||||
{
|
||||
fpi_image_device_deactivate_complete (self, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_device_class_init (FpImageDeviceClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FpDeviceClass *fp_device_class = FP_DEVICE_CLASS (klass);
|
||||
|
||||
object_class->finalize = fp_image_device_finalize;
|
||||
|
||||
fp_device_class->open = fp_image_device_open;
|
||||
fp_device_class->close = fp_image_device_close;
|
||||
fp_device_class->enroll = fp_image_device_start_capture_action;
|
||||
fp_device_class->verify = fp_image_device_start_capture_action;
|
||||
fp_device_class->identify = fp_image_device_start_capture_action;
|
||||
fp_device_class->capture = fp_image_device_start_capture_action;
|
||||
|
||||
fp_device_class->cancel = fp_image_device_cancel_action;
|
||||
|
||||
/* Default implementations */
|
||||
klass->activate = fp_image_device_default_activate;
|
||||
klass->deactivate = fp_image_device_default_deactivate;
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_device_init (FpImageDevice *self)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
|
||||
|
||||
/* Set default values. */
|
||||
fpi_device_set_nr_enroll_stages (FP_DEVICE (self), IMG_ENROLL_STAGES);
|
||||
|
||||
priv->bz3_threshold = BOZORTH3_DEFAULT_THRESHOLD;
|
||||
if (cls->bz3_threshold > 0)
|
||||
priv->bz3_threshold = cls->bz3_threshold;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
g_autoptr(FpImage) image = FP_IMAGE (source_object);
|
||||
GError *error = NULL;
|
||||
FpPrint *print = NULL;
|
||||
FpDevice *device = FP_DEVICE (user_data);
|
||||
FpImageDevicePrivate *priv;
|
||||
FpDeviceAction action;
|
||||
|
||||
/* Note: We rely on the device to not disappear during an operation. */
|
||||
|
||||
if (!fp_image_detect_minutiae_finish (image, res, &error))
|
||||
{
|
||||
/* Cancel operation . */
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
{
|
||||
fpi_device_action_error (device, g_steal_pointer (&error));
|
||||
fp_image_device_deactivate (device);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Replace error with a retry condition. */
|
||||
g_warning ("Failed to detect minutiae: %s", error->message);
|
||||
g_clear_pointer (&error, g_error_free);
|
||||
|
||||
error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_GENERAL, "Minutiae detection failed, please retry");
|
||||
}
|
||||
|
||||
priv = fp_image_device_get_instance_private (FP_IMAGE_DEVICE (device));
|
||||
action = fpi_device_get_current_action (device);
|
||||
|
||||
if (action == FP_DEVICE_ACTION_CAPTURE)
|
||||
{
|
||||
fpi_device_capture_complete (device, g_steal_pointer (&image), error);
|
||||
fp_image_device_deactivate (device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
print = fp_print_new (device);
|
||||
fpi_print_set_type (print, FP_PRINT_NBIS);
|
||||
if (!fpi_print_add_from_image (print, image, &error))
|
||||
g_clear_object (&print);
|
||||
}
|
||||
|
||||
if (action == FP_DEVICE_ACTION_ENROLL)
|
||||
{
|
||||
FpPrint *enroll_print;
|
||||
fpi_device_get_enroll_data (device, &enroll_print);
|
||||
|
||||
if (print)
|
||||
{
|
||||
fpi_print_add_print (enroll_print, print);
|
||||
priv->enroll_stage += 1;
|
||||
}
|
||||
|
||||
fpi_device_enroll_progress (device, priv->enroll_stage, print, error);
|
||||
|
||||
if (priv->enroll_stage == IMG_ENROLL_STAGES)
|
||||
{
|
||||
fpi_device_enroll_complete (device, g_object_ref (enroll_print), NULL);
|
||||
fp_image_device_deactivate (device);
|
||||
}
|
||||
}
|
||||
else if (action == FP_DEVICE_ACTION_VERIFY)
|
||||
{
|
||||
FpPrint *template;
|
||||
FpiMatchResult result;
|
||||
|
||||
fpi_device_get_verify_data (device, &template);
|
||||
if (print)
|
||||
result = fpi_print_bz3_match (template, print, priv->bz3_threshold, &error);
|
||||
else
|
||||
result = FPI_MATCH_ERROR;
|
||||
|
||||
fpi_device_verify_complete (device, result, print, error);
|
||||
fp_image_device_deactivate (device);
|
||||
}
|
||||
else if (action == FP_DEVICE_ACTION_IDENTIFY)
|
||||
{
|
||||
gint i;
|
||||
GPtrArray *templates;
|
||||
FpPrint *result = NULL;
|
||||
|
||||
fpi_device_get_identify_data (device, &templates);
|
||||
for (i = 0; !error && i < templates->len; i++)
|
||||
{
|
||||
FpPrint *template = g_ptr_array_index (templates, i);
|
||||
|
||||
if (fpi_print_bz3_match (template, print, priv->bz3_threshold, &error) == FPI_MATCH_SUCCESS)
|
||||
{
|
||||
result = g_object_ref (template);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fpi_device_identify_complete (device, result, print, error);
|
||||
fp_image_device_deactivate (device);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* XXX: This can be hit currently due to a race condition in the enroll code!
|
||||
* In that case we scan a further image even though the minutiae for the previous
|
||||
* one have not yet been detected.
|
||||
* We need to keep track on the pending minutiae detection and the fact that
|
||||
* it will finish eventually (or we may need to retry on error and activate the
|
||||
* device again). */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/* Private API */
|
||||
|
||||
/**
|
||||
* fpi_image_device_set_bz3_threshold:
|
||||
* @self: a #FpImageDevice imaging fingerprint device
|
||||
* @bz3_threshold: BZ3 threshold to use
|
||||
*
|
||||
* Dynamically adjust the bz3 threshold. This is only needed for drivers
|
||||
* that support devices with different properties. It should generally be
|
||||
* called from the probe callback, but is acceptable to call from the open
|
||||
* callback.
|
||||
*/
|
||||
void
|
||||
fpi_image_device_set_bz3_threshold (FpImageDevice *self,
|
||||
gint bz3_threshold)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (FP_IS_IMAGE_DEVICE (self));
|
||||
g_return_if_fail (bz3_threshold > 0);
|
||||
|
||||
priv->bz3_threshold = bz3_threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_image_device_report_finger_status:
|
||||
* @self: a #FpImageDevice imaging fingerprint device
|
||||
* @present: whether the finger is present on the sensor
|
||||
*
|
||||
* Reports from the driver whether the user's finger is on
|
||||
* the sensor.
|
||||
*/
|
||||
void
|
||||
fpi_image_device_report_finger_status (FpImageDevice *self,
|
||||
gboolean present)
|
||||
{
|
||||
FpDevice *device = FP_DEVICE (self);
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpDeviceAction action;
|
||||
|
||||
action = fpi_device_get_current_action (device);
|
||||
|
||||
if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE)
|
||||
{
|
||||
/* Do we really want to always ignore such reports? We could
|
||||
* also track the state in case the user had the finger on
|
||||
* the device at initialisation time and the driver reports
|
||||
* this early.
|
||||
*/
|
||||
g_debug ("Ignoring finger presence report as the device is not active!");
|
||||
return;
|
||||
}
|
||||
|
||||
action = fpi_device_get_current_action (device);
|
||||
|
||||
g_assert (action != FP_DEVICE_ACTION_OPEN);
|
||||
g_assert (action != FP_DEVICE_ACTION_CLOSE);
|
||||
|
||||
g_debug ("Image device reported finger status: %s", present ? "on" : "off");
|
||||
|
||||
if (present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
|
||||
{
|
||||
fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_CAPTURE);
|
||||
}
|
||||
else if (!present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
|
||||
{
|
||||
/* We need to deactivate or continue to await finger */
|
||||
|
||||
/* There are three possible situations:
|
||||
* 1. We are deactivating the device and the action is still in progress
|
||||
* (minutiae detection).
|
||||
* 2. We are still deactivating the device after an action completed
|
||||
* 3. We were waiting for finger removal to start the new action
|
||||
* Either way, we always end up deactivating except for the enroll case.
|
||||
* XXX: This is not quite correct though, as we assume we need another finger
|
||||
* scan even though we might be processing the last one (successfully).
|
||||
*/
|
||||
if (action != FP_DEVICE_ACTION_ENROLL)
|
||||
fp_image_device_deactivate (device);
|
||||
else
|
||||
fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_image_device_image_captured:
|
||||
* @self: a #FpImageDevice imaging fingerprint device
|
||||
* @image: whether the finger is present on the sensor
|
||||
*
|
||||
* Reports an image capture. Only use this function if the image was
|
||||
* captured successfully. If there was an issue where the user should
|
||||
* retry, use fpi_image_device_retry_scan() to report the retry condition.
|
||||
*
|
||||
* In the event of a fatal error for the operation use
|
||||
* fpi_image_device_session_error(). This will abort the entire operation
|
||||
* including e.g. an enroll operation which captures multiple images during
|
||||
* one session.
|
||||
*/
|
||||
void
|
||||
fpi_image_device_image_captured (FpImageDevice *self, FpImage *image)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpDeviceAction action;
|
||||
|
||||
action = fpi_device_get_current_action (FP_DEVICE (self));
|
||||
|
||||
g_return_if_fail (image != NULL);
|
||||
g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_CAPTURE);
|
||||
g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL ||
|
||||
action == FP_DEVICE_ACTION_VERIFY ||
|
||||
action == FP_DEVICE_ACTION_IDENTIFY ||
|
||||
action == FP_DEVICE_ACTION_CAPTURE);
|
||||
|
||||
fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF);
|
||||
|
||||
g_debug ("Image device captured an image");
|
||||
|
||||
/* XXX: We also detect minutiae in capture mode, we solely do this
|
||||
* to normalize the image which will happen as a by-product. */
|
||||
fp_image_detect_minutiae (image,
|
||||
fpi_device_get_cancellable (FP_DEVICE (self)),
|
||||
fpi_image_device_minutiae_detected,
|
||||
self);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_image_device_retry_scan:
|
||||
* @self: a #FpImageDevice imaging fingerprint device
|
||||
* @retry: The #FpDeviceRetry error code to report
|
||||
*
|
||||
* Reports a scan failure to the user. This may or may not abort the
|
||||
* current session. It is the equivalent of fpi_image_device_image_captured()
|
||||
* in the case of a retryable error condition (e.g. short swipe).
|
||||
*/
|
||||
void
|
||||
fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpDeviceAction action;
|
||||
GError *error;
|
||||
|
||||
action = fpi_device_get_current_action (FP_DEVICE (self));
|
||||
|
||||
/* We might be waiting for a finger at this point, so just accept
|
||||
* all but INACTIVE */
|
||||
g_return_if_fail (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE);
|
||||
g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL ||
|
||||
action == FP_DEVICE_ACTION_VERIFY ||
|
||||
action == FP_DEVICE_ACTION_IDENTIFY ||
|
||||
action == FP_DEVICE_ACTION_CAPTURE);
|
||||
|
||||
error = fpi_device_retry_new (retry);
|
||||
|
||||
if (action == FP_DEVICE_ACTION_ENROLL)
|
||||
{
|
||||
g_debug ("Reporting retry during enroll");
|
||||
fpi_device_enroll_progress (FP_DEVICE (self), priv->enroll_stage, NULL, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We abort the operation and let the surrounding code retry in the
|
||||
* non-enroll case (this is identical to a session error). */
|
||||
g_debug ("Abort current operation due to retry (non-enroll case)");
|
||||
fp_image_device_deactivate (FP_DEVICE (self));
|
||||
fpi_device_action_error (FP_DEVICE (self), error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_image_device_session_error:
|
||||
* @self: a #FpImageDevice imaging fingerprint device
|
||||
* @error: The #GError to report
|
||||
*
|
||||
* Report an error while interacting with the device. This effectively
|
||||
* aborts the current ongoing action.
|
||||
*/
|
||||
void
|
||||
fpi_image_device_session_error (FpImageDevice *self, GError *error)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (self);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
g_warning ("Driver did not provide an error, generating a generic one");
|
||||
error = g_error_new (FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL, "Driver reported session error without an error");
|
||||
}
|
||||
|
||||
if (!priv->active)
|
||||
{
|
||||
FpDeviceAction action = fpi_device_get_current_action (FP_DEVICE (self));
|
||||
g_warning ("Driver reported session error, but device is inactive.");
|
||||
|
||||
if (action != FP_DEVICE_ACTION_NONE)
|
||||
{
|
||||
g_warning ("Translating to activation failure!");
|
||||
fpi_image_device_activate_complete (self, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE)
|
||||
{
|
||||
g_warning ("Driver reported session error; translating to deactivation failure.");
|
||||
fpi_image_device_deactivate_complete (self, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (error->domain == FP_DEVICE_RETRY)
|
||||
g_warning ("Driver should report retries using fpi_image_device_retry_scan!");
|
||||
|
||||
fp_image_device_deactivate (FP_DEVICE (self));
|
||||
fpi_device_action_error (FP_DEVICE (self), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_image_device_activate_complete:
|
||||
* @self: a #FpImageDevice imaging fingerprint device
|
||||
* @error: A #GError or %NULL on success
|
||||
*
|
||||
* Reports completion of device activation.
|
||||
*/
|
||||
void
|
||||
fpi_image_device_activate_complete (FpImageDevice *self, GError *error)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpDeviceAction action;
|
||||
|
||||
action = fpi_device_get_current_action (FP_DEVICE (self));
|
||||
|
||||
g_return_if_fail (priv->active == FALSE);
|
||||
g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL ||
|
||||
action == FP_DEVICE_ACTION_VERIFY ||
|
||||
action == FP_DEVICE_ACTION_IDENTIFY ||
|
||||
action == FP_DEVICE_ACTION_CAPTURE);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_debug ("Image device activation failed");
|
||||
fpi_device_action_error (FP_DEVICE (self), error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("Image device activation completed");
|
||||
|
||||
priv->active = TRUE;
|
||||
|
||||
/* We always want to capture at this point, move to AWAIT_FINGER
|
||||
* state. */
|
||||
fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_image_device_deactivate_complete:
|
||||
* @self: a #FpImageDevice imaging fingerprint device
|
||||
* @error: A #GError or %NULL on success
|
||||
*
|
||||
* Reports completion of device deactivation.
|
||||
*/
|
||||
void
|
||||
fpi_image_device_deactivate_complete (FpImageDevice *self, GError *error)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
|
||||
FpDeviceAction action;
|
||||
|
||||
g_return_if_fail (priv->active == TRUE);
|
||||
g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE);
|
||||
|
||||
g_debug ("Image device deactivation completed");
|
||||
|
||||
priv->active = FALSE;
|
||||
|
||||
/* Deactivation completed. As we deactivate in the background
|
||||
* there may already be a new task pending. Check whether we
|
||||
* need to do anything. */
|
||||
action = fpi_device_get_current_action (FP_DEVICE (self));
|
||||
|
||||
/* Special case, if we should be closing, but didn't due to a running
|
||||
* deactivation, then do so now. */
|
||||
if (action == FP_DEVICE_ACTION_CLOSE)
|
||||
{
|
||||
cls->img_close (self);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We might be waiting to be able to activate again. */
|
||||
if (priv->pending_activation_timeout_id)
|
||||
fp_image_device_activate (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_image_device_open_complete:
|
||||
* @self: a #FpImageDevice imaging fingerprint device
|
||||
* @error: A #GError or %NULL on success
|
||||
*
|
||||
* Reports completion of open operation.
|
||||
*/
|
||||
void
|
||||
fpi_image_device_open_complete (FpImageDevice *self, GError *error)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpDeviceAction action;
|
||||
|
||||
action = fpi_device_get_current_action (FP_DEVICE (self));
|
||||
|
||||
g_return_if_fail (priv->active == FALSE);
|
||||
g_return_if_fail (action == FP_DEVICE_ACTION_OPEN);
|
||||
|
||||
g_debug ("Image device open completed");
|
||||
|
||||
priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE;
|
||||
|
||||
fpi_device_open_complete (FP_DEVICE (self), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_image_device_close_complete:
|
||||
* @self: a #FpImageDevice imaging fingerprint device
|
||||
* @error: A #GError or %NULL on success
|
||||
*
|
||||
* Reports completion of close operation.
|
||||
*/
|
||||
void
|
||||
fpi_image_device_close_complete (FpImageDevice *self, GError *error)
|
||||
{
|
||||
FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
|
||||
FpDeviceAction action;
|
||||
|
||||
action = fpi_device_get_current_action (FP_DEVICE (self));
|
||||
|
||||
g_debug ("Image device close completed");
|
||||
|
||||
g_return_if_fail (priv->active == FALSE);
|
||||
g_return_if_fail (action == FP_DEVICE_ACTION_CLOSE);
|
||||
|
||||
priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE;
|
||||
|
||||
fpi_device_close_complete (FP_DEVICE (self), error);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
* FpImageDevice - An image based fingerprint reader device
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -17,18 +17,13 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __FPI_DATA_H__
|
||||
#define __FPI_DATA_H__
|
||||
#pragma once
|
||||
|
||||
struct fp_print_data;
|
||||
struct fp_print_data_item {
|
||||
size_t length;
|
||||
unsigned char data[0];
|
||||
};
|
||||
#include <fp-device.h>
|
||||
|
||||
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev);
|
||||
struct fp_print_data_item *fpi_print_data_item_new(size_t length);
|
||||
struct fp_print_data_item *fpi_print_data_get_item(struct fp_print_data *data);
|
||||
void fpi_print_data_add_item(struct fp_print_data *data, struct fp_print_data_item *item);
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#endif
|
||||
#define FP_TYPE_IMAGE_DEVICE (fp_image_device_get_type ())
|
||||
G_DECLARE_DERIVABLE_TYPE (FpImageDevice, fp_image_device, FP, IMAGE_DEVICE, FpDevice)
|
||||
|
||||
G_END_DECLS
|
610
libfprint/fp-image.c
Normal file
610
libfprint/fp-image.c
Normal file
|
@ -0,0 +1,610 @@
|
|||
/*
|
||||
* FPrint Image
|
||||
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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 "fpi-image.h"
|
||||
|
||||
#include "nbis/include/lfs.h"
|
||||
|
||||
#if HAVE_PIXMAN
|
||||
#include <pixman.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* SECTION: fp-image
|
||||
* @title: FpImage
|
||||
* @short_description: Internal Image handling routines
|
||||
*
|
||||
* Some devices will provide the image data corresponding to a print
|
||||
* this object allows accessing this data.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION: fpi-image
|
||||
* @title: Internal FpImage
|
||||
* @short_description: Internal image handling routines
|
||||
*
|
||||
* Internal image handling routines. Also see the public <ulink
|
||||
* url="libfprint-FpImage.html">FpImage routines</ulink>.
|
||||
*/
|
||||
|
||||
G_DEFINE_TYPE (FpImage, fp_image, G_TYPE_OBJECT)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_WIDTH,
|
||||
PROP_HEIGHT,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS];
|
||||
|
||||
FpImage *
|
||||
fp_image_new (gint width, gint height)
|
||||
{
|
||||
return g_object_new (FP_TYPE_IMAGE,
|
||||
"width", width,
|
||||
"height", height,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_finalize (GObject *object)
|
||||
{
|
||||
FpImage *self = (FpImage *) object;
|
||||
|
||||
g_clear_pointer (&self->data, g_free);
|
||||
g_clear_pointer (&self->binarized, g_free);
|
||||
g_clear_pointer (&self->minutiae, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (fp_image_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_constructed (GObject *object)
|
||||
{
|
||||
FpImage *self = (FpImage *) object;
|
||||
|
||||
self->data = g_malloc0 (self->width * self->height);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FpImage *self = FP_IMAGE (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_WIDTH:
|
||||
g_value_set_uint (value, self->width);
|
||||
break;
|
||||
|
||||
case PROP_HEIGHT:
|
||||
g_value_set_uint (value, self->height);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FpImage *self = FP_IMAGE (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_WIDTH:
|
||||
self->width = g_value_get_uint (value);
|
||||
break;
|
||||
|
||||
case PROP_HEIGHT:
|
||||
self->height = g_value_get_uint (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_class_init (FpImageClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = fp_image_finalize;
|
||||
object_class->constructed = fp_image_constructed;
|
||||
object_class->set_property = fp_image_set_property;
|
||||
object_class->get_property = fp_image_get_property;
|
||||
|
||||
properties[PROP_WIDTH] =
|
||||
g_param_spec_uint ("width",
|
||||
"Width",
|
||||
"The width of the image",
|
||||
0,
|
||||
G_MAXUINT16,
|
||||
0,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
properties[PROP_HEIGHT] =
|
||||
g_param_spec_uint ("height",
|
||||
"Height",
|
||||
"The height of the image",
|
||||
0,
|
||||
G_MAXUINT16,
|
||||
0,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_init (FpImage *self)
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GAsyncReadyCallback user_cb;
|
||||
struct fp_minutiae *minutiae;
|
||||
gint width, height;
|
||||
gdouble ppmm;
|
||||
FpiImageFlags flags;
|
||||
guchar *image;
|
||||
guchar *binarized;
|
||||
} DetectMinutiaeData;
|
||||
|
||||
static void
|
||||
fp_image_detect_minutiae_free (DetectMinutiaeData *data)
|
||||
{
|
||||
g_clear_pointer (&data->image, g_free);
|
||||
g_clear_pointer (&data->minutiae, free_minutiae);
|
||||
g_clear_pointer (&data->binarized, g_free);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_detect_minutiae_cb (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task = G_TASK (res);
|
||||
FpImage *image;
|
||||
DetectMinutiaeData *data = g_task_get_task_data (task);
|
||||
GCancellable *cancellable;
|
||||
|
||||
cancellable = g_task_get_cancellable (task);
|
||||
if (!cancellable || !g_cancellable_is_cancelled (cancellable))
|
||||
{
|
||||
gint i;
|
||||
image = FP_IMAGE (source_object);
|
||||
|
||||
image->flags = data->flags;
|
||||
|
||||
g_clear_pointer (&image->data, g_free);
|
||||
image->data = g_steal_pointer (&data->image);
|
||||
|
||||
g_clear_pointer (&image->binarized, g_free);
|
||||
image->binarized = g_steal_pointer (&data->binarized);
|
||||
|
||||
g_clear_pointer (&image->minutiae, g_ptr_array_unref);
|
||||
image->minutiae = g_ptr_array_new_full (data->minutiae->num,
|
||||
(GDestroyNotify) free_minutia);
|
||||
|
||||
for (i = 0; i < data->minutiae->num; i++)
|
||||
g_ptr_array_add (image->minutiae,
|
||||
g_steal_pointer (&data->minutiae->list[i]));
|
||||
|
||||
/* Don't let it delete anything. */
|
||||
data->minutiae->num = 0;
|
||||
}
|
||||
|
||||
if (data->user_cb)
|
||||
data->user_cb (source_object, res, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
vflip (guint8 *data, gint width, gint height)
|
||||
{
|
||||
int data_len = width * height;
|
||||
unsigned char rowbuf[width];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < height / 2; i++)
|
||||
{
|
||||
int offset = i * width;
|
||||
int swap_offset = data_len - (width * (i + 1));
|
||||
|
||||
/* copy top row into buffer */
|
||||
memcpy (rowbuf, data + offset, width);
|
||||
|
||||
/* copy lower row over upper row */
|
||||
memcpy (data + offset, data + swap_offset, width);
|
||||
|
||||
/* copy buffer over lower row */
|
||||
memcpy (data + swap_offset, rowbuf, width);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hflip (guint8 *data, gint width, gint height)
|
||||
{
|
||||
unsigned char rowbuf[width];
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < height; i++)
|
||||
{
|
||||
int offset = i * width;
|
||||
|
||||
memcpy (rowbuf, data + offset, width);
|
||||
for (j = 0; j < width; j++)
|
||||
data[offset + j] = rowbuf[width - j - 1];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
invert_colors (guint8 *data, gint width, gint height)
|
||||
{
|
||||
int data_len = width * height;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data_len; i++)
|
||||
data[i] = 0xff - data[i];
|
||||
}
|
||||
|
||||
static void
|
||||
fp_image_detect_minutiae_thread_func (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
g_autoptr(GTimer) timer = NULL;
|
||||
DetectMinutiaeData *data = task_data;
|
||||
struct fp_minutiae *minutiae = NULL;
|
||||
g_autofree gint *direction_map = NULL;
|
||||
g_autofree gint *low_contrast_map = NULL;
|
||||
g_autofree gint *low_flow_map = NULL;
|
||||
g_autofree gint *high_curve_map = NULL;
|
||||
g_autofree gint *quality_map = NULL;
|
||||
g_autofree guchar *bdata = NULL;
|
||||
gint map_w, map_h;
|
||||
gint bw, bh, bd;
|
||||
gint r;
|
||||
|
||||
/* Normalize the image first */
|
||||
if (data->flags & FPI_IMAGE_H_FLIPPED)
|
||||
hflip (data->image, data->width, data->height);
|
||||
|
||||
if (data->flags & FPI_IMAGE_V_FLIPPED)
|
||||
vflip (data->image, data->width, data->height);
|
||||
|
||||
if (data->flags & FPI_IMAGE_COLORS_INVERTED)
|
||||
invert_colors (data->image, data->width, data->height);
|
||||
|
||||
data->flags &= ~(FPI_IMAGE_H_FLIPPED | FPI_IMAGE_V_FLIPPED | FPI_IMAGE_COLORS_INVERTED);
|
||||
|
||||
timer = g_timer_new ();
|
||||
r = get_minutiae (&minutiae, &quality_map, &direction_map,
|
||||
&low_contrast_map, &low_flow_map, &high_curve_map,
|
||||
&map_w, &map_h, &bdata, &bw, &bh, &bd,
|
||||
data->image, data->width, data->height, 8,
|
||||
data->ppmm, &g_lfsparms_V2);
|
||||
g_timer_stop (timer);
|
||||
fp_dbg ("Minutiae scan completed in %f secs", g_timer_elapsed (timer, NULL));
|
||||
|
||||
data->binarized = g_steal_pointer (&bdata);
|
||||
data->minutiae = minutiae;
|
||||
|
||||
if (r)
|
||||
{
|
||||
fp_err ("get minutiae failed, code %d", r);
|
||||
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Minutiae scan failed with code %d", r);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_image_get_height:
|
||||
* @self: A #FpImage
|
||||
*
|
||||
* Gets the pixel height of an image.
|
||||
*
|
||||
* Returns: the height of the image
|
||||
*/
|
||||
guint
|
||||
fp_image_get_height (FpImage *self)
|
||||
{
|
||||
return self->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_image_get_width:
|
||||
* @self: A #FpImage
|
||||
*
|
||||
* Gets the pixel width of an image.
|
||||
*
|
||||
* Returns: the width of the image
|
||||
*/
|
||||
guint
|
||||
fp_image_get_width (FpImage *self)
|
||||
{
|
||||
return self->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_image_get_ppmm:
|
||||
* @self: A #FpImage
|
||||
*
|
||||
* Gets the resolution of the image. Note that this is assumed to
|
||||
* be fixed to 500 points per inch (~19.685 p/mm) for most drivers.
|
||||
*
|
||||
* Returns: the resolution of the image in points per millimeter
|
||||
*/
|
||||
gdouble
|
||||
fp_image_get_ppmm (FpImage *self)
|
||||
{
|
||||
return self->ppmm;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_image_get_data:
|
||||
* @self: A #FpImage
|
||||
* @len: (out) (optional): Return location for length or %NULL
|
||||
*
|
||||
* Gets the greyscale data for an image. This data must not be modified or
|
||||
* freed.
|
||||
*
|
||||
* Returns: (transfer none) (array length=len): The image data
|
||||
*/
|
||||
const guchar *
|
||||
fp_image_get_data (FpImage *self, gsize *len)
|
||||
{
|
||||
if (len)
|
||||
*len = self->width * self->height;
|
||||
|
||||
return self->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_image_get_binarized:
|
||||
* @self: A #FpImage
|
||||
* @len: (out) (optional): Return location for length or %NULL
|
||||
*
|
||||
* Gets the binarized data for an image. This data must not be modified or
|
||||
* freed. You need to first detect the minutiae using
|
||||
* fp_image_detect_minutiae().
|
||||
*
|
||||
* Returns: (transfer none) (array length=len): The binarized image data
|
||||
*/
|
||||
const guchar *
|
||||
fp_image_get_binarized (FpImage *self, gsize *len)
|
||||
{
|
||||
if (len && self->binarized)
|
||||
*len = self->width * self->height;
|
||||
|
||||
return self->binarized;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_image_get_minutiae:
|
||||
* @self: A #FpImage
|
||||
*
|
||||
* Gets the minutiae for an image. This data must not be modified or
|
||||
* freed. You need to first detect the minutiae using
|
||||
* fp_image_detect_minutiae().
|
||||
*
|
||||
* Returns: (transfer none) (element-type FpMinutia): The detected minutiae
|
||||
*/
|
||||
GPtrArray *
|
||||
fp_image_get_minutiae (FpImage *self)
|
||||
{
|
||||
return self->minutiae;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_image_detect_minutiae:
|
||||
* @self: A #FpImage
|
||||
* @cancellable: a #GCancellable, or %NULL
|
||||
* @callback: the function to call on completion
|
||||
* @user_data: the data to pass to @callback
|
||||
*
|
||||
* Detects the minutiae found in an image.
|
||||
*/
|
||||
void
|
||||
fp_image_detect_minutiae (FpImage *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
DetectMinutiaeData *data = g_new0 (DetectMinutiaeData, 1);
|
||||
|
||||
task = g_task_new (self, cancellable, fp_image_detect_minutiae_cb, user_data);
|
||||
|
||||
data->image = g_malloc (self->width * self->height);
|
||||
memcpy (data->image, self->data, self->width * self->height);
|
||||
data->flags = self->flags;
|
||||
data->width = self->width;
|
||||
data->height = self->height;
|
||||
data->ppmm = self->ppmm;
|
||||
data->user_cb = callback;
|
||||
|
||||
g_task_set_task_data (task, data, (GDestroyNotify) fp_image_detect_minutiae_free);
|
||||
g_task_run_in_thread (task, fp_image_detect_minutiae_thread_func);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_image_detect_minutiae_finish:
|
||||
* @self: A #FpImage
|
||||
* @result: A #GAsyncResult
|
||||
* @error: Return location for errors, or %NULL to ignore
|
||||
*
|
||||
* Finish minutiae detection in an image
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*/
|
||||
gboolean
|
||||
fp_image_detect_minutiae_finish (FpImage *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* fpi_std_sq_dev:
|
||||
* @buf: buffer (usually bitmap, one byte per pixel)
|
||||
* @size: size of @buffer
|
||||
*
|
||||
* Calculates the squared standard deviation of the individual
|
||||
* pixels in the buffer, as per the following formula:
|
||||
* |[<!-- -->
|
||||
* mean = sum (buf[0..size]) / size
|
||||
* sq_dev = sum ((buf[0.size] - mean) ^ 2)
|
||||
* ]|
|
||||
* This function is usually used to determine whether image
|
||||
* is empty.
|
||||
*
|
||||
* Returns: the squared standard deviation for @buffer
|
||||
*/
|
||||
gint
|
||||
fpi_std_sq_dev (const guint8 *buf,
|
||||
gint size)
|
||||
{
|
||||
guint64 res = 0, mean = 0;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
mean += buf[i];
|
||||
|
||||
mean /= size;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
int dev = (int) buf[i] - mean;
|
||||
res += dev * dev;
|
||||
}
|
||||
|
||||
return res / size;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_mean_sq_diff_norm:
|
||||
* @buf1: buffer (usually bitmap, one byte per pixel)
|
||||
* @buf2: buffer (usually bitmap, one byte per pixel)
|
||||
* @size: buffer size of smallest buffer
|
||||
*
|
||||
* This function calculates the normalized mean square difference of
|
||||
* two buffers, usually two lines, as per the following formula:
|
||||
* |[<!-- -->
|
||||
* sq_diff = sum ((buf1[0..size] - buf2[0..size]) ^ 2) / size
|
||||
* ]|
|
||||
*
|
||||
* This functions is usually used to get numerical difference
|
||||
* between two images.
|
||||
*
|
||||
* Returns: the normalized mean squared difference between @buf1 and @buf2
|
||||
*/
|
||||
gint
|
||||
fpi_mean_sq_diff_norm (const guint8 *buf1,
|
||||
const guint8 *buf2,
|
||||
gint size)
|
||||
{
|
||||
int res = 0, i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
int dev = (int) buf1[i] - (int) buf2[i];
|
||||
res += dev * dev;
|
||||
}
|
||||
|
||||
return res / size;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_minutia_get_coords:
|
||||
* @min: A #FpMinutia
|
||||
* @x: (out): x position in image
|
||||
* @y: (out): y position in image
|
||||
*
|
||||
* Returns the coordinates of the found minutia. This is only useful for
|
||||
* debugging purposes and the API is not considered stable for production.
|
||||
*/
|
||||
void
|
||||
fp_minutia_get_coords (FpMinutia *min, gint *x, gint *y)
|
||||
{
|
||||
if (x)
|
||||
*x = min->x;
|
||||
if (y)
|
||||
*y = min->y;
|
||||
}
|
||||
|
||||
#if HAVE_PIXMAN
|
||||
FpImage *
|
||||
fpi_image_resize (FpImage *orig_img,
|
||||
guint w_factor,
|
||||
guint h_factor)
|
||||
{
|
||||
int new_width = orig_img->width * w_factor;
|
||||
int new_height = orig_img->height * h_factor;
|
||||
pixman_image_t *orig, *resized;
|
||||
pixman_transform_t transform;
|
||||
FpImage *newimg;
|
||||
|
||||
orig = pixman_image_create_bits (PIXMAN_a8, orig_img->width, orig_img->height, (uint32_t *) orig_img->data, orig_img->width);
|
||||
resized = pixman_image_create_bits (PIXMAN_a8, new_width, new_height, NULL, new_width);
|
||||
|
||||
pixman_transform_init_identity (&transform);
|
||||
pixman_transform_scale (NULL, &transform, pixman_int_to_fixed (w_factor), pixman_int_to_fixed (h_factor));
|
||||
pixman_image_set_transform (orig, &transform);
|
||||
pixman_image_set_filter (orig, PIXMAN_FILTER_BILINEAR, NULL, 0);
|
||||
pixman_image_composite32 (PIXMAN_OP_SRC,
|
||||
orig, /* src */
|
||||
NULL, /* mask */
|
||||
resized, /* dst */
|
||||
0, 0, /* src x y */
|
||||
0, 0, /* mask x y */
|
||||
0, 0, /* dst x y */
|
||||
new_width, new_height /* width height */
|
||||
);
|
||||
|
||||
newimg = fp_image_new (new_width, new_height);
|
||||
newimg->flags = orig_img->flags;
|
||||
|
||||
memcpy (newimg->data, pixman_image_get_data (resized), new_width * new_height);
|
||||
|
||||
pixman_image_unref (orig);
|
||||
pixman_image_unref (resized);
|
||||
|
||||
return newimg;
|
||||
}
|
||||
#endif
|
59
libfprint/fp-image.h
Normal file
59
libfprint/fp-image.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* FPrint Image
|
||||
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FP_TYPE_IMAGE (fp_image_get_type ())
|
||||
|
||||
typedef struct fp_minutia FpMinutia;
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FpImage, fp_image, FP, IMAGE, GObject)
|
||||
|
||||
FpImage *fp_image_new (gint width,
|
||||
gint height);
|
||||
|
||||
guint fp_image_get_width (FpImage *self);
|
||||
guint fp_image_get_height (FpImage *self);
|
||||
gdouble fp_image_get_ppmm (FpImage *self);
|
||||
|
||||
GPtrArray * fp_image_get_minutiae (FpImage *self);
|
||||
|
||||
void fp_image_detect_minutiae (FpImage *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean fp_image_detect_minutiae_finish (FpImage *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
const guchar * fp_image_get_data (FpImage *self,
|
||||
gsize *len);
|
||||
const guchar * fp_image_get_binarized (FpImage *self,
|
||||
gsize *len);
|
||||
|
||||
void fp_minutia_get_coords (FpMinutia *min,
|
||||
gint *x,
|
||||
gint *y);
|
||||
|
||||
G_END_DECLS
|
1118
libfprint/fp-print.c
Normal file
1118
libfprint/fp-print.c
Normal file
File diff suppressed because it is too large
Load diff
101
libfprint/fp-print.h
Normal file
101
libfprint/fp-print.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* FPrint Print handling
|
||||
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fp-image.h"
|
||||
#include "fp-enums.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FP_TYPE_PRINT (fp_print_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (FpPrint, fp_print, FP, PRINT, GInitiallyUnowned)
|
||||
|
||||
#include "fp-device.h"
|
||||
|
||||
/**
|
||||
* FpFinger:
|
||||
* @FP_FINGER_UNKNOWN: The finger is unknown
|
||||
* @FP_FINGER_LEFT_THUMB: Left thumb
|
||||
* @FP_FINGER_LEFT_INDEX: Left index finger
|
||||
* @FP_FINGER_LEFT_MIDDLE: Left middle finger
|
||||
* @FP_FINGER_LEFT_RING: Left ring finger
|
||||
* @FP_FINGER_LEFT_LITTLE: Left little finger
|
||||
* @FP_FINGER_RIGHT_THUMB: Right thumb
|
||||
* @FP_FINGER_RIGHT_INDEX: Right index finger
|
||||
* @FP_FINGER_RIGHT_MIDDLE: Right middle finger
|
||||
* @FP_FINGER_RIGHT_RING: Right ring finger
|
||||
* @FP_FINGER_RIGHT_LITTLE: Right little finger
|
||||
*/
|
||||
typedef enum {
|
||||
FP_FINGER_UNKNOWN = 0,
|
||||
FP_FINGER_LEFT_THUMB,
|
||||
FP_FINGER_LEFT_INDEX,
|
||||
FP_FINGER_LEFT_MIDDLE,
|
||||
FP_FINGER_LEFT_RING,
|
||||
FP_FINGER_LEFT_LITTLE,
|
||||
FP_FINGER_RIGHT_THUMB,
|
||||
FP_FINGER_RIGHT_INDEX,
|
||||
FP_FINGER_RIGHT_MIDDLE,
|
||||
FP_FINGER_RIGHT_RING,
|
||||
FP_FINGER_RIGHT_LITTLE,
|
||||
} FpFinger;
|
||||
|
||||
FpPrint *fp_print_new (FpDevice *device);
|
||||
|
||||
FpPrint *fp_print_new_from_data (guchar *data,
|
||||
gsize length);
|
||||
gboolean fp_print_to_data (guchar **data,
|
||||
gsize length);
|
||||
|
||||
const gchar *fp_print_get_driver (FpPrint *print);
|
||||
const gchar *fp_print_get_device_id (FpPrint *print);
|
||||
FpImage *fp_print_get_image (FpPrint *print);
|
||||
|
||||
FpFinger fp_print_get_finger (FpPrint *print);
|
||||
const gchar *fp_print_get_username (FpPrint *print);
|
||||
const gchar *fp_print_get_description (FpPrint *print);
|
||||
const GDate *fp_print_get_enroll_date (FpPrint *print);
|
||||
gboolean fp_print_get_device_stored (FpPrint *print);
|
||||
|
||||
void fp_print_set_finger (FpPrint *print,
|
||||
FpFinger finger);
|
||||
void fp_print_set_username (FpPrint *print,
|
||||
const gchar *username);
|
||||
void fp_print_set_description (FpPrint *print,
|
||||
const gchar *description);
|
||||
void fp_print_set_enroll_date (FpPrint *print,
|
||||
const GDate *enroll_date);
|
||||
|
||||
gboolean fp_print_compatible (FpPrint *self,
|
||||
FpDevice *device);
|
||||
gboolean fp_print_equal (FpPrint *self,
|
||||
FpPrint *other);
|
||||
|
||||
gboolean fp_print_serialize (FpPrint *print,
|
||||
guchar **data,
|
||||
gsize *length,
|
||||
GError **error);
|
||||
|
||||
FpPrint *fp_print_deserialize (const guchar *data,
|
||||
gsize length,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
|
@ -20,147 +20,10 @@
|
|||
#ifndef __FPRINT_INTERNAL_H__
|
||||
#define __FPRINT_INTERNAL_H__
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include "nbis-helpers.h"
|
||||
#include "fprint.h"
|
||||
#include "fpi-dev.h"
|
||||
#include "fpi-core.h"
|
||||
#include "fpi-log.h"
|
||||
#include "fpi-dev-img.h"
|
||||
#include "fpi-data.h"
|
||||
#include "fpi-img.h"
|
||||
#include "drivers/driver_ids.h"
|
||||
|
||||
/* Global variables */
|
||||
extern libusb_context *fpi_usb_ctx;
|
||||
extern GSList *opened_devices;
|
||||
|
||||
/* fp_print_data structure definition */
|
||||
enum fp_print_data_type {
|
||||
PRINT_DATA_RAW = 0, /* memset-imposed default */
|
||||
PRINT_DATA_NBIS_MINUTIAE
|
||||
};
|
||||
|
||||
struct fp_print_data {
|
||||
uint16_t driver_id;
|
||||
uint32_t devtype;
|
||||
enum fp_print_data_type type;
|
||||
GSList *prints;
|
||||
};
|
||||
|
||||
/* fp_dev structure definition */
|
||||
enum fp_dev_state {
|
||||
DEV_STATE_INITIAL = 0,
|
||||
DEV_STATE_ERROR,
|
||||
DEV_STATE_INITIALIZING,
|
||||
DEV_STATE_INITIALIZED,
|
||||
DEV_STATE_DEINITIALIZING,
|
||||
DEV_STATE_DEINITIALIZED,
|
||||
DEV_STATE_ENROLL_STARTING,
|
||||
DEV_STATE_ENROLLING,
|
||||
DEV_STATE_ENROLL_STOPPING,
|
||||
DEV_STATE_VERIFY_STARTING,
|
||||
DEV_STATE_VERIFYING,
|
||||
DEV_STATE_VERIFY_DONE,
|
||||
DEV_STATE_VERIFY_STOPPING,
|
||||
DEV_STATE_IDENTIFY_STARTING,
|
||||
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_dev {
|
||||
struct fp_driver *drv;
|
||||
uint32_t devtype;
|
||||
|
||||
/* only valid if drv->type == DRIVER_IMAGING */
|
||||
struct fp_img_dev *img_dev;
|
||||
/* Link to the instance specific struct */
|
||||
void *instance_data;
|
||||
|
||||
int nr_enroll_stages;
|
||||
|
||||
/* FIXME: This will eventually have a bus type */
|
||||
libusb_device_handle *udev;
|
||||
|
||||
/* read-only to drivers */
|
||||
struct fp_print_data *verify_data;
|
||||
|
||||
/* 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? */
|
||||
fp_dev_open_cb open_cb;
|
||||
void *open_cb_data;
|
||||
fp_operation_stop_cb close_cb;
|
||||
void *close_cb_data;
|
||||
fp_enroll_stage_cb enroll_stage_cb;
|
||||
void *enroll_stage_cb_data;
|
||||
fp_operation_stop_cb enroll_stop_cb;
|
||||
void *enroll_stop_cb_data;
|
||||
fp_img_operation_cb verify_cb;
|
||||
void *verify_cb_data;
|
||||
fp_operation_stop_cb verify_stop_cb;
|
||||
void *verify_stop_cb_data;
|
||||
fp_identify_cb identify_cb;
|
||||
void *identify_cb_data;
|
||||
fp_operation_stop_cb identify_stop_cb;
|
||||
void *identify_stop_cb_data;
|
||||
fp_img_operation_cb capture_cb;
|
||||
void *capture_cb_data;
|
||||
fp_operation_stop_cb capture_stop_cb;
|
||||
void *capture_stop_cb_data;
|
||||
|
||||
/* FIXME: better place to put this? */
|
||||
struct fp_print_data **identify_gallery;
|
||||
};
|
||||
|
||||
/* fp_img_dev structure definition */
|
||||
struct fp_img_dev {
|
||||
struct fp_dev *parent;
|
||||
|
||||
enum fp_imgdev_action action;
|
||||
int action_state;
|
||||
|
||||
struct fp_print_data *acquire_data;
|
||||
struct fp_print_data *enroll_data;
|
||||
struct fp_img *acquire_img;
|
||||
int enroll_stage;
|
||||
int action_result;
|
||||
|
||||
/* FIXME: better place to put this? */
|
||||
size_t identify_match_offset;
|
||||
};
|
||||
|
||||
/* fp_driver structure definition */
|
||||
|
||||
/* fp_img_driver structure definition */
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
#define fpi_driver_to_img_driver(drv) \
|
||||
container_of((drv), struct fp_img_driver, driver)
|
||||
|
||||
/* fp_dscv_dev structure definition */
|
||||
struct fp_dscv_dev {
|
||||
struct libusb_device *udev;
|
||||
struct fp_driver *drv;
|
||||
unsigned long driver_data;
|
||||
uint32_t devtype;
|
||||
};
|
||||
#include "nbis-helpers.h"
|
||||
#include "fpi-image.h"
|
||||
#include "fpi-image-device.h"
|
||||
|
||||
/* fp_minutia structure definition */
|
||||
struct fp_minutia {
|
||||
|
@ -185,48 +48,5 @@ struct fp_minutiae {
|
|||
struct fp_minutia **list;
|
||||
};
|
||||
|
||||
/* Defined in fpi-dev-img.c */
|
||||
void fpi_img_driver_setup(struct fp_img_driver *idriver);
|
||||
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
|
||||
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
|
||||
|
||||
/* Exported for use in command-line tools
|
||||
* Defined in fpi-core.c */
|
||||
struct fp_driver **fprint_get_drivers (void);
|
||||
|
||||
/* Defined in fpi-core.c */
|
||||
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
|
||||
|
||||
/* Defined in fpi-data.c */
|
||||
gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
|
||||
enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
|
||||
enum fp_print_data_type type2);
|
||||
|
||||
/* Defined in fpi-img.c */
|
||||
gboolean fpi_img_is_sane(struct fp_img *img);
|
||||
int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
|
||||
struct fp_print_data **ret);
|
||||
int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
|
||||
struct fp_print_data *new_print);
|
||||
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
|
||||
struct fp_print_data **gallery, int match_threshold, size_t *match_offset);
|
||||
|
||||
/* Defined in fpi-poll.c */
|
||||
void fpi_timeout_cancel_all_for_dev(struct fp_dev *dev);
|
||||
void fpi_poll_init(void);
|
||||
void fpi_poll_exit(void);
|
||||
|
||||
/* Defined in fpi-async.c */
|
||||
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);
|
||||
|
||||
void fpi_drvcb_identify_started(struct fp_dev *dev, int status);
|
||||
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);
|
||||
|
||||
#include "drivers_definitions.h"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -23,12 +23,8 @@
|
|||
|
||||
#include "fp_internal.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libusb.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "fpi-assembling.h"
|
||||
|
||||
/**
|
||||
|
@ -42,125 +38,141 @@
|
|||
* data in small stripes.
|
||||
*/
|
||||
|
||||
static unsigned int calc_error(struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *first_frame,
|
||||
struct fpi_frame *second_frame,
|
||||
int dx,
|
||||
int dy)
|
||||
static unsigned int
|
||||
calc_error (struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *first_frame,
|
||||
struct fpi_frame *second_frame,
|
||||
int dx,
|
||||
int dy)
|
||||
{
|
||||
unsigned int width, height;
|
||||
unsigned int x1, y1, x2, y2, err, i, j;
|
||||
unsigned int width, height;
|
||||
unsigned int x1, y1, x2, y2, err, i, j;
|
||||
|
||||
width = ctx->frame_width - (dx > 0 ? dx : -dx);
|
||||
height = ctx->frame_height - dy;
|
||||
width = ctx->frame_width - (dx > 0 ? dx : -dx);
|
||||
height = ctx->frame_height - dy;
|
||||
|
||||
y1 = 0;
|
||||
y2 = dy;
|
||||
i = 0;
|
||||
err = 0;
|
||||
do {
|
||||
x1 = dx < 0 ? 0 : dx;
|
||||
x2 = dx < 0 ? -dx : 0;
|
||||
j = 0;
|
||||
y1 = 0;
|
||||
y2 = dy;
|
||||
i = 0;
|
||||
err = 0;
|
||||
do
|
||||
{
|
||||
x1 = dx < 0 ? 0 : dx;
|
||||
x2 = dx < 0 ? -dx : 0;
|
||||
j = 0;
|
||||
|
||||
do {
|
||||
unsigned char v1, v2;
|
||||
do
|
||||
{
|
||||
unsigned char v1, v2;
|
||||
|
||||
|
||||
v1 = ctx->get_pixel(ctx, first_frame, x1, y1);
|
||||
v2 = ctx->get_pixel(ctx, second_frame, x2, y2);
|
||||
err += v1 > v2 ? v1 - v2 : v2 - v1;
|
||||
j++;
|
||||
x1++;
|
||||
x2++;
|
||||
v1 = ctx->get_pixel (ctx, first_frame, x1, y1);
|
||||
v2 = ctx->get_pixel (ctx, second_frame, x2, y2);
|
||||
err += v1 > v2 ? v1 - v2 : v2 - v1;
|
||||
j++;
|
||||
x1++;
|
||||
x2++;
|
||||
|
||||
} while (j < width);
|
||||
i++;
|
||||
y1++;
|
||||
y2++;
|
||||
} while (i < height);
|
||||
}
|
||||
while (j < width);
|
||||
i++;
|
||||
y1++;
|
||||
y2++;
|
||||
}
|
||||
while (i < height);
|
||||
|
||||
/* Normalize error */
|
||||
err *= (ctx->frame_height * ctx->frame_width);
|
||||
err /= (height * width);
|
||||
/* Normalize error */
|
||||
err *= (ctx->frame_height * ctx->frame_width);
|
||||
err /= (height * width);
|
||||
|
||||
if (err == 0)
|
||||
return INT_MAX;
|
||||
if (err == 0)
|
||||
return INT_MAX;
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This function is rather CPU-intensive. It's better to use hardware
|
||||
* to detect movement direction when possible.
|
||||
*/
|
||||
static void find_overlap(struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *first_frame,
|
||||
struct fpi_frame *second_frame,
|
||||
unsigned int *min_error)
|
||||
static void
|
||||
find_overlap (struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *first_frame,
|
||||
struct fpi_frame *second_frame,
|
||||
unsigned int *min_error)
|
||||
{
|
||||
int dx, dy;
|
||||
unsigned int err;
|
||||
*min_error = 255 * ctx->frame_height * ctx->frame_width;
|
||||
int dx, dy;
|
||||
unsigned int err;
|
||||
|
||||
/* Seeking in horizontal and vertical dimensions,
|
||||
* for horizontal dimension we'll check only 8 pixels
|
||||
* in both directions. For vertical direction diff is
|
||||
* rarely less than 2, so start with it.
|
||||
*/
|
||||
for (dy = 2; dy < ctx->frame_height; dy++) {
|
||||
for (dx = -8; dx < 8; dx++) {
|
||||
err = calc_error(ctx, first_frame, second_frame,
|
||||
dx, dy);
|
||||
if (err < *min_error) {
|
||||
*min_error = err;
|
||||
second_frame->delta_x = -dx;
|
||||
second_frame->delta_y = dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
*min_error = 255 * ctx->frame_height * ctx->frame_width;
|
||||
|
||||
/* Seeking in horizontal and vertical dimensions,
|
||||
* for horizontal dimension we'll check only 8 pixels
|
||||
* in both directions. For vertical direction diff is
|
||||
* rarely less than 2, so start with it.
|
||||
*/
|
||||
for (dy = 2; dy < ctx->frame_height; dy++)
|
||||
{
|
||||
for (dx = -8; dx < 8; dx++)
|
||||
{
|
||||
err = calc_error (ctx, first_frame, second_frame,
|
||||
dx, dy);
|
||||
if (err < *min_error)
|
||||
{
|
||||
*min_error = err;
|
||||
second_frame->delta_x = -dx;
|
||||
second_frame->delta_y = dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes,
|
||||
gboolean reverse)
|
||||
static unsigned int
|
||||
do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes,
|
||||
gboolean reverse)
|
||||
{
|
||||
GSList *list_entry = stripes;
|
||||
GTimer *timer;
|
||||
int frame = 1;
|
||||
struct fpi_frame *prev_stripe = list_entry->data;
|
||||
unsigned int min_error;
|
||||
/* Max error is width * height * 255, for AES2501 which has the largest
|
||||
* sensor its 192*16*255 = 783360. So for 32bit value it's ~5482 frame before
|
||||
* we might get int overflow. Use 64bit value here to prevent integer overflow
|
||||
*/
|
||||
unsigned long long total_error = 0;
|
||||
GSList *list_entry = stripes;
|
||||
GTimer *timer;
|
||||
int frame = 1;
|
||||
struct fpi_frame *prev_stripe = list_entry->data;
|
||||
unsigned int min_error;
|
||||
/* Max error is width * height * 255, for AES2501 which has the largest
|
||||
* sensor its 192*16*255 = 783360. So for 32bit value it's ~5482 frame before
|
||||
* we might get int overflow. Use 64bit value here to prevent integer overflow
|
||||
*/
|
||||
unsigned long long total_error = 0;
|
||||
|
||||
list_entry = g_slist_next(list_entry);
|
||||
list_entry = g_slist_next (list_entry);
|
||||
|
||||
timer = g_timer_new();
|
||||
do {
|
||||
struct fpi_frame *cur_stripe = list_entry->data;
|
||||
timer = g_timer_new ();
|
||||
do
|
||||
{
|
||||
struct fpi_frame *cur_stripe = list_entry->data;
|
||||
|
||||
if (reverse) {
|
||||
find_overlap(ctx, prev_stripe, cur_stripe, &min_error);
|
||||
cur_stripe->delta_y = -cur_stripe->delta_y;
|
||||
cur_stripe->delta_x = -cur_stripe->delta_x;
|
||||
}
|
||||
else
|
||||
find_overlap(ctx, cur_stripe, prev_stripe, &min_error);
|
||||
total_error += min_error;
|
||||
if (reverse)
|
||||
{
|
||||
find_overlap (ctx, prev_stripe, cur_stripe, &min_error);
|
||||
cur_stripe->delta_y = -cur_stripe->delta_y;
|
||||
cur_stripe->delta_x = -cur_stripe->delta_x;
|
||||
}
|
||||
else
|
||||
{
|
||||
find_overlap (ctx, cur_stripe, prev_stripe, &min_error);
|
||||
}
|
||||
total_error += min_error;
|
||||
|
||||
frame++;
|
||||
prev_stripe = cur_stripe;
|
||||
list_entry = g_slist_next(list_entry);
|
||||
frame++;
|
||||
prev_stripe = cur_stripe;
|
||||
list_entry = g_slist_next (list_entry);
|
||||
|
||||
} while (frame < num_stripes);
|
||||
}
|
||||
while (frame < num_stripes);
|
||||
|
||||
g_timer_stop(timer);
|
||||
fp_dbg("calc delta completed in %f secs", g_timer_elapsed(timer, NULL));
|
||||
g_timer_destroy(timer);
|
||||
g_timer_stop (timer);
|
||||
fp_dbg ("calc delta completed in %f secs", g_timer_elapsed (timer, NULL));
|
||||
g_timer_destroy (timer);
|
||||
|
||||
return total_error / num_stripes;
|
||||
return total_error / num_stripes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -179,77 +191,88 @@ static unsigned int do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
|
|||
* Note that @num_stripes might be shorter than the length of the list,
|
||||
* if some stripes should be skipped.
|
||||
*/
|
||||
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes)
|
||||
void
|
||||
fpi_do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes)
|
||||
{
|
||||
int err, rev_err;
|
||||
err = do_movement_estimation(ctx, stripes, num_stripes, FALSE);
|
||||
rev_err = do_movement_estimation(ctx, stripes, num_stripes, TRUE);
|
||||
fp_dbg("errors: %d rev: %d", err, rev_err);
|
||||
if (err < rev_err) {
|
||||
do_movement_estimation(ctx, stripes, num_stripes, FALSE);
|
||||
}
|
||||
int err, rev_err;
|
||||
|
||||
err = do_movement_estimation (ctx, stripes, num_stripes, FALSE);
|
||||
rev_err = do_movement_estimation (ctx, stripes, num_stripes, TRUE);
|
||||
fp_dbg ("errors: %d rev: %d", err, rev_err);
|
||||
if (err < rev_err)
|
||||
do_movement_estimation (ctx, stripes, num_stripes, FALSE);
|
||||
}
|
||||
|
||||
static inline void aes_blit_stripe(struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fp_img *img,
|
||||
struct fpi_frame *stripe,
|
||||
int x, int y)
|
||||
static inline void
|
||||
aes_blit_stripe (struct fpi_frame_asmbl_ctx *ctx,
|
||||
FpImage *img,
|
||||
struct fpi_frame *stripe,
|
||||
int x, int y)
|
||||
{
|
||||
unsigned int ix, iy;
|
||||
unsigned int fx, fy;
|
||||
unsigned int width, height;
|
||||
unsigned int ix, iy;
|
||||
unsigned int fx, fy;
|
||||
unsigned int width, height;
|
||||
|
||||
/* Find intersection */
|
||||
if (x < 0) {
|
||||
width = ctx->frame_width + x;
|
||||
ix = 0;
|
||||
fx = -x;
|
||||
} else {
|
||||
ix = x;
|
||||
fx = 0;
|
||||
width = ctx->frame_width;
|
||||
}
|
||||
if ((ix + width) > img->width)
|
||||
width = img->width - ix;
|
||||
/* Find intersection */
|
||||
if (x < 0)
|
||||
{
|
||||
width = ctx->frame_width + x;
|
||||
ix = 0;
|
||||
fx = -x;
|
||||
}
|
||||
else
|
||||
{
|
||||
ix = x;
|
||||
fx = 0;
|
||||
width = ctx->frame_width;
|
||||
}
|
||||
if ((ix + width) > img->width)
|
||||
width = img->width - ix;
|
||||
|
||||
if (y < 0) {
|
||||
iy = 0;
|
||||
fy = -y;
|
||||
height = ctx->frame_height + y;
|
||||
} else {
|
||||
iy = y;
|
||||
fy = 0;
|
||||
height = ctx->frame_height;
|
||||
}
|
||||
if (y < 0)
|
||||
{
|
||||
iy = 0;
|
||||
fy = -y;
|
||||
height = ctx->frame_height + y;
|
||||
}
|
||||
else
|
||||
{
|
||||
iy = y;
|
||||
fy = 0;
|
||||
height = ctx->frame_height;
|
||||
}
|
||||
|
||||
if (fx > ctx->frame_width)
|
||||
return;
|
||||
if (fx > ctx->frame_width)
|
||||
return;
|
||||
|
||||
if (fy > ctx->frame_height)
|
||||
return;
|
||||
if (fy > ctx->frame_height)
|
||||
return;
|
||||
|
||||
if (ix > img->width)
|
||||
return;
|
||||
if (ix > img->width)
|
||||
return;
|
||||
|
||||
if (iy > img->height)
|
||||
return;
|
||||
if (iy > img->height)
|
||||
return;
|
||||
|
||||
if ((iy + height) > img->height)
|
||||
height = img->height - iy;
|
||||
if ((iy + height) > img->height)
|
||||
height = img->height - iy;
|
||||
|
||||
for (; fy < height; fy++, iy++) {
|
||||
if (x < 0) {
|
||||
ix = 0;
|
||||
fx = -x;
|
||||
} else {
|
||||
ix = x;
|
||||
fx = 0;
|
||||
}
|
||||
for (; fx < width; fx++, ix++) {
|
||||
img->data[ix + (iy * img->width)] = ctx->get_pixel(ctx, stripe, fx, fy);
|
||||
}
|
||||
}
|
||||
for (; fy < height; fy++, iy++)
|
||||
{
|
||||
if (x < 0)
|
||||
{
|
||||
ix = 0;
|
||||
fx = -x;
|
||||
}
|
||||
else
|
||||
{
|
||||
ix = x;
|
||||
fx = 0;
|
||||
}
|
||||
for (; fx < width; fx++, ix++)
|
||||
img->data[ix + (iy * img->width)] = ctx->get_pixel (ctx, stripe, fx, fy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,130 +289,145 @@ static inline void aes_blit_stripe(struct fpi_frame_asmbl_ctx *ctx,
|
|||
*
|
||||
* Returns: a newly allocated #fp_img.
|
||||
*/
|
||||
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes)
|
||||
FpImage *
|
||||
fpi_assemble_frames (struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes)
|
||||
{
|
||||
GSList *stripe;
|
||||
struct fp_img *img;
|
||||
int height = 0;
|
||||
int i, y, x;
|
||||
gboolean reverse = FALSE;
|
||||
struct fpi_frame *fpi_frame;
|
||||
GSList *stripe;
|
||||
FpImage *img;
|
||||
int height = 0;
|
||||
int i, y, x;
|
||||
gboolean reverse = FALSE;
|
||||
struct fpi_frame *fpi_frame;
|
||||
|
||||
//FIXME g_return_if_fail
|
||||
BUG_ON(num_stripes == 0);
|
||||
BUG_ON(ctx->image_width < ctx->frame_width);
|
||||
//FIXME g_return_if_fail
|
||||
BUG_ON (num_stripes == 0);
|
||||
BUG_ON (ctx->image_width < ctx->frame_width);
|
||||
|
||||
/* Calculate height */
|
||||
i = 0;
|
||||
stripe = stripes;
|
||||
/* Calculate height */
|
||||
i = 0;
|
||||
stripe = stripes;
|
||||
|
||||
/* No offset for 1st image */
|
||||
fpi_frame = stripe->data;
|
||||
fpi_frame->delta_x = 0;
|
||||
fpi_frame->delta_y = 0;
|
||||
do {
|
||||
fpi_frame = stripe->data;
|
||||
/* No offset for 1st image */
|
||||
fpi_frame = stripe->data;
|
||||
fpi_frame->delta_x = 0;
|
||||
fpi_frame->delta_y = 0;
|
||||
do
|
||||
{
|
||||
fpi_frame = stripe->data;
|
||||
|
||||
height += fpi_frame->delta_y;
|
||||
i++;
|
||||
stripe = g_slist_next(stripe);
|
||||
} while (i < num_stripes);
|
||||
height += fpi_frame->delta_y;
|
||||
i++;
|
||||
stripe = g_slist_next (stripe);
|
||||
}
|
||||
while (i < num_stripes);
|
||||
|
||||
fp_dbg("height is %d", height);
|
||||
fp_dbg ("height is %d", height);
|
||||
|
||||
if (height < 0) {
|
||||
reverse = TRUE;
|
||||
height = -height;
|
||||
}
|
||||
if (height < 0)
|
||||
{
|
||||
reverse = TRUE;
|
||||
height = -height;
|
||||
}
|
||||
|
||||
/* For last frame */
|
||||
height += ctx->frame_height;
|
||||
/* For last frame */
|
||||
height += ctx->frame_height;
|
||||
|
||||
/* Create buffer big enough for max image */
|
||||
img = fpi_img_new(ctx->image_width * height);
|
||||
img->flags = FP_IMG_COLORS_INVERTED;
|
||||
img->flags |= reverse ? 0 : FP_IMG_H_FLIPPED | FP_IMG_V_FLIPPED;
|
||||
img->width = ctx->image_width;
|
||||
img->height = height;
|
||||
/* Create buffer big enough for max image */
|
||||
img = fp_image_new (ctx->image_width, height);
|
||||
img->flags = FPI_IMAGE_COLORS_INVERTED;
|
||||
img->flags |= reverse ? 0 : FPI_IMAGE_H_FLIPPED | FPI_IMAGE_V_FLIPPED;
|
||||
img->width = ctx->image_width;
|
||||
img->height = height;
|
||||
|
||||
/* Assemble stripes */
|
||||
i = 0;
|
||||
stripe = stripes;
|
||||
y = reverse ? (height - ctx->frame_height) : 0;
|
||||
x = (ctx->image_width - ctx->frame_width) / 2;
|
||||
/* Assemble stripes */
|
||||
i = 0;
|
||||
stripe = stripes;
|
||||
y = reverse ? (height - ctx->frame_height) : 0;
|
||||
x = (ctx->image_width - ctx->frame_width) / 2;
|
||||
|
||||
do {
|
||||
fpi_frame = stripe->data;
|
||||
do
|
||||
{
|
||||
fpi_frame = stripe->data;
|
||||
|
||||
if(reverse) {
|
||||
y += fpi_frame->delta_y;
|
||||
x += fpi_frame->delta_x;
|
||||
}
|
||||
if(reverse)
|
||||
{
|
||||
y += fpi_frame->delta_y;
|
||||
x += fpi_frame->delta_x;
|
||||
}
|
||||
|
||||
aes_blit_stripe(ctx, img, fpi_frame, x, y);
|
||||
aes_blit_stripe (ctx, img, fpi_frame, x, y);
|
||||
|
||||
if(!reverse) {
|
||||
y += fpi_frame->delta_y;
|
||||
x += fpi_frame->delta_x;
|
||||
}
|
||||
if(!reverse)
|
||||
{
|
||||
y += fpi_frame->delta_y;
|
||||
x += fpi_frame->delta_x;
|
||||
}
|
||||
|
||||
stripe = g_slist_next(stripe);
|
||||
i++;
|
||||
} while (i < num_stripes);
|
||||
stripe = g_slist_next (stripe);
|
||||
i++;
|
||||
}
|
||||
while (i < num_stripes);
|
||||
|
||||
return img;
|
||||
return img;
|
||||
}
|
||||
|
||||
static int cmpint(const void *p1, const void *p2, gpointer data)
|
||||
static int
|
||||
cmpint (const void *p1, const void *p2, gpointer data)
|
||||
{
|
||||
int a = *((int *)p1);
|
||||
int b = *((int *)p2);
|
||||
if (a < b)
|
||||
return -1;
|
||||
else if (a == b)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
int a = *((int *) p1);
|
||||
int b = *((int *) p2);
|
||||
|
||||
if (a < b)
|
||||
return -1;
|
||||
else if (a == b)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void median_filter(int *data, int size, int filtersize)
|
||||
static void
|
||||
median_filter (int *data, int size, int filtersize)
|
||||
{
|
||||
int i;
|
||||
int *result = (int *)g_malloc0(size*sizeof(int));
|
||||
int *sortbuf = (int *)g_malloc0(filtersize*sizeof(int));
|
||||
for (i = 0; i < size; i++) {
|
||||
int i1 = i - (filtersize-1)/2;
|
||||
int i2 = i + (filtersize-1)/2;
|
||||
if (i1 < 0)
|
||||
i1 = 0;
|
||||
if (i2 >= size)
|
||||
i2 = size-1;
|
||||
memmove(sortbuf, data+i1, (i2-i1+1)*sizeof(int));
|
||||
g_qsort_with_data(sortbuf, i2-i1+1, sizeof(int), cmpint, NULL);
|
||||
result[i] = sortbuf[(i2-i1+1)/2];
|
||||
}
|
||||
memmove(data, result, size*sizeof(int));
|
||||
g_free(result);
|
||||
g_free(sortbuf);
|
||||
int i;
|
||||
int *result = (int *) g_malloc0 (size * sizeof (int));
|
||||
int *sortbuf = (int *) g_malloc0 (filtersize * sizeof (int));
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
int i1 = i - (filtersize - 1) / 2;
|
||||
int i2 = i + (filtersize - 1) / 2;
|
||||
if (i1 < 0)
|
||||
i1 = 0;
|
||||
if (i2 >= size)
|
||||
i2 = size - 1;
|
||||
memmove (sortbuf, data + i1, (i2 - i1 + 1) * sizeof (int));
|
||||
g_qsort_with_data (sortbuf, i2 - i1 + 1, sizeof (int), cmpint, NULL);
|
||||
result[i] = sortbuf[(i2 - i1 + 1) / 2];
|
||||
}
|
||||
memmove (data, result, size * sizeof (int));
|
||||
g_free (result);
|
||||
g_free (sortbuf);
|
||||
}
|
||||
|
||||
static void interpolate_lines(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *line1, float y1, GSList *line2,
|
||||
float y2, unsigned char *output, float yi, int size)
|
||||
static void
|
||||
interpolate_lines (struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *line1, float y1, GSList *line2,
|
||||
float y2, unsigned char *output, float yi, int size)
|
||||
{
|
||||
int i;
|
||||
unsigned char p1, p2;
|
||||
int i;
|
||||
unsigned char p1, p2;
|
||||
|
||||
if (!line1 || !line2)
|
||||
return;
|
||||
if (!line1 || !line2)
|
||||
return;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
p1 = ctx->get_pixel(ctx, line1, i);
|
||||
p2 = ctx->get_pixel(ctx, line2, i);
|
||||
output[i] = (float)p1
|
||||
+ (yi - y1)/(y2 - y1)*(p2 - p1);
|
||||
}
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
p1 = ctx->get_pixel (ctx, line1, i);
|
||||
p2 = ctx->get_pixel (ctx, line2, i);
|
||||
output[i] = (float) p1
|
||||
+ (yi - y1) / (y2 - y1) * (p2 - p1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -406,82 +444,89 @@ static void interpolate_lines(struct fpi_line_asmbl_ctx *ctx,
|
|||
*
|
||||
* Returns: a newly allocated #fp_img.
|
||||
*/
|
||||
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *lines, size_t num_lines)
|
||||
FpImage *
|
||||
fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *lines, size_t num_lines)
|
||||
{
|
||||
/* Number of output lines per distance between two scanners */
|
||||
int i;
|
||||
GSList *row1, *row2;
|
||||
float y = 0.0;
|
||||
int line_ind = 0;
|
||||
int *offsets = (int *)g_malloc0((num_lines / 2) * sizeof(int));
|
||||
unsigned char *output = g_malloc0(ctx->line_width * ctx->max_height);
|
||||
struct fp_img *img;
|
||||
/* Number of output lines per distance between two scanners */
|
||||
int i;
|
||||
GSList *row1, *row2;
|
||||
float y = 0.0;
|
||||
int line_ind = 0;
|
||||
int *offsets = (int *) g_malloc0 ((num_lines / 2) * sizeof (int));
|
||||
unsigned char *output = g_malloc0 (ctx->line_width * ctx->max_height);
|
||||
FpImage *img;
|
||||
|
||||
g_return_val_if_fail (lines != NULL, NULL);
|
||||
g_return_val_if_fail (num_lines >= 2, NULL);
|
||||
g_return_val_if_fail (lines != NULL, NULL);
|
||||
g_return_val_if_fail (num_lines >= 2, NULL);
|
||||
|
||||
fp_dbg("%"G_GINT64_FORMAT, g_get_real_time());
|
||||
fp_dbg ("%"G_GINT64_FORMAT, g_get_real_time ());
|
||||
|
||||
row1 = lines;
|
||||
for (i = 0; (i < num_lines - 1) && row1; i += 2) {
|
||||
int bestmatch = i;
|
||||
int bestdiff = 0;
|
||||
int j, firstrow, lastrow;
|
||||
row1 = lines;
|
||||
for (i = 0; (i < num_lines - 1) && row1; i += 2)
|
||||
{
|
||||
int bestmatch = i;
|
||||
int bestdiff = 0;
|
||||
int j, firstrow, lastrow;
|
||||
|
||||
firstrow = i + 1;
|
||||
lastrow = MIN(i + ctx->max_search_offset, num_lines - 1);
|
||||
firstrow = i + 1;
|
||||
lastrow = MIN (i + ctx->max_search_offset, num_lines - 1);
|
||||
|
||||
row2 = g_slist_next(row1);
|
||||
for (j = firstrow; j <= lastrow; j++) {
|
||||
int diff = ctx->get_deviation(ctx,
|
||||
row1,
|
||||
row2);
|
||||
if ((j == firstrow) || (diff < bestdiff)) {
|
||||
bestdiff = diff;
|
||||
bestmatch = j;
|
||||
}
|
||||
row2 = g_slist_next(row2);
|
||||
}
|
||||
offsets[i / 2] = bestmatch - i;
|
||||
fp_dbg("%d", offsets[i / 2]);
|
||||
row1 = g_slist_next(row1);
|
||||
if (row1)
|
||||
row1 = g_slist_next(row1);
|
||||
}
|
||||
row2 = g_slist_next (row1);
|
||||
for (j = firstrow; j <= lastrow; j++)
|
||||
{
|
||||
int diff = ctx->get_deviation (ctx,
|
||||
row1,
|
||||
row2);
|
||||
if ((j == firstrow) || (diff < bestdiff))
|
||||
{
|
||||
bestdiff = diff;
|
||||
bestmatch = j;
|
||||
}
|
||||
row2 = g_slist_next (row2);
|
||||
}
|
||||
offsets[i / 2] = bestmatch - i;
|
||||
fp_dbg ("%d", offsets[i / 2]);
|
||||
row1 = g_slist_next (row1);
|
||||
if (row1)
|
||||
row1 = g_slist_next (row1);
|
||||
}
|
||||
|
||||
median_filter(offsets, (num_lines / 2) - 1, ctx->median_filter_size);
|
||||
median_filter (offsets, (num_lines / 2) - 1, ctx->median_filter_size);
|
||||
|
||||
fp_dbg("offsets_filtered: %"G_GINT64_FORMAT, g_get_real_time());
|
||||
for (i = 0; i <= (num_lines / 2) - 1; i++)
|
||||
fp_dbg("%d", offsets[i]);
|
||||
row1 = lines;
|
||||
for (i = 0; i < num_lines - 1; i++, row1 = g_slist_next(row1)) {
|
||||
int offset = offsets[i/2];
|
||||
if (offset > 0) {
|
||||
float ynext = y + (float)ctx->resolution / offset;
|
||||
while (line_ind < ynext) {
|
||||
if (line_ind > ctx->max_height - 1)
|
||||
goto out;
|
||||
interpolate_lines(ctx,
|
||||
row1, y,
|
||||
g_slist_next(row1),
|
||||
ynext,
|
||||
output + line_ind * ctx->line_width,
|
||||
line_ind,
|
||||
ctx->line_width);
|
||||
line_ind++;
|
||||
}
|
||||
y = ynext;
|
||||
}
|
||||
}
|
||||
fp_dbg ("offsets_filtered: %"G_GINT64_FORMAT, g_get_real_time ());
|
||||
for (i = 0; i <= (num_lines / 2) - 1; i++)
|
||||
fp_dbg ("%d", offsets[i]);
|
||||
row1 = lines;
|
||||
for (i = 0; i < num_lines - 1; i++, row1 = g_slist_next (row1))
|
||||
{
|
||||
int offset = offsets[i / 2];
|
||||
if (offset > 0)
|
||||
{
|
||||
float ynext = y + (float) ctx->resolution / offset;
|
||||
while (line_ind < ynext)
|
||||
{
|
||||
if (line_ind > ctx->max_height - 1)
|
||||
goto out;
|
||||
interpolate_lines (ctx,
|
||||
row1, y,
|
||||
g_slist_next (row1),
|
||||
ynext,
|
||||
output + line_ind * ctx->line_width,
|
||||
line_ind,
|
||||
ctx->line_width);
|
||||
line_ind++;
|
||||
}
|
||||
y = ynext;
|
||||
}
|
||||
}
|
||||
out:
|
||||
img = fpi_img_new(ctx->line_width * line_ind);
|
||||
img->height = line_ind;
|
||||
img->width = ctx->line_width;
|
||||
img->flags = FP_IMG_V_FLIPPED;
|
||||
memmove(img->data, output, ctx->line_width * line_ind);
|
||||
g_free(offsets);
|
||||
g_free(output);
|
||||
return img;
|
||||
img = fp_image_new (ctx->line_width, line_ind);
|
||||
img->height = line_ind;
|
||||
img->width = ctx->line_width;
|
||||
img->flags = FPI_IMAGE_V_FLIPPED;
|
||||
memmove (img->data, output, ctx->line_width * line_ind);
|
||||
g_free (offsets);
|
||||
g_free (output);
|
||||
return img;
|
||||
}
|
||||
|
|
|
@ -32,10 +32,11 @@
|
|||
* populate delta_x and delta_y if the device supports hardware movement
|
||||
* estimation.
|
||||
*/
|
||||
struct fpi_frame {
|
||||
int delta_x;
|
||||
int delta_y;
|
||||
unsigned char data[0];
|
||||
struct fpi_frame
|
||||
{
|
||||
int delta_x;
|
||||
int delta_y;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -52,21 +53,24 @@ struct fpi_frame {
|
|||
* hardware parameters of scanner. @image_width is usually 25% wider than
|
||||
* @frame_width to take horizontal movement into account.
|
||||
*/
|
||||
struct fpi_frame_asmbl_ctx {
|
||||
unsigned int frame_width;
|
||||
unsigned int frame_height;
|
||||
unsigned int image_width;
|
||||
unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *frame,
|
||||
unsigned int x,
|
||||
unsigned int y);
|
||||
struct fpi_frame_asmbl_ctx
|
||||
{
|
||||
unsigned int frame_width;
|
||||
unsigned int frame_height;
|
||||
unsigned int image_width;
|
||||
unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *frame,
|
||||
unsigned int x,
|
||||
unsigned int y);
|
||||
};
|
||||
|
||||
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes);
|
||||
void fpi_do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes,
|
||||
size_t num_stripes);
|
||||
|
||||
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes);
|
||||
FpImage *fpi_assemble_frames (struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes,
|
||||
size_t num_stripes);
|
||||
|
||||
/**
|
||||
* fpi_line_asmbl_ctx:
|
||||
|
@ -96,20 +100,23 @@ struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
|
|||
* returns two lines at a time, this function should be used to estimate the
|
||||
* difference between pairs of lines.
|
||||
*/
|
||||
struct fpi_line_asmbl_ctx {
|
||||
unsigned int line_width;
|
||||
unsigned int max_height;
|
||||
unsigned int resolution;
|
||||
unsigned int median_filter_size;
|
||||
unsigned int max_search_offset;
|
||||
int (*get_deviation)(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *line1, GSList *line2);
|
||||
unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *line,
|
||||
unsigned int x);
|
||||
struct fpi_line_asmbl_ctx
|
||||
{
|
||||
unsigned int line_width;
|
||||
unsigned int max_height;
|
||||
unsigned int resolution;
|
||||
unsigned int median_filter_size;
|
||||
unsigned int max_search_offset;
|
||||
int (*get_deviation)(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *line1,
|
||||
GSList *line2);
|
||||
unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *line,
|
||||
unsigned int x);
|
||||
};
|
||||
|
||||
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *lines, size_t num_lines);
|
||||
FpImage *fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *lines,
|
||||
size_t num_lines);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,706 +0,0 @@
|
|||
/*
|
||||
* Asynchronous I/O functionality
|
||||
* Copyright (C) 2008 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
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "async"
|
||||
|
||||
#include "fp_internal.h"
|
||||
#include "fpi-async.h"
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
|
||||
/*
|
||||
* SECTION:fpi-async
|
||||
* @title: Asynchronous operations reporting
|
||||
* @short_description: Asynchronous operations reporting functions
|
||||
*
|
||||
* Those functions are used by primitive drivers to report back their
|
||||
* current status. Most drivers, imaging ones, do not need to use them.
|
||||
*/
|
||||
|
||||
/* Drivers call this when device initialisation has completed */
|
||||
void fpi_drvcb_open_complete(struct fp_dev *dev, int status)
|
||||
{
|
||||
fp_dbg("status %d", status);
|
||||
BUG_ON(dev->state != DEV_STATE_INITIALIZING);
|
||||
dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_INITIALIZED;
|
||||
opened_devices = g_slist_prepend(opened_devices, dev);
|
||||
if (dev->open_cb)
|
||||
dev->open_cb(dev, status, dev->open_cb_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_dev_open:
|
||||
* @ddev: the struct #fp_dscv_dev discovered device to open
|
||||
* @callback: the callback to call when the device has been opened
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Opens and initialises a device. This is the function you call in order
|
||||
* to convert a #fp_dscv_dev discovered device into an actual device handle
|
||||
* that you can perform operations with.
|
||||
*
|
||||
* The error status of the opening will be provided as an argument to the
|
||||
* #fp_dev_open_cb callback.
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb callback,
|
||||
void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
struct fp_dev *dev;
|
||||
libusb_device_handle *udevh;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(ddev != NULL, -ENODEV);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
drv = ddev->drv;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
r = libusb_open(ddev->udev, &udevh);
|
||||
if (r < 0) {
|
||||
fp_err("usb_open failed, error %d", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
dev = g_malloc0(sizeof(*dev));
|
||||
dev->drv = drv;
|
||||
dev->udev = udevh;
|
||||
dev->__enroll_stage = -1;
|
||||
dev->state = DEV_STATE_INITIALIZING;
|
||||
dev->open_cb = callback;
|
||||
dev->open_cb_data = user_data;
|
||||
|
||||
if (!drv->open) {
|
||||
fpi_drvcb_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev->state = DEV_STATE_INITIALIZING;
|
||||
r = drv->open(dev, ddev->driver_data);
|
||||
if (r) {
|
||||
fp_err("device initialisation failed, driver=%s", drv->name);
|
||||
libusb_close(udevh);
|
||||
g_free(dev);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when device deinitialisation has completed */
|
||||
void fpi_drvcb_close_complete(struct fp_dev *dev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
|
||||
dev->state = DEV_STATE_DEINITIALIZED;
|
||||
fpi_timeout_cancel_all_for_dev(dev);
|
||||
libusb_close(dev->udev);
|
||||
if (dev->close_cb)
|
||||
dev->close_cb(dev, dev->close_cb_data);
|
||||
g_free(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_dev_close:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @callback: the callback to call when the device has been closed
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Closes a device. You must call this function when you have finished using
|
||||
* a fingerprint device.
|
||||
*/
|
||||
API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
|
||||
fp_operation_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
|
||||
g_return_if_fail (dev != NULL);
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
g_return_if_fail (drv->close != NULL);
|
||||
|
||||
if (g_slist_index(opened_devices, (gconstpointer) dev) == -1)
|
||||
fp_err("device %p not in opened list!", dev);
|
||||
opened_devices = g_slist_remove(opened_devices, (gconstpointer) dev);
|
||||
|
||||
dev->close_cb = callback;
|
||||
dev->close_cb_data = user_data;
|
||||
dev->state = DEV_STATE_DEINITIALIZING;
|
||||
drv->close(dev);
|
||||
}
|
||||
|
||||
/* Drivers call this when enrollment has started */
|
||||
void fpi_drvcb_enroll_started(struct fp_dev *dev, int status)
|
||||
{
|
||||
fp_dbg("status %d", status);
|
||||
BUG_ON(dev->state != DEV_STATE_ENROLL_STARTING);
|
||||
if (status) {
|
||||
if (status > 0) {
|
||||
status = -status;
|
||||
fp_dbg("adjusted to %d", status);
|
||||
}
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
if (dev->enroll_stage_cb)
|
||||
dev->enroll_stage_cb(dev, status, NULL, NULL,
|
||||
dev->enroll_stage_cb_data);
|
||||
} else {
|
||||
dev->state = DEV_STATE_ENROLLING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_enroll_start:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @callback: the callback to call for each stage of the enrollment
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Starts an enrollment and calls @callback for each enrollment stage.
|
||||
* See [Enrolling](libfprint-Devices-operations.html#enrolling)
|
||||
* for an explanation of enroll stages.
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_enroll_start(struct fp_dev *dev,
|
||||
fp_enroll_stage_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
if (!dev->nr_enroll_stages || !drv->enroll_start) {
|
||||
fp_err("driver %s has 0 enroll stages or no enroll func",
|
||||
drv->name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
fp_dbg("starting enrollment");
|
||||
dev->enroll_stage_cb = callback;
|
||||
dev->enroll_stage_cb_data = user_data;
|
||||
|
||||
dev->state = DEV_STATE_ENROLL_STARTING;
|
||||
r = drv->enroll_start(dev);
|
||||
if (r < 0) {
|
||||
dev->enroll_stage_cb = NULL;
|
||||
fp_err("failed to start enrollment");
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when an enroll stage has completed */
|
||||
void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
|
||||
struct fp_print_data *data, struct fp_img *img)
|
||||
{
|
||||
BUG_ON(dev->state != DEV_STATE_ENROLLING);
|
||||
fp_dbg("result %d", result);
|
||||
if (!dev->enroll_stage_cb) {
|
||||
fp_dbg("ignoring enroll result as no callback is subscribed");
|
||||
return;
|
||||
}
|
||||
if (result == FP_ENROLL_COMPLETE && !data) {
|
||||
fp_err("BUG: complete but no data?");
|
||||
result = FP_ENROLL_FAIL;
|
||||
}
|
||||
dev->enroll_stage_cb(dev, result, data, img, dev->enroll_stage_cb_data);
|
||||
}
|
||||
|
||||
/* Drivers call this when enrollment has stopped */
|
||||
void fpi_drvcb_enroll_stopped(struct fp_dev *dev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
BUG_ON(dev->state != DEV_STATE_ENROLL_STOPPING);
|
||||
dev->state = DEV_STATE_INITIALIZED;
|
||||
if (dev->enroll_stop_cb)
|
||||
dev->enroll_stop_cb(dev, dev->enroll_stop_cb_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_enroll_stop:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @callback: the callback to call when the enrollment has been cancelled
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Stops an ongoing enrollment started with fp_async_enroll_start().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_enroll_stop(struct fp_dev *dev,
|
||||
fp_operation_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
if (!drv->enroll_start)
|
||||
return -ENOTSUP;
|
||||
|
||||
dev->enroll_stage_cb = NULL;
|
||||
dev->enroll_stop_cb = callback;
|
||||
dev->enroll_stop_cb_data = user_data;
|
||||
dev->state = DEV_STATE_ENROLL_STOPPING;
|
||||
|
||||
if (!drv->enroll_stop) {
|
||||
fpi_drvcb_enroll_stopped(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = drv->enroll_stop(dev);
|
||||
if (r < 0) {
|
||||
fp_err("failed to stop enrollment");
|
||||
dev->enroll_stop_cb = NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_verify_start:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @data: the print to verify against. Must have been previously
|
||||
* enrolled with a device compatible to the device selected to perform the scan
|
||||
* @callback: the callback to call when the verification has finished
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Starts a verification and calls @callback when the verification has
|
||||
* finished. See fp_verify_finger_img() for the synchronous API. When the
|
||||
* @callback has been called, you must call fp_async_verify_stop().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
|
||||
struct fp_print_data *data, fp_img_operation_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
if (!drv->verify_start)
|
||||
return -ENOTSUP;
|
||||
|
||||
dev->state = DEV_STATE_VERIFY_STARTING;
|
||||
dev->verify_cb = callback;
|
||||
dev->verify_cb_data = user_data;
|
||||
dev->verify_data = data;
|
||||
|
||||
r = drv->verify_start(dev);
|
||||
if (r < 0) {
|
||||
dev->verify_cb = NULL;
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
fp_err("failed to start verification, error %d", r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when verification has started */
|
||||
void fpi_drvcb_verify_started(struct fp_dev *dev, int status)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
BUG_ON(dev->state != DEV_STATE_VERIFY_STARTING);
|
||||
if (status) {
|
||||
if (status > 0) {
|
||||
status = -status;
|
||||
fp_dbg("adjusted to %d", status);
|
||||
}
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
if (dev->verify_cb)
|
||||
dev->verify_cb(dev, status, NULL, dev->verify_cb_data);
|
||||
} else {
|
||||
dev->state = DEV_STATE_VERIFYING;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drivers call this to report a verify result (which might mark completion) */
|
||||
void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
|
||||
struct fp_img *img)
|
||||
{
|
||||
fp_dbg("result %d", result);
|
||||
BUG_ON(dev->state != DEV_STATE_VERIFYING);
|
||||
if (result < 0 || result == FP_VERIFY_NO_MATCH
|
||||
|| result == FP_VERIFY_MATCH)
|
||||
dev->state = DEV_STATE_VERIFY_DONE;
|
||||
|
||||
if (dev->verify_cb)
|
||||
dev->verify_cb(dev, result, img, dev->verify_cb_data);
|
||||
else
|
||||
fp_dbg("ignoring verify result as no callback is subscribed");
|
||||
}
|
||||
|
||||
/* Drivers call this when verification has stopped */
|
||||
void fpi_drvcb_verify_stopped(struct fp_dev *dev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
BUG_ON(dev->state != DEV_STATE_VERIFY_STOPPING);
|
||||
dev->state = DEV_STATE_INITIALIZED;
|
||||
if (dev->verify_stop_cb)
|
||||
dev->verify_stop_cb(dev, dev->verify_stop_cb_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_verify_stop:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @callback: the callback to call to finish a verification
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Finishes an ongoing verification started with fp_async_verify_start().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
|
||||
fp_operation_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
gboolean iterating = (dev->state == DEV_STATE_VERIFYING);
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (dev->state == DEV_STATE_VERIFY_STOPPING) {
|
||||
fp_dbg ("Already stopping verification, returning -EINPROGRESS");
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
if (dev->state == DEV_STATE_INITIALIZED) {
|
||||
if (callback)
|
||||
callback(dev, user_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
BUG_ON(dev->state != DEV_STATE_ERROR
|
||||
&& dev->state != DEV_STATE_VERIFYING
|
||||
&& dev->state != DEV_STATE_VERIFY_DONE);
|
||||
|
||||
dev->verify_cb = NULL;
|
||||
dev->verify_stop_cb = callback;
|
||||
dev->verify_stop_cb_data = user_data;
|
||||
dev->state = DEV_STATE_VERIFY_STOPPING;
|
||||
|
||||
if (!drv->verify_start)
|
||||
return -ENOTSUP;
|
||||
if (!drv->verify_stop) {
|
||||
dev->state = DEV_STATE_INITIALIZED;
|
||||
fpi_drvcb_verify_stopped(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = drv->verify_stop(dev, iterating);
|
||||
if (r < 0) {
|
||||
fp_err("failed to stop verification");
|
||||
dev->verify_stop_cb = NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_identify_start:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @gallery: NULL-terminated array of pointers to the prints to
|
||||
* identify against. Each one must have been previously enrolled with a device
|
||||
* compatible to the device selected to perform the scan
|
||||
* @callback: the callback to call when the identification has finished
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Performs a new scan and verifies it against a previously enrolled print.
|
||||
* See also: fp_verify_finger_img()
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_identify_start(struct fp_dev *dev,
|
||||
struct fp_print_data **gallery, fp_identify_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
if (!drv->identify_start)
|
||||
return -ENOTSUP;
|
||||
dev->state = DEV_STATE_IDENTIFY_STARTING;
|
||||
dev->identify_cb = callback;
|
||||
dev->identify_cb_data = user_data;
|
||||
dev->identify_gallery = gallery;
|
||||
|
||||
r = drv->identify_start(dev);
|
||||
if (r < 0) {
|
||||
fp_err("identify_start failed with error %d", r);
|
||||
dev->identify_cb = NULL;
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Driver-lib: identification has started, expect results soon */
|
||||
void fpi_drvcb_identify_started(struct fp_dev *dev, int status)
|
||||
{
|
||||
fp_dbg("status %d", status);
|
||||
BUG_ON(dev->state != DEV_STATE_IDENTIFY_STARTING);
|
||||
if (status) {
|
||||
if (status > 0) {
|
||||
status = -status;
|
||||
fp_dbg("adjusted to %d", status);
|
||||
}
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
if (dev->identify_cb)
|
||||
dev->identify_cb(dev, status, 0, NULL, dev->identify_cb_data);
|
||||
} else {
|
||||
dev->state = DEV_STATE_IDENTIFYING;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drivers report an identify result (which might mark completion) */
|
||||
void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
|
||||
size_t match_offset, struct fp_img *img)
|
||||
{
|
||||
fp_dbg("result %d", result);
|
||||
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
|
||||
&& dev->state != DEV_STATE_ERROR);
|
||||
if (result < 0 || result == FP_VERIFY_NO_MATCH
|
||||
|| result == FP_VERIFY_MATCH)
|
||||
dev->state = DEV_STATE_IDENTIFY_DONE;
|
||||
|
||||
if (dev->identify_cb)
|
||||
dev->identify_cb(dev, result, match_offset, img, dev->identify_cb_data);
|
||||
else
|
||||
fp_dbg("ignoring verify result as no callback is subscribed");
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_identify_stop:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @callback: the callback to call when the identification has stopped
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Stops an ongoing identification started with fp_async_identify_start().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
|
||||
fp_operation_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
gboolean iterating = (dev->state == DEV_STATE_IDENTIFYING);
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (dev->state == DEV_STATE_IDENTIFY_STOPPING) {
|
||||
fp_dbg ("Already stopping identification, returning -EINPROGRESS");
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
if (dev->state == DEV_STATE_INITIALIZED) {
|
||||
if (callback)
|
||||
callback(dev, user_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
|
||||
&& dev->state != DEV_STATE_IDENTIFY_DONE);
|
||||
|
||||
dev->state = DEV_STATE_IDENTIFY_STOPPING;
|
||||
dev->identify_cb = NULL;
|
||||
dev->identify_stop_cb = callback;
|
||||
dev->identify_stop_cb_data = user_data;
|
||||
|
||||
if (!drv->identify_start)
|
||||
return -ENOTSUP;
|
||||
if (!drv->identify_stop) {
|
||||
dev->state = DEV_STATE_INITIALIZED;
|
||||
fpi_drvcb_identify_stopped(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = drv->identify_stop(dev, iterating);
|
||||
if (r < 0) {
|
||||
fp_err("failed to stop identification");
|
||||
dev->identify_stop_cb = NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when identification has stopped */
|
||||
void fpi_drvcb_identify_stopped(struct fp_dev *dev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
BUG_ON(dev->state != DEV_STATE_IDENTIFY_STOPPING);
|
||||
dev->state = DEV_STATE_INITIALIZED;
|
||||
if (dev->identify_stop_cb)
|
||||
dev->identify_stop_cb(dev, dev->identify_stop_cb_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_capture_start:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @unconditional: whether to unconditionally capture an image, or to only capture when a finger is detected
|
||||
* @callback: the callback to call when the capture has finished
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Start the capture of an #fp_img from a device. When the @callback has been called,
|
||||
* you must call fp_async_capture_stop().
|
||||
*
|
||||
* Returns: 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
|
||||
*/
|
||||
API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional,
|
||||
fp_img_operation_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
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 capture, error %d", r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when capture has started */
|
||||
void fpi_drvcb_capture_started(struct fp_dev *dev, int status)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
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)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_capture_stop:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @callback: the callback to call when the capture has been stopped
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Stops an ongoing verification started with fp_async_capture_start().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
|
||||
fp_operation_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
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 capture");
|
||||
dev->capture_stop_cb = NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __FPI_ASYNC_H__
|
||||
#define __FPI_ASYNC_H__
|
||||
|
||||
#include "fpi-dev.h"
|
||||
#include "fpi-data.h"
|
||||
|
||||
void fpi_drvcb_open_complete(struct fp_dev *dev, int status);
|
||||
void fpi_drvcb_close_complete(struct fp_dev *dev);
|
||||
|
||||
void fpi_drvcb_enroll_started(struct fp_dev *dev, int status);
|
||||
void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
|
||||
struct fp_print_data *data, struct fp_img *img);
|
||||
void fpi_drvcb_enroll_stopped(struct fp_dev *dev);
|
||||
|
||||
void fpi_drvcb_verify_started(struct fp_dev *dev, int status);
|
||||
void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
|
||||
struct fp_img *img);
|
||||
void fpi_drvcb_verify_stopped(struct fp_dev *dev);
|
||||
|
||||
#endif
|
33
libfprint/fpi-context.h
Normal file
33
libfprint/fpi-context.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* FpContext - A FPrint context
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <gusb.h>
|
||||
#include "fp-context.h"
|
||||
|
||||
/**
|
||||
* fpi_get_driver_types:
|
||||
* @drivers: #GArray to be filled with all driver types
|
||||
*
|
||||
* This function is purely for private used. It is solely part of the public
|
||||
* API as it is useful during build time.
|
||||
*
|
||||
* Stability: private
|
||||
*/
|
||||
void fpi_get_driver_types (GArray *drivers);
|
|
@ -1,730 +0,0 @@
|
|||
/*
|
||||
* Core functions for libfprint
|
||||
* Copyright (C) 2007-2008 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 <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
|
||||
libusb_context *fpi_usb_ctx = NULL;
|
||||
GSList *opened_devices = NULL;
|
||||
|
||||
/**
|
||||
* SECTION:discovery
|
||||
* @title: Device discovery
|
||||
* @short_description: Device discovery functions
|
||||
*
|
||||
* 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
|
||||
* @short_description: Driver operation functions
|
||||
*
|
||||
* 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
|
||||
* @short_description: Device operation functions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:fpi-core
|
||||
* @title: Driver structures
|
||||
* @short_description: Driver structures
|
||||
*
|
||||
* Driver structures need to be defined inside each driver in
|
||||
* order for the core library to know what function to call, and the capabilities
|
||||
* of the driver and the devices it supports.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:fpi-core-img
|
||||
* @title: Image driver structures
|
||||
* @short_description: Image driver structures
|
||||
*
|
||||
* Image driver structures need to be defined inside each image driver in
|
||||
* order for the core library to know what function to call, and the capabilities
|
||||
* of the driver and the devices it supports. Its structure is based off the
|
||||
* #fp_driver struct.
|
||||
*/
|
||||
|
||||
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. Note that %NULL
|
||||
* is only returned on error. When there are no supported readers available,
|
||||
* an empty list is returned instead.
|
||||
*
|
||||
* Returns: a nul-terminated list of discovered devices or %NULL on error.
|
||||
* Must be freed with fp_dscv_devs_free() after use.
|
||||
*/
|
||||
API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void)
|
||||
{
|
||||
GPtrArray *tmparray;
|
||||
libusb_device *udev;
|
||||
libusb_device **devs;
|
||||
int r;
|
||||
int i = 0;
|
||||
|
||||
g_return_val_if_fail (registered_drivers != NULL, 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;
|
||||
}
|
||||
|
||||
tmparray = g_ptr_array_new ();
|
||||
|
||||
/* Check each device against each driver, temporarily storing successfully
|
||||
* discovered devices in a GPtrArray. */
|
||||
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);
|
||||
g_ptr_array_add (tmparray, (gpointer) ddev);
|
||||
}
|
||||
libusb_free_device_list(devs, 1);
|
||||
|
||||
/* Convert our temporary array into a standard NULL-terminated pointer
|
||||
* array. */
|
||||
g_ptr_array_add (tmparray, NULL);
|
||||
return (struct fp_dscv_dev **) g_ptr_array_free (tmparray, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <emphasis role="strong">before</emphasis> 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)
|
||||
{
|
||||
g_return_val_if_fail(dev, NULL);
|
||||
|
||||
return dev->drv;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dscv_dev_get_driver_id:
|
||||
* @dev: a discovered fingerprint device
|
||||
*
|
||||
* Returns a unique driver identifier for the underlying driver
|
||||
* for that device.
|
||||
*
|
||||
* Returns: the ID for #dev
|
||||
*/
|
||||
API_EXPORTED uint16_t fp_dscv_dev_get_driver_id(struct fp_dscv_dev *dev)
|
||||
{
|
||||
g_return_val_if_fail(dev, 0);
|
||||
|
||||
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)
|
||||
{
|
||||
g_return_val_if_fail(dev, 0);
|
||||
|
||||
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)
|
||||
{
|
||||
g_return_val_if_fail(dev, 0);
|
||||
g_return_val_if_fail(print, 0);
|
||||
|
||||
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_dev_get_driver:
|
||||
* @dev: the struct #fp_dev 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)
|
||||
{
|
||||
g_return_val_if_fail(dev, NULL);
|
||||
|
||||
return dev->drv;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_get_nr_enroll_stages:
|
||||
* @dev: the struct #fp_dev 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)
|
||||
{
|
||||
g_return_val_if_fail(dev, 0);
|
||||
|
||||
return dev->nr_enroll_stages;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_get_devtype:
|
||||
* @dev: the struct #fp_dev 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)
|
||||
{
|
||||
g_return_val_if_fail(dev, 0);
|
||||
|
||||
return dev->devtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_supports_print_data:
|
||||
* @dev: the struct #fp_dev 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)
|
||||
{
|
||||
g_return_val_if_fail(dev, 0);
|
||||
g_return_val_if_fail(data, 0);
|
||||
|
||||
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_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)
|
||||
{
|
||||
g_return_val_if_fail(drv, NULL);
|
||||
|
||||
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)
|
||||
{
|
||||
g_return_val_if_fail(drv, NULL);
|
||||
|
||||
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)
|
||||
{
|
||||
g_return_val_if_fail(drv, 0);
|
||||
|
||||
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)
|
||||
{
|
||||
g_return_val_if_fail(drv, FP_SCAN_TYPE_PRESS);
|
||||
|
||||
return drv->scan_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_driver_supports_imaging:
|
||||
* @drv: the driver
|
||||
*
|
||||
* Determines if a driver has imaging capabilities. If a driver has imaging
|
||||
* capabilities you are able to perform imaging operations such as retrieving
|
||||
* scan images using fp_dev_img_capture(). However, not all drivers support
|
||||
* 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_driver_supports_imaging(struct fp_driver *drv)
|
||||
{
|
||||
g_return_val_if_fail(drv, 0);
|
||||
|
||||
return drv->capture_start != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_supports_imaging:
|
||||
* @dev: the struct #fp_dev 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)
|
||||
{
|
||||
g_return_val_if_fail(dev, 0);
|
||||
|
||||
return dev->drv->capture_start != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_supports_identification:
|
||||
* @dev: the struct #fp_dev 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)
|
||||
{
|
||||
g_return_val_if_fail(dev, 0);
|
||||
|
||||
return dev->drv->identify_start != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_get_img_width:
|
||||
* @dev: the struct #fp_dev 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)
|
||||
{
|
||||
g_return_val_if_fail(dev, -1);
|
||||
|
||||
if (!dev->img_dev) {
|
||||
fp_dbg("get image width for non-imaging device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fpi_imgdev_get_img_width(dev->img_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_get_img_height:
|
||||
* @dev: the struct #fp_dev 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)
|
||||
{
|
||||
g_return_val_if_fail(dev, -1);
|
||||
|
||||
if (!dev->img_dev) {
|
||||
fp_dbg("get image height for non-imaging device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fpi_imgdev_get_img_height(dev->img_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_poll_exit();
|
||||
g_slist_free(registered_drivers);
|
||||
registered_drivers = NULL;
|
||||
libusb_exit(fpi_usb_ctx);
|
||||
}
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __FPI_CORE_H__
|
||||
#define __FPI_CORE_H__
|
||||
|
||||
#include <fprint.h>
|
||||
#include "fpi-dev-img.h"
|
||||
|
||||
/**
|
||||
* usb_id:
|
||||
* @vendor: the USB vendor ID
|
||||
* @product: the USB product ID
|
||||
* @driver_data: data to differentiate devices of different
|
||||
* vendor and product IDs.
|
||||
*
|
||||
* The struct #usb_id is used to declare devices supported by a
|
||||
* particular driver. The @driver_data information is used to
|
||||
* differentiate different models of devices which only need
|
||||
* small changes compared to the default driver behaviour to function.
|
||||
*
|
||||
* For example, a device might have a different initialisation from
|
||||
* the stock device, so the driver could do:
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* if (driver_data == MY_DIFFERENT_DEVICE_QUIRK) {
|
||||
* ...
|
||||
* } else {
|
||||
* ...
|
||||
* }
|
||||
* ]|
|
||||
*
|
||||
* The default value is zero, so the @driver_data needs to be a
|
||||
* non-zero to be useful.
|
||||
*/
|
||||
struct usb_id {
|
||||
uint16_t vendor;
|
||||
uint16_t product;
|
||||
unsigned long driver_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* fp_driver_type:
|
||||
* @DRIVER_PRIMITIVE: primitive, non-imaging, driver
|
||||
* @DRIVER_IMAGING: imaging driver
|
||||
*
|
||||
* The type of device the driver supports.
|
||||
*/
|
||||
enum fp_driver_type {
|
||||
DRIVER_PRIMITIVE = 0,
|
||||
DRIVER_IMAGING = 1,
|
||||
};
|
||||
|
||||
struct fp_driver {
|
||||
const uint16_t id;
|
||||
const char *name;
|
||||
const char *full_name;
|
||||
const struct usb_id * const id_table;
|
||||
enum fp_driver_type type;
|
||||
enum fp_scan_type scan_type;
|
||||
|
||||
/* Device operations */
|
||||
int (*discover)(struct libusb_device_descriptor *dsc, uint32_t *devtype);
|
||||
int (*open)(struct fp_dev *dev, unsigned long driver_data);
|
||||
void (*close)(struct fp_dev *dev);
|
||||
int (*enroll_start)(struct fp_dev *dev);
|
||||
int (*enroll_stop)(struct fp_dev *dev);
|
||||
int (*verify_start)(struct fp_dev *dev);
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
* FpiImgDriverFlags:
|
||||
* @FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE: Whether the driver supports
|
||||
* unconditional image capture. No driver currently does.
|
||||
*
|
||||
* Flags used in the #fp_img_driver to advertise the capabilities of drivers.
|
||||
*/
|
||||
typedef enum {
|
||||
FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE = 1 << 0
|
||||
} FpiImgDriverFlags;
|
||||
|
||||
struct fp_img_driver {
|
||||
struct fp_driver driver;
|
||||
FpiImgDriverFlags flags;
|
||||
int img_width;
|
||||
int img_height;
|
||||
int bz3_threshold;
|
||||
|
||||
/* Device operations */
|
||||
int (*open)(struct fp_img_dev *dev, unsigned long driver_data);
|
||||
void (*close)(struct fp_img_dev *dev);
|
||||
int (*activate)(struct fp_img_dev *dev);
|
||||
int (*change_state)(struct fp_img_dev *dev, enum fp_imgdev_state state);
|
||||
void (*deactivate)(struct fp_img_dev *dev);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,340 +0,0 @@
|
|||
/*
|
||||
* Fingerprint data handling and storage
|
||||
* 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 <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
|
||||
#define DIR_PERMS 0700
|
||||
|
||||
struct fpi_print_data_fp2 {
|
||||
char prefix[3];
|
||||
uint16_t driver_id;
|
||||
uint32_t devtype;
|
||||
unsigned char data_type;
|
||||
unsigned char data[0];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct fpi_print_data_item_fp2 {
|
||||
uint32_t length;
|
||||
unsigned char data[0];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* SECTION: print_data
|
||||
* @title: Stored prints
|
||||
* @short_description: Stored prints functions
|
||||
*
|
||||
* Stored prints are represented by a structure named #fp_print_data.
|
||||
* Stored prints are originally obtained from an enrollment function such as
|
||||
* fp_enroll_finger().
|
||||
*
|
||||
* This page documents the various operations you can do with a stored print.
|
||||
* Note that by default, "stored prints" are not actually stored anywhere
|
||||
* except in RAM. Storage needs to be handled by the API user by using the
|
||||
* fp_print_data_get_data() and fp_print_data_from_data(). This API allows
|
||||
* to convert print data into byte strings, and to reconstruct stored prints
|
||||
* from such data at a later point. You are welcome to store these byte strings
|
||||
* in any fashion that suits you.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SECTION: fpi-data
|
||||
* @title: Stored prints creation
|
||||
* @short_description: Stored prints creation functions
|
||||
*
|
||||
* Stored print can be loaded and created by certain drivers which do their own
|
||||
* print matching in hardware. Most drivers will not be using those functions.
|
||||
* See #fp_print_data for the public API counterpart.
|
||||
*/
|
||||
|
||||
#define FP_FINGER_IS_VALID(finger) \
|
||||
((finger) >= LEFT_THUMB && (finger) <= RIGHT_LITTLE)
|
||||
|
||||
static struct fp_print_data *print_data_new(uint16_t driver_id,
|
||||
uint32_t devtype, enum fp_print_data_type type)
|
||||
{
|
||||
struct fp_print_data *data = g_malloc0(sizeof(*data));
|
||||
fp_dbg("driver=%02x devtype=%04x", driver_id, devtype);
|
||||
data->driver_id = driver_id;
|
||||
data->devtype = devtype;
|
||||
data->type = type;
|
||||
return data;
|
||||
}
|
||||
|
||||
static void fpi_print_data_item_free(struct fp_print_data_item *item)
|
||||
{
|
||||
g_free(item);
|
||||
}
|
||||
|
||||
struct fp_print_data_item *fpi_print_data_item_new(size_t length)
|
||||
{
|
||||
struct fp_print_data_item *item = g_malloc0(sizeof(*item) + length);
|
||||
item->length = length;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev)
|
||||
{
|
||||
return print_data_new(dev->drv->id, dev->devtype,
|
||||
fpi_driver_get_data_type(dev->drv));
|
||||
}
|
||||
|
||||
struct fp_print_data_item *
|
||||
fpi_print_data_get_item(struct fp_print_data *data)
|
||||
{
|
||||
return data->prints->data;
|
||||
}
|
||||
|
||||
void
|
||||
fpi_print_data_add_item(struct fp_print_data *data,
|
||||
struct fp_print_data_item *item)
|
||||
{
|
||||
data->prints = g_slist_prepend(data->prints, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_print_data_get_data:
|
||||
* @data: the stored print
|
||||
* @ret: output location for the data buffer. Must be freed with free()
|
||||
* after use.
|
||||
|
||||
* Convert a stored print into a unified representation inside a data buffer.
|
||||
* You can then store this data buffer in any way that suits you, and load
|
||||
* it back at some later time using fp_print_data_from_data().
|
||||
*
|
||||
* Returns: the size of the freshly allocated buffer, or 0 on error.
|
||||
*/
|
||||
API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
|
||||
unsigned char **ret)
|
||||
{
|
||||
struct fpi_print_data_fp2 *out_data;
|
||||
struct fpi_print_data_item_fp2 *out_item;
|
||||
struct fp_print_data_item *item;
|
||||
size_t buflen = 0;
|
||||
GSList *list_item;
|
||||
unsigned char *buf;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
list_item = data->prints;
|
||||
while (list_item) {
|
||||
item = list_item->data;
|
||||
buflen += sizeof(*out_item);
|
||||
buflen += item->length;
|
||||
list_item = g_slist_next(list_item);
|
||||
}
|
||||
|
||||
buflen += sizeof(*out_data);
|
||||
out_data = g_malloc(buflen);
|
||||
|
||||
*ret = (unsigned char *) out_data;
|
||||
buf = out_data->data;
|
||||
out_data->prefix[0] = 'F';
|
||||
out_data->prefix[1] = 'P';
|
||||
out_data->prefix[2] = '2';
|
||||
out_data->driver_id = GUINT16_TO_LE(data->driver_id);
|
||||
out_data->devtype = GUINT32_TO_LE(data->devtype);
|
||||
out_data->data_type = data->type;
|
||||
|
||||
list_item = data->prints;
|
||||
while (list_item) {
|
||||
item = list_item->data;
|
||||
out_item = (struct fpi_print_data_item_fp2 *)buf;
|
||||
out_item->length = GUINT32_TO_LE(item->length);
|
||||
/* FIXME: fp_print_data_item->data content is not endianness agnostic */
|
||||
memcpy(out_item->data, item->data, item->length);
|
||||
buf += sizeof(*out_item);
|
||||
buf += item->length;
|
||||
list_item = g_slist_next(list_item);
|
||||
}
|
||||
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static struct fp_print_data *fpi_print_data_from_fp1_data(unsigned char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
size_t print_data_len;
|
||||
struct fp_print_data *data;
|
||||
struct fp_print_data_item *item;
|
||||
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
|
||||
|
||||
print_data_len = buflen - sizeof(*raw);
|
||||
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
|
||||
GUINT32_FROM_LE(raw->devtype), raw->data_type);
|
||||
item = fpi_print_data_item_new(print_data_len);
|
||||
/* FIXME: fp_print_data->data content is not endianness agnostic */
|
||||
memcpy(item->data, raw->data, print_data_len);
|
||||
data->prints = g_slist_prepend(data->prints, item);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static struct fp_print_data *fpi_print_data_from_fp2_data(unsigned char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
size_t total_data_len, item_len;
|
||||
struct fp_print_data *data;
|
||||
struct fp_print_data_item *item;
|
||||
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
|
||||
unsigned char *raw_buf;
|
||||
struct fpi_print_data_item_fp2 *raw_item;
|
||||
|
||||
total_data_len = buflen - sizeof(*raw);
|
||||
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
|
||||
GUINT32_FROM_LE(raw->devtype), raw->data_type);
|
||||
raw_buf = raw->data;
|
||||
while (total_data_len) {
|
||||
if (total_data_len < sizeof(*raw_item))
|
||||
break;
|
||||
total_data_len -= sizeof(*raw_item);
|
||||
|
||||
raw_item = (struct fpi_print_data_item_fp2 *)raw_buf;
|
||||
item_len = GUINT32_FROM_LE(raw_item->length);
|
||||
fp_dbg("item len %d, total_data_len %d", (int) item_len, (int) total_data_len);
|
||||
if (total_data_len < item_len) {
|
||||
fp_err("corrupted fingerprint data");
|
||||
break;
|
||||
}
|
||||
total_data_len -= item_len;
|
||||
|
||||
item = fpi_print_data_item_new(item_len);
|
||||
/* FIXME: fp_print_data->data content is not endianness agnostic */
|
||||
memcpy(item->data, raw_item->data, item_len);
|
||||
data->prints = g_slist_prepend(data->prints, item);
|
||||
|
||||
raw_buf += sizeof(*raw_item);
|
||||
raw_buf += item_len;
|
||||
}
|
||||
|
||||
if (g_slist_length(data->prints) == 0) {
|
||||
fp_print_data_free(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_print_data_from_data:
|
||||
* @buf: the data buffer
|
||||
* @buflen: the length of the buffer
|
||||
|
||||
* Load a stored print from a data buffer. The contents of said buffer must
|
||||
* be the untouched contents of a buffer previously supplied to you by the
|
||||
* fp_print_data_get_data() function.
|
||||
*
|
||||
* Returns: the stored print represented by the data, or %NULL on error. Must
|
||||
* be freed with fp_print_data_free() after use.
|
||||
*/
|
||||
API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
|
||||
|
||||
fp_dbg("buffer size %zd", buflen);
|
||||
if (buflen < sizeof(*raw))
|
||||
return NULL;
|
||||
|
||||
if (strncmp(raw->prefix, "FP1", 3) == 0) {
|
||||
return fpi_print_data_from_fp1_data(buf, buflen);
|
||||
} else if (strncmp(raw->prefix, "FP2", 3) == 0) {
|
||||
return fpi_print_data_from_fp2_data(buf, buflen);
|
||||
} else {
|
||||
fp_dbg("bad header prefix");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
|
||||
enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
|
||||
enum fp_print_data_type type2)
|
||||
{
|
||||
if (driver_id1 != driver_id2) {
|
||||
fp_dbg("driver ID mismatch: %02x vs %02x", driver_id1, driver_id2);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (devtype1 != devtype2) {
|
||||
fp_dbg("devtype mismatch: %04x vs %04x", devtype1, devtype2);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (type1 != type2) {
|
||||
fp_dbg("type mismatch: %d vs %d", type1, type2);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_print_data_free:
|
||||
* @data: the stored print to destroy. If NULL, function simply returns.
|
||||
*
|
||||
* Frees a stored print. Must be called when you are finished using the print.
|
||||
*/
|
||||
API_EXPORTED void fp_print_data_free(struct fp_print_data *data)
|
||||
{
|
||||
if (data)
|
||||
g_slist_free_full(data->prints, (GDestroyNotify)fpi_print_data_item_free);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_print_data_get_driver_id:
|
||||
* @data: the stored print
|
||||
|
||||
* Gets the [driver ID](advanced-topics.html#driver_id) for a stored print. The driver ID
|
||||
* indicates which driver the print originally came from. The print is
|
||||
* only usable with a device controlled by that driver.
|
||||
*
|
||||
* Returns: the driver ID of the driver compatible with the print
|
||||
*/
|
||||
API_EXPORTED uint16_t fp_print_data_get_driver_id(struct fp_print_data *data)
|
||||
{
|
||||
return data->driver_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_print_data_get_devtype:
|
||||
* @data: the stored print
|
||||
|
||||
* Gets the [devtype](advanced-topics.html#device-types) for a stored print. The devtype represents
|
||||
* which type of device under the parent driver is compatible with the print.
|
||||
*
|
||||
* Returns: the devtype of the device range compatible with the print
|
||||
*/
|
||||
API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
|
||||
{
|
||||
return data->devtype;
|
||||
}
|
|
@ -1,660 +0,0 @@
|
|||
/*
|
||||
* Core imaging device functions for libfprint
|
||||
* Copyright (C) 2007-2008 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 <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "fpi-dev-img.h"
|
||||
#include "fpi-async.h"
|
||||
#include "fp_internal.h"
|
||||
|
||||
/**
|
||||
* SECTION:fpi-dev-img
|
||||
* @title: Image device operations
|
||||
* @short_description: Image device operation functions
|
||||
*
|
||||
* As drivers work through different operations, they need to report back
|
||||
* to the core as to their internal state, so errors and successes can be
|
||||
* reported back to front-ends.
|
||||
*/
|
||||
|
||||
#define MIN_ACCEPTABLE_MINUTIAE 10
|
||||
#define BOZORTH3_DEFAULT_THRESHOLD 40
|
||||
#define IMG_ENROLL_STAGES 5
|
||||
|
||||
/**
|
||||
* fpi_imgdev_get_action_state:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
*
|
||||
* Returns the state of an imaging device while enrolling a fingerprint.
|
||||
*
|
||||
* Returns: a enum #fp_imgdev_enroll_state
|
||||
*/
|
||||
enum fp_imgdev_enroll_state
|
||||
fpi_imgdev_get_action_state(struct fp_img_dev *imgdev)
|
||||
{
|
||||
return imgdev->action_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_get_action:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
*
|
||||
* Returns the current action being performed by an imaging device.
|
||||
*
|
||||
* Returns: a enum #fp_imgdev_action
|
||||
*/
|
||||
enum fp_imgdev_action
|
||||
fpi_imgdev_get_action(struct fp_img_dev *imgdev)
|
||||
{
|
||||
return imgdev->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_get_action_result:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
*
|
||||
* Returns an integer representing the result of an action. Which enum
|
||||
* the result code is taken from depends on the current action being performed.
|
||||
* See #fp_capture_result, #fp_enroll_result and #fp_verify_result.
|
||||
*/
|
||||
int
|
||||
fpi_imgdev_get_action_result(struct fp_img_dev *imgdev)
|
||||
{
|
||||
return imgdev->action_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_set_action_result:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
* @action_result: an action result
|
||||
*
|
||||
* Drivers should use fpi_imgdev_image_captured() instead. This function
|
||||
* should not be used, and will be removed soon.
|
||||
*/
|
||||
void
|
||||
fpi_imgdev_set_action_result(struct fp_img_dev *imgdev,
|
||||
int action_result)
|
||||
{
|
||||
imgdev->action_result = action_result;
|
||||
}
|
||||
|
||||
static int img_dev_open(struct fp_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
struct fp_img_dev *imgdev = g_malloc0(sizeof(*imgdev));
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(dev->drv);
|
||||
int r = 0;
|
||||
|
||||
/* Set up back pointers */
|
||||
dev->img_dev = imgdev;
|
||||
imgdev->parent = dev;
|
||||
|
||||
imgdev->enroll_stage = 0;
|
||||
dev->nr_enroll_stages = IMG_ENROLL_STAGES;
|
||||
|
||||
if (imgdrv->open) {
|
||||
r = imgdrv->open(imgdev, driver_data);
|
||||
if (r)
|
||||
goto err;
|
||||
} else {
|
||||
fpi_drvcb_open_complete(dev, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
g_free(imgdev);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_open_complete:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
* @status: an error code
|
||||
*
|
||||
* Function to call when the device has been opened, whether
|
||||
* successfully of not.
|
||||
*/
|
||||
void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status)
|
||||
{
|
||||
fpi_drvcb_open_complete(FP_DEV(imgdev), status);
|
||||
}
|
||||
|
||||
static void img_dev_close(struct fp_dev *dev)
|
||||
{
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(dev->drv);
|
||||
|
||||
if (imgdrv->close)
|
||||
imgdrv->close(dev->img_dev);
|
||||
else
|
||||
fpi_drvcb_close_complete(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_close_complete:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
*
|
||||
* Function to call when the device has been closed.
|
||||
*/
|
||||
void fpi_imgdev_close_complete(struct fp_img_dev *imgdev)
|
||||
{
|
||||
fpi_drvcb_close_complete(FP_DEV(imgdev));
|
||||
g_free(imgdev);
|
||||
}
|
||||
|
||||
static int dev_change_state(struct fp_img_dev *imgdev,
|
||||
enum fp_imgdev_state state)
|
||||
{
|
||||
struct fp_driver *drv = FP_DEV(imgdev)->drv;
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
|
||||
|
||||
if (!imgdrv->change_state)
|
||||
return 0;
|
||||
return imgdrv->change_state(imgdev, state);
|
||||
}
|
||||
|
||||
/* check image properties and resize it if necessary. potentially returns a new
|
||||
* image after freeing the old one. */
|
||||
static int sanitize_image(struct fp_img_dev *imgdev, struct fp_img **_img)
|
||||
{
|
||||
struct fp_driver *drv = FP_DEV(imgdev)->drv;
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
|
||||
struct fp_img *img = *_img;
|
||||
|
||||
if (imgdrv->img_width > 0) {
|
||||
img->width = imgdrv->img_width;
|
||||
} else if (img->width <= 0) {
|
||||
fp_err("no image width assigned");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (imgdrv->img_height > 0) {
|
||||
img->height = imgdrv->img_height;
|
||||
} else if (img->height <= 0) {
|
||||
fp_err("no image height assigned");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!fpi_img_is_sane(img)) {
|
||||
fp_err("image is not sane!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_report_finger_status:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
* @present: whether the finger is present on the sensor
|
||||
*
|
||||
* Reports from the driver whether the user's finger is on
|
||||
* the sensor.
|
||||
*/
|
||||
void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
|
||||
gboolean present)
|
||||
{
|
||||
int r = imgdev->action_result;
|
||||
struct fp_print_data *data = imgdev->acquire_data;
|
||||
struct fp_img *img = imgdev->acquire_img;
|
||||
|
||||
fp_dbg(present ? "finger on sensor" : "finger removed");
|
||||
|
||||
if (present && imgdev->action_state == IMG_ACQUIRE_STATE_AWAIT_FINGER_ON) {
|
||||
dev_change_state(imgdev, IMGDEV_STATE_CAPTURE);
|
||||
imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_IMAGE;
|
||||
return;
|
||||
} else if (present
|
||||
|| imgdev->action_state != IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF) {
|
||||
fp_dbg("ignoring status report");
|
||||
return;
|
||||
}
|
||||
|
||||
/* clear these before reporting results to avoid complications with
|
||||
* call cascading in and out of the library */
|
||||
imgdev->acquire_img = NULL;
|
||||
imgdev->acquire_data = NULL;
|
||||
|
||||
/* finger removed, report results */
|
||||
switch (imgdev->action) {
|
||||
case IMG_ACTION_ENROLL:
|
||||
fp_dbg("reporting enroll result");
|
||||
data = imgdev->enroll_data;
|
||||
if (r == FP_ENROLL_COMPLETE) {
|
||||
imgdev->enroll_data = NULL;
|
||||
}
|
||||
fpi_drvcb_enroll_stage_completed(FP_DEV(imgdev), r,
|
||||
r == FP_ENROLL_COMPLETE ? data : NULL,
|
||||
img);
|
||||
/* the callback can cancel enrollment, so recheck current
|
||||
* action and the status to see if retry is needed */
|
||||
if (imgdev->action == IMG_ACTION_ENROLL &&
|
||||
r > 0 && r != FP_ENROLL_COMPLETE && r != FP_ENROLL_FAIL) {
|
||||
imgdev->action_result = 0;
|
||||
imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_ON;
|
||||
dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
|
||||
}
|
||||
break;
|
||||
case IMG_ACTION_VERIFY:
|
||||
fpi_drvcb_report_verify_result(FP_DEV(imgdev), r, img);
|
||||
imgdev->action_result = 0;
|
||||
fp_print_data_free(data);
|
||||
break;
|
||||
case IMG_ACTION_IDENTIFY:
|
||||
fpi_drvcb_report_identify_result(FP_DEV(imgdev), r,
|
||||
imgdev->identify_match_offset, img);
|
||||
imgdev->action_result = 0;
|
||||
fp_print_data_free(data);
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
fpi_drvcb_report_capture_result(FP_DEV(imgdev), r, img);
|
||||
imgdev->action_result = 0;
|
||||
break;
|
||||
default:
|
||||
fp_err("unhandled action %d", imgdev->action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void verify_process_img(struct fp_img_dev *imgdev)
|
||||
{
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(FP_DEV(imgdev)->drv);
|
||||
int match_score = imgdrv->bz3_threshold;
|
||||
int r;
|
||||
|
||||
if (match_score == 0)
|
||||
match_score = BOZORTH3_DEFAULT_THRESHOLD;
|
||||
|
||||
r = fpi_img_compare_print_data(FP_DEV(imgdev)->verify_data,
|
||||
imgdev->acquire_data);
|
||||
|
||||
if (r >= match_score)
|
||||
r = FP_VERIFY_MATCH;
|
||||
else if (r >= 0)
|
||||
r = FP_VERIFY_NO_MATCH;
|
||||
|
||||
imgdev->action_result = r;
|
||||
}
|
||||
|
||||
static void identify_process_img(struct fp_img_dev *imgdev)
|
||||
{
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(FP_DEV(imgdev)->drv);
|
||||
int match_score = imgdrv->bz3_threshold;
|
||||
size_t match_offset;
|
||||
int r;
|
||||
|
||||
if (match_score == 0)
|
||||
match_score = BOZORTH3_DEFAULT_THRESHOLD;
|
||||
|
||||
r = fpi_img_compare_print_data_to_gallery(imgdev->acquire_data,
|
||||
FP_DEV(imgdev)->identify_gallery, match_score, &match_offset);
|
||||
|
||||
imgdev->action_result = r;
|
||||
imgdev->identify_match_offset = match_offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_abort_scan:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
* @result: the scan result
|
||||
*
|
||||
* Aborts a scan after an error, and set the action result. See
|
||||
* fpi_imgdev_get_action_result() for possible values.
|
||||
*/
|
||||
void fpi_imgdev_abort_scan(struct fp_img_dev *imgdev, int result)
|
||||
{
|
||||
imgdev->action_result = result;
|
||||
imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF;
|
||||
dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_OFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_image_captured:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
* @img: an #fp_img image
|
||||
*
|
||||
* Report to the core that the driver captured this image from the sensor.
|
||||
*/
|
||||
void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
|
||||
{
|
||||
struct fp_print_data *print = NULL;
|
||||
int r;
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (imgdev->action_state != IMG_ACQUIRE_STATE_AWAIT_IMAGE) {
|
||||
fp_dbg("ignoring due to current state %d", imgdev->action_state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (imgdev->action_result) {
|
||||
fp_dbg("not overwriting existing action result");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sanitize_image(imgdev, &img);
|
||||
if (r < 0) {
|
||||
imgdev->action_result = r;
|
||||
fp_img_free(img);
|
||||
goto next_state;
|
||||
}
|
||||
|
||||
fp_img_standardize(img);
|
||||
imgdev->acquire_img = img;
|
||||
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;
|
||||
switch (imgdev->action) {
|
||||
case IMG_ACTION_ENROLL:
|
||||
if (!imgdev->enroll_data) {
|
||||
imgdev->enroll_data = fpi_print_data_new(FP_DEV(imgdev));
|
||||
}
|
||||
BUG_ON(g_slist_length(print->prints) != 1);
|
||||
/* Move print data from acquire data into enroll_data */
|
||||
imgdev->enroll_data->prints =
|
||||
g_slist_prepend(imgdev->enroll_data->prints, print->prints->data);
|
||||
print->prints = g_slist_remove(print->prints, print->prints->data);
|
||||
|
||||
fp_print_data_free(imgdev->acquire_data);
|
||||
imgdev->acquire_data = NULL;
|
||||
imgdev->enroll_stage++;
|
||||
if (imgdev->enroll_stage == FP_DEV(imgdev)->nr_enroll_stages)
|
||||
imgdev->action_result = FP_ENROLL_COMPLETE;
|
||||
else
|
||||
imgdev->action_result = FP_ENROLL_PASS;
|
||||
break;
|
||||
case IMG_ACTION_VERIFY:
|
||||
verify_process_img(imgdev);
|
||||
break;
|
||||
case IMG_ACTION_IDENTIFY:
|
||||
identify_process_img(imgdev);
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
imgdev->action_result = FP_CAPTURE_COMPLETE;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
next_state:
|
||||
imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF;
|
||||
dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_OFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_session_error:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
* @error: an error code
|
||||
*
|
||||
* Report an error that occurred in the driver.
|
||||
*/
|
||||
void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error)
|
||||
{
|
||||
fp_dbg("error %d", error);
|
||||
BUG_ON(error == 0);
|
||||
switch (imgdev->action) {
|
||||
case IMG_ACTION_ENROLL:
|
||||
fpi_drvcb_enroll_stage_completed(FP_DEV(imgdev), error, NULL, NULL);
|
||||
break;
|
||||
case IMG_ACTION_VERIFY:
|
||||
fpi_drvcb_report_verify_result(FP_DEV(imgdev), error, NULL);
|
||||
break;
|
||||
case IMG_ACTION_IDENTIFY:
|
||||
fpi_drvcb_report_identify_result(FP_DEV(imgdev), error, 0, NULL);
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
fpi_drvcb_report_capture_result(FP_DEV(imgdev), error, NULL);
|
||||
break;
|
||||
default:
|
||||
fp_err("unhandled action %d", imgdev->action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_activate_complete:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
* @status: the activation result
|
||||
*
|
||||
* Marks an activation as complete, whether successful or not.
|
||||
* See fpi_imgdev_get_action_result() for possible values.
|
||||
*/
|
||||
void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status)
|
||||
{
|
||||
fp_dbg("status %d", status);
|
||||
|
||||
switch (imgdev->action) {
|
||||
case IMG_ACTION_ENROLL:
|
||||
fpi_drvcb_enroll_started(FP_DEV(imgdev), status);
|
||||
break;
|
||||
case IMG_ACTION_VERIFY:
|
||||
fpi_drvcb_verify_started(FP_DEV(imgdev), status);
|
||||
break;
|
||||
case IMG_ACTION_IDENTIFY:
|
||||
fpi_drvcb_identify_started(FP_DEV(imgdev), status);
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
fpi_drvcb_capture_started(FP_DEV(imgdev), status);
|
||||
break;
|
||||
default:
|
||||
fp_err("unhandled action %d", imgdev->action);
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_ON;
|
||||
dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_imgdev_deactivate_complete:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
*
|
||||
* Marks a deactivation as complete.
|
||||
*/
|
||||
void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
|
||||
{
|
||||
enum fp_imgdev_action action;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
action = imgdev->action;
|
||||
imgdev->action = IMG_ACTION_NONE;
|
||||
imgdev->action_state = 0;
|
||||
|
||||
switch (action) {
|
||||
case IMG_ACTION_ENROLL:
|
||||
fpi_drvcb_enroll_stopped(FP_DEV(imgdev));
|
||||
break;
|
||||
case IMG_ACTION_VERIFY:
|
||||
fpi_drvcb_verify_stopped(FP_DEV(imgdev));
|
||||
break;
|
||||
case IMG_ACTION_IDENTIFY:
|
||||
fpi_drvcb_identify_stopped(FP_DEV(imgdev));
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
fpi_drvcb_capture_stopped(FP_DEV(imgdev));
|
||||
break;
|
||||
default:
|
||||
fp_err("unhandled action %d", imgdev->action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev)
|
||||
{
|
||||
struct fp_driver *drv = FP_DEV(imgdev)->drv;
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
|
||||
int width = imgdrv->img_width;
|
||||
|
||||
if (width == -1)
|
||||
width = 0;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev)
|
||||
{
|
||||
struct fp_driver *drv = FP_DEV(imgdev)->drv;
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
|
||||
int height = imgdrv->img_height;
|
||||
|
||||
if (height == -1)
|
||||
height = 0;
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
static int dev_activate(struct fp_img_dev *imgdev)
|
||||
{
|
||||
struct fp_driver *drv = FP_DEV(imgdev)->drv;
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
|
||||
|
||||
if (!imgdrv->activate)
|
||||
return 0;
|
||||
return imgdrv->activate(imgdev);
|
||||
}
|
||||
|
||||
static void dev_deactivate(struct fp_img_dev *imgdev)
|
||||
{
|
||||
struct fp_driver *drv = FP_DEV(imgdev)->drv;
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
|
||||
|
||||
if (!imgdrv->deactivate)
|
||||
return;
|
||||
return imgdrv->deactivate(imgdev);
|
||||
}
|
||||
|
||||
static int generic_acquire_start(struct fp_dev *dev, int action)
|
||||
{
|
||||
struct fp_img_dev *imgdev = dev->img_dev;
|
||||
int r;
|
||||
fp_dbg("action %d", action);
|
||||
imgdev->action = action;
|
||||
imgdev->action_state = IMG_ACQUIRE_STATE_ACTIVATING;
|
||||
imgdev->enroll_stage = 0;
|
||||
|
||||
r = dev_activate(imgdev);
|
||||
if (r < 0)
|
||||
fp_err("activation failed with error %d", r);
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
static void generic_acquire_stop(struct fp_img_dev *imgdev)
|
||||
{
|
||||
imgdev->action_state = IMG_ACQUIRE_STATE_DEACTIVATING;
|
||||
dev_deactivate(imgdev);
|
||||
|
||||
fp_print_data_free(imgdev->acquire_data);
|
||||
fp_print_data_free(imgdev->enroll_data);
|
||||
fp_img_free(imgdev->acquire_img);
|
||||
imgdev->acquire_data = NULL;
|
||||
imgdev->enroll_data = NULL;
|
||||
imgdev->acquire_img = NULL;
|
||||
imgdev->action_result = 0;
|
||||
}
|
||||
|
||||
static int img_dev_enroll_start(struct fp_dev *dev)
|
||||
{
|
||||
return generic_acquire_start(dev, IMG_ACTION_ENROLL);
|
||||
}
|
||||
|
||||
static int img_dev_verify_start(struct fp_dev *dev)
|
||||
{
|
||||
return generic_acquire_start(dev, IMG_ACTION_VERIFY);
|
||||
}
|
||||
|
||||
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->img_dev;
|
||||
BUG_ON(imgdev->action != IMG_ACTION_ENROLL);
|
||||
generic_acquire_stop(imgdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_dev_verify_stop(struct fp_dev *dev, gboolean iterating)
|
||||
{
|
||||
struct fp_img_dev *imgdev = dev->img_dev;
|
||||
BUG_ON(imgdev->action != IMG_ACTION_VERIFY);
|
||||
generic_acquire_stop(imgdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_dev_identify_stop(struct fp_dev *dev, gboolean iterating)
|
||||
{
|
||||
struct fp_img_dev *imgdev = dev->img_dev;
|
||||
BUG_ON(imgdev->action != IMG_ACTION_IDENTIFY);
|
||||
generic_acquire_stop(imgdev);
|
||||
imgdev->identify_match_offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_dev_capture_stop(struct fp_dev *dev)
|
||||
{
|
||||
struct fp_img_dev *imgdev = dev->img_dev;
|
||||
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;
|
||||
idriver->driver.open = img_dev_open;
|
||||
idriver->driver.close = img_dev_close;
|
||||
idriver->driver.enroll_start = img_dev_enroll_start;
|
||||
idriver->driver.enroll_stop = img_dev_enroll_stop;
|
||||
idriver->driver.verify_start = img_dev_verify_start;
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __FPI_DEV_IMG_H__
|
||||
#define __FPI_DEV_IMG_H__
|
||||
|
||||
#include "fpi-dev.h"
|
||||
#include "fpi-img.h"
|
||||
|
||||
/**
|
||||
* fp_imgdev_action:
|
||||
* @IMG_ACTION_NONE: no action
|
||||
* @IMG_ACTION_ENROLL: device action is enrolling
|
||||
* @IMG_ACTION_VERIFY: device action is verifying
|
||||
* @IMG_ACTION_IDENTIFY: device action is identifying
|
||||
* @IMG_ACTION_CAPTURE: device action is capturing
|
||||
*
|
||||
* The current action being performed by an imaging device. The current
|
||||
* action can be gathered inside the driver using fpi_imgdev_get_action().
|
||||
*/
|
||||
enum fp_imgdev_action {
|
||||
IMG_ACTION_NONE = 0,
|
||||
IMG_ACTION_ENROLL,
|
||||
IMG_ACTION_VERIFY,
|
||||
IMG_ACTION_IDENTIFY,
|
||||
IMG_ACTION_CAPTURE,
|
||||
};
|
||||
|
||||
/**
|
||||
* fp_imgdev_state:
|
||||
* @IMGDEV_STATE_INACTIVE: inactive
|
||||
* @IMGDEV_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped
|
||||
* @IMGDEV_STATE_CAPTURE: capturing an image
|
||||
* @IMGDEV_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed
|
||||
*
|
||||
* The state of an imaging device while doing a capture. The state is
|
||||
* passed through to the driver using the ::activate() or ::change_state() vfuncs.
|
||||
*/
|
||||
enum fp_imgdev_state {
|
||||
IMGDEV_STATE_INACTIVE,
|
||||
IMGDEV_STATE_AWAIT_FINGER_ON,
|
||||
IMGDEV_STATE_CAPTURE,
|
||||
IMGDEV_STATE_AWAIT_FINGER_OFF,
|
||||
};
|
||||
|
||||
/**
|
||||
* fp_imgdev_enroll_state:
|
||||
* @IMG_ACQUIRE_STATE_NONE: doing nothing
|
||||
* @IMG_ACQUIRE_STATE_ACTIVATING: activating the device
|
||||
* @IMG_ACQUIRE_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped
|
||||
* @IMG_ACQUIRE_STATE_AWAIT_IMAGE: waiting for the image to be captured
|
||||
* @IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed
|
||||
* @IMG_ACQUIRE_STATE_DONE: enrollment has all the images it needs
|
||||
* @IMG_ACQUIRE_STATE_DEACTIVATING: deactivating the device
|
||||
*
|
||||
* The state of an imaging device while enrolling a fingerprint. Given that enrollment
|
||||
* requires multiple captures, a number of those states will be repeated before
|
||||
* the state is @IMG_ACQUIRE_STATE_DONE.
|
||||
*/
|
||||
enum fp_imgdev_enroll_state {
|
||||
IMG_ACQUIRE_STATE_NONE = 0,
|
||||
IMG_ACQUIRE_STATE_ACTIVATING,
|
||||
IMG_ACQUIRE_STATE_AWAIT_FINGER_ON,
|
||||
IMG_ACQUIRE_STATE_AWAIT_IMAGE,
|
||||
IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF,
|
||||
IMG_ACQUIRE_STATE_DONE,
|
||||
IMG_ACQUIRE_STATE_DEACTIVATING,
|
||||
};
|
||||
|
||||
void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status);
|
||||
void fpi_imgdev_close_complete(struct fp_img_dev *imgdev);
|
||||
void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status);
|
||||
void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev);
|
||||
void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
|
||||
gboolean present);
|
||||
void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img);
|
||||
void fpi_imgdev_abort_scan(struct fp_img_dev *imgdev, int result);
|
||||
void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error);
|
||||
|
||||
enum fp_imgdev_enroll_state fpi_imgdev_get_action_state(struct fp_img_dev *imgdev);
|
||||
enum fp_imgdev_action fpi_imgdev_get_action(struct fp_img_dev *imgdev);
|
||||
int fpi_imgdev_get_action_result(struct fp_img_dev *imgdev);
|
||||
void fpi_imgdev_set_action_result(struct fp_img_dev *imgdev, int action_result);
|
||||
|
||||
#endif
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* fp_dev types manipulation
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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 "fp_internal.h"
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* SECTION:fpi-dev
|
||||
* @title: Device operations
|
||||
* @short_description: Device operation functions
|
||||
*
|
||||
* Those macros and functions will help get access to and from struct #fp_dev,
|
||||
* and struct #fp_img_dev types, as well as get and set the instance struct
|
||||
* data, eg. the structure containing the data specific to each driver.
|
||||
*/
|
||||
|
||||
/**
|
||||
* FP_DEV:
|
||||
* @dev: a struct #fp_img_dev
|
||||
*
|
||||
* Returns the struct #fp_dev associated with @dev, or %NULL on failure.
|
||||
*
|
||||
* Returns: a struct #fp_dev or %NULL
|
||||
*/
|
||||
struct fp_dev *
|
||||
FP_DEV(struct fp_img_dev *dev)
|
||||
{
|
||||
struct fp_img_dev *imgdev;
|
||||
|
||||
g_return_val_if_fail (dev, NULL);
|
||||
imgdev = (struct fp_img_dev *) dev;
|
||||
return imgdev->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* FP_IMG_DEV:
|
||||
* @dev: a struct #fp_dev representing an imaging device.
|
||||
*
|
||||
* Returns a struct #fp_img_dev associated with @dev, or %NULL on failure.
|
||||
*
|
||||
* Returns: a struct #fp_img_dev or %NULL
|
||||
*/
|
||||
struct fp_img_dev *
|
||||
FP_IMG_DEV(struct fp_dev *dev)
|
||||
{
|
||||
g_return_val_if_fail (dev, NULL);
|
||||
g_return_val_if_fail (dev->drv, NULL);
|
||||
g_return_val_if_fail (dev->drv->type == DRIVER_IMAGING, NULL);
|
||||
return dev->img_dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_set_instance_data:
|
||||
* @dev: a struct #fp_dev
|
||||
* @instance_data: a pointer to the instance data
|
||||
*
|
||||
* Set the instance data for a struct #fp_dev. This is usually a structure
|
||||
* private to the driver used to keep state and pass it as user_data to
|
||||
* asynchronous functions.
|
||||
*
|
||||
* The core does not do any memory management for this data, so the driver
|
||||
* itself will have to create and free its own structure when appropriate.
|
||||
*/
|
||||
void
|
||||
fp_dev_set_instance_data (struct fp_dev *dev,
|
||||
void *instance_data)
|
||||
{
|
||||
g_return_if_fail (dev);
|
||||
g_return_if_fail (instance_data != NULL);
|
||||
g_return_if_fail (dev->instance_data == NULL);
|
||||
|
||||
dev->instance_data = instance_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* FP_INSTANCE_DATA:
|
||||
* @dev: a struct #fp_dev
|
||||
*
|
||||
* Returns the instance data set using fp_dev_set_instance_data().
|
||||
*/
|
||||
void *
|
||||
FP_INSTANCE_DATA (struct fp_dev *dev)
|
||||
{
|
||||
g_return_val_if_fail (dev, NULL);
|
||||
|
||||
return dev->instance_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_dev_get_usb_dev:
|
||||
* @dev: a struct #fp_dev
|
||||
*
|
||||
* Returns the #libusb_device_handle associated with @dev or %NULL
|
||||
* if none are associated.
|
||||
*
|
||||
* Returns: a #libusb_device_handle pointer or %NULL
|
||||
*/
|
||||
libusb_device_handle *
|
||||
fpi_dev_get_usb_dev(struct fp_dev *dev)
|
||||
{
|
||||
return dev->udev;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_dev_set_nr_enroll_stages:
|
||||
* @dev: a struct #fp_dev
|
||||
* @nr_enroll_stages: the number of enroll stages
|
||||
*
|
||||
* Sets the number of enroll stages that this device uses. This is
|
||||
* usually only necessary for primitive devices which have a hard-coded
|
||||
* number of enroll stages baked into their protocol.
|
||||
*/
|
||||
void
|
||||
fpi_dev_set_nr_enroll_stages(struct fp_dev *dev,
|
||||
int nr_enroll_stages)
|
||||
{
|
||||
dev->nr_enroll_stages = nr_enroll_stages;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_dev_get_verify_data:
|
||||
* @dev: a struct #fp_dev
|
||||
*
|
||||
* Returns the verify data associated with @dev.
|
||||
* This is usually only necessary for primitive devices which need to
|
||||
* have access to the raw verify data as it might have been stored on disk.
|
||||
*
|
||||
* Returns: a struct #fp_print_data pointer or %NULL
|
||||
*/
|
||||
struct fp_print_data *
|
||||
fpi_dev_get_verify_data(struct fp_dev *dev)
|
||||
{
|
||||
return dev->verify_data;
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __FPI_DEV_H__
|
||||
#define __FPI_DEV_H__
|
||||
|
||||
#include <libusb.h>
|
||||
#include <fprint.h>
|
||||
|
||||
struct fp_dev;
|
||||
|
||||
/**
|
||||
* fp_img_dev:
|
||||
*
|
||||
* #fp_img_dev is an opaque structure type. You must access it using the
|
||||
* appropriate functions.
|
||||
*/
|
||||
struct fp_img_dev;
|
||||
|
||||
struct fp_dev *FP_DEV (struct fp_img_dev *dev);
|
||||
struct fp_img_dev *FP_IMG_DEV (struct fp_dev *dev);
|
||||
|
||||
void fp_dev_set_instance_data (struct fp_dev *dev,
|
||||
void *instance_data);
|
||||
void *FP_INSTANCE_DATA (struct fp_dev *dev);
|
||||
|
||||
libusb_device_handle *fpi_dev_get_usb_dev(struct fp_dev *dev);
|
||||
void fpi_dev_set_nr_enroll_stages(struct fp_dev *dev,
|
||||
int nr_enroll_stages);
|
||||
struct fp_print_data *fpi_dev_get_verify_data(struct fp_dev *dev);
|
||||
|
||||
#endif
|
252
libfprint/fpi-device.h
Normal file
252
libfprint/fpi-device.h
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* FpDevice - A fingerprint reader device
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gusb.h>
|
||||
#include "fp-device.h"
|
||||
#include "fp-image.h"
|
||||
#include "fpi-print.h"
|
||||
|
||||
/**
|
||||
* FpIdEntry:
|
||||
*
|
||||
* An entry in the table of supported hardware. For USB devices, the product ID
|
||||
* and vendor ID should be provided. The optional @driver_data field defaults
|
||||
* to 0 and can be used as a simple flag for device quirks.
|
||||
*/
|
||||
typedef struct _FpIdEntry FpIdEntry;
|
||||
|
||||
struct _FpIdEntry
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
guint pid;
|
||||
guint vid;
|
||||
};
|
||||
const gchar *virtual_envvar;
|
||||
};
|
||||
guint64 driver_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* FpDeviceClass:
|
||||
* @id: ID string for the driver. Should be a valid C identifier and should
|
||||
* match the drivers file name.
|
||||
* @full_name: Human readable description of the driver
|
||||
* @type: The type of driver
|
||||
* @id_table: The table of IDs to bind the driver to
|
||||
* @nr_enroll_stages: The number of enroll stages supported devices need; use
|
||||
* fpi_device_set_nr_enroll_stages() from @probe if this is dynamic.
|
||||
* @scan_type: The scan type of supported devices; use
|
||||
* fpi_device_set_scan_type() from @probe if this is dynamic.
|
||||
* @usb_discover: Class method to check whether a USB device is supported by
|
||||
* the driver. Should return 0 if the device is unsupported and a positive
|
||||
* score otherwise. The default score is 50 and the driver with the highest
|
||||
* score will be loaded.
|
||||
* @probe: Called immediately for all devices. Most drivers will not need to
|
||||
* implement this. Drivers should setup the device identifier from the probe
|
||||
* callback which will be used to verify the compatibility of stored
|
||||
* #FpPrint's. It is permissable to temporarily open the USB device if this
|
||||
* is required for the operation. If an error is returned, then the device
|
||||
* will be destroyed again immediately and never reported to the API user.
|
||||
* @open: Open the device for futher operations. Any of the normal actions are
|
||||
* guaranteed to only happen when the device is open (this includes delete).
|
||||
* @close: Close the device again
|
||||
* @enroll: Start an enroll operation
|
||||
* @verify: Start a verify operation
|
||||
* @identify: Start an identify operation
|
||||
* @capture: Start a capture operation
|
||||
* @list: List prints stored on the device
|
||||
* @delete: Delete a print from the device
|
||||
* @cancel: Called on cancellation, this is a convenience to not need to handle
|
||||
* the #GCancellable directly by using fpi_device_get_cancellable().
|
||||
*
|
||||
* NOTE: If your driver is image based, then you should subclass #FpImageDevice
|
||||
* instead. #FpImageDevice based drivers use a different way of interacting
|
||||
* with libfprint.
|
||||
*
|
||||
* These are the main entry points for drivers to implement. Drivers may not
|
||||
* implement all of these entry points if they do not support the operation
|
||||
* (or a default implementation is sufficient).
|
||||
*
|
||||
* Drivers must eventually call the corresponding function to finish the
|
||||
* operation. It is also acceptable to call the generic
|
||||
* fpi_device_action_error() function but doing so is not recommended in most
|
||||
* usecases.
|
||||
*
|
||||
* Drivers must also handle cancellation properly for any long running
|
||||
* operation (i.e. any operation that requires capturing). It is entirely fine
|
||||
* to ignore cancellation requests for short operations (e.g. open/close).
|
||||
*
|
||||
* This API is solely intended for drivers. It is purely internal and neither
|
||||
* API nor ABI stable.
|
||||
*/
|
||||
struct _FpDeviceClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
/* Static information about the driver. */
|
||||
const gchar *id;
|
||||
const gchar *full_name;
|
||||
FpDeviceType type;
|
||||
const FpIdEntry *id_table;
|
||||
|
||||
/* Defaults for device properties */
|
||||
gint nr_enroll_stages;
|
||||
FpScanType scan_type;
|
||||
|
||||
/* Callbacks */
|
||||
gint (*usb_discover) (GUsbDevice *usb_device);
|
||||
void (*probe) (FpDevice *device);
|
||||
void (*open) (FpDevice *device);
|
||||
void (*close) (FpDevice *device);
|
||||
void (*enroll) (FpDevice *device);
|
||||
void (*verify) (FpDevice *device);
|
||||
void (*identify) (FpDevice *device);
|
||||
void (*capture) (FpDevice *device);
|
||||
void (*list) (FpDevice *device);
|
||||
void (*delete) (FpDevice * device);
|
||||
|
||||
void (*cancel) (FpDevice *device);
|
||||
};
|
||||
|
||||
/**
|
||||
* FpTimeoutFunc:
|
||||
* @device: The #FpDevice passed to fpi_device_add_timeout()
|
||||
* @user_data: the data passed to fpi_device_add_timeout()
|
||||
*
|
||||
* The prototype of the callback function for fpi_device_add_timeout().
|
||||
*/
|
||||
typedef void (*FpTimeoutFunc) (FpDevice *device,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* FpDeviceAction:
|
||||
* @FP_DEVICE_ACTION_NONE: No action is active.
|
||||
* @FP_DEVICE_ACTION_PROBE: Probe device for support and information.
|
||||
* @FP_DEVICE_ACTION_OPEN: Device is currently being opened.
|
||||
* @FP_DEVICE_ACTION_CLOSE: Device is currently being closed.
|
||||
* @FP_DEVICE_ACTION_ENROLL: Device is currently enrolling.
|
||||
* @FP_DEVICE_ACTION_VERIFY: Device is currently verifying.
|
||||
* @FP_DEVICE_ACTION_IDENTIFY: Device is currently identifying.
|
||||
* @FP_DEVICE_ACTION_CAPTURE: Device is currently capturing an image.
|
||||
* @FP_DEVICE_ACTION_LIST: Device stored prints are being queried.
|
||||
* @FP_DEVICE_ACTION_DELETE: Device stored print is being deleted.
|
||||
*
|
||||
* Current active action of the device. A driver can retrieve the action.
|
||||
*/
|
||||
typedef enum {
|
||||
FP_DEVICE_ACTION_NONE = 0,
|
||||
FP_DEVICE_ACTION_PROBE,
|
||||
FP_DEVICE_ACTION_OPEN,
|
||||
FP_DEVICE_ACTION_CLOSE,
|
||||
FP_DEVICE_ACTION_ENROLL,
|
||||
FP_DEVICE_ACTION_VERIFY,
|
||||
FP_DEVICE_ACTION_IDENTIFY,
|
||||
FP_DEVICE_ACTION_CAPTURE,
|
||||
FP_DEVICE_ACTION_LIST,
|
||||
FP_DEVICE_ACTION_DELETE,
|
||||
} FpDeviceAction;
|
||||
|
||||
GUsbDevice *fpi_device_get_usb_device (FpDevice *device);
|
||||
const gchar *fpi_device_get_virtual_env (FpDevice *device);
|
||||
//const gchar *fpi_device_get_spi_dev (FpDevice *device);
|
||||
|
||||
|
||||
FpDeviceAction fpi_device_get_current_action (FpDevice *device);
|
||||
gboolean fpi_device_action_is_cancelled (FpDevice *device);
|
||||
|
||||
GError * fpi_device_retry_new (FpDeviceRetry error);
|
||||
GError * fpi_device_error_new (FpDeviceError error);
|
||||
|
||||
GError * fpi_device_retry_new_msg (FpDeviceRetry error,
|
||||
const gchar *msg);
|
||||
GError * fpi_device_error_new_msg (FpDeviceError error,
|
||||
const gchar *msg);
|
||||
|
||||
guint64 fpi_device_get_driver_data (FpDevice *device);
|
||||
|
||||
void fpi_device_get_enroll_data (FpDevice *device,
|
||||
FpPrint **print);
|
||||
|
||||
void fpi_device_get_capture_data (FpDevice *device,
|
||||
gboolean *wait_for_finger);
|
||||
void fpi_device_get_verify_data (FpDevice *device,
|
||||
FpPrint **print);
|
||||
void fpi_device_get_identify_data (FpDevice *device,
|
||||
GPtrArray **prints);
|
||||
void fpi_device_get_delete_data (FpDevice *device,
|
||||
FpPrint **print);
|
||||
GCancellable *fpi_device_get_cancellable (FpDevice *device);
|
||||
|
||||
|
||||
|
||||
GSource * fpi_device_add_timeout (FpDevice *device,
|
||||
gint interval,
|
||||
FpTimeoutFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
void fpi_device_set_nr_enroll_stages (FpDevice *device,
|
||||
gint enroll_stages);
|
||||
|
||||
void fpi_device_set_scan_type (FpDevice *device,
|
||||
FpScanType scan_type);
|
||||
|
||||
void fpi_device_action_error (FpDevice *device,
|
||||
GError *error);
|
||||
|
||||
void fpi_device_probe_complete (FpDevice *device,
|
||||
const gchar *device_id,
|
||||
const gchar *device_name,
|
||||
GError *error);
|
||||
void fpi_device_open_complete (FpDevice *device,
|
||||
GError *error);
|
||||
void fpi_device_close_complete (FpDevice *device,
|
||||
GError *error);
|
||||
void fpi_device_enroll_complete (FpDevice *device,
|
||||
FpPrint *print,
|
||||
GError *error);
|
||||
void fpi_device_verify_complete (FpDevice *device,
|
||||
FpiMatchResult result,
|
||||
FpPrint *print,
|
||||
GError *error);
|
||||
void fpi_device_identify_complete (FpDevice *device,
|
||||
FpPrint *match,
|
||||
FpPrint *print,
|
||||
GError *error);
|
||||
void fpi_device_capture_complete (FpDevice *device,
|
||||
FpImage *image,
|
||||
GError *error);
|
||||
void fpi_device_delete_complete (FpDevice *device,
|
||||
GError *error);
|
||||
void fpi_device_list_complete (FpDevice *device,
|
||||
GPtrArray *prints,
|
||||
GError *error);
|
||||
|
||||
void fpi_device_enroll_progress (FpDevice *device,
|
||||
gint completed_stages,
|
||||
FpPrint *print,
|
||||
GError *error);
|
||||
|
||||
G_END_DECLS
|
118
libfprint/fpi-image-device.h
Normal file
118
libfprint/fpi-image-device.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* FpImageDevice - An image based fingerprint reader device
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fpi-device.h"
|
||||
#include "fp-image-device.h"
|
||||
|
||||
/**
|
||||
* FpImageDeviceState:
|
||||
* @FP_IMAGE_DEVICE_STATE_INACTIVE: inactive
|
||||
* @FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped
|
||||
* @FP_IMAGE_DEVICE_STATE_CAPTURE: capturing an image
|
||||
* @FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed
|
||||
*
|
||||
* The state of an imaging device while doing a capture. The state is
|
||||
* passed through to the driver using the ::activate() or ::change_state() vfuncs.
|
||||
*
|
||||
* The driver needs to call fpi_image_device_report_finger_status() to move
|
||||
* between the different states. Note that the capture state might be entered
|
||||
* unconditionally if the device supports raw capturing.
|
||||
*/
|
||||
typedef enum {
|
||||
FP_IMAGE_DEVICE_STATE_INACTIVE,
|
||||
FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON,
|
||||
FP_IMAGE_DEVICE_STATE_CAPTURE,
|
||||
FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF,
|
||||
} FpImageDeviceState;
|
||||
|
||||
/**
|
||||
* FpImageDeviceClass:
|
||||
* @bz3_threshold: Threshold to consider bozorth3 score a match, default: 40
|
||||
* @img_width: Width of the image, only provide if constant
|
||||
* @img_height: Height of the image, only provide if constant
|
||||
* @img_open: Open the device and do basic initialization
|
||||
* (use this instead of the #FpDeviceClass open vfunc)
|
||||
* @img_close: Close the device
|
||||
* (use this instead of the #FpDeviceClass close vfunc)
|
||||
* @activate: Start image capture and finger detection
|
||||
* @deactivate: Stop image capture and finger detection
|
||||
* @change_state: Notification about the current device state (i.e. waiting for
|
||||
* finger or image capture). Implementing this is optional, it can e.g. be
|
||||
* used to flash an LED when waiting for a finger.
|
||||
*
|
||||
* These are the main entry points for image based drivers. For all but the
|
||||
* change_state vfunc, implementations *must* eventually call the corresponding
|
||||
* function to finish the operation. It is also acceptable to call the generic
|
||||
*
|
||||
*
|
||||
* These are the main entry points for drivers to implement. Drivers may not
|
||||
* implement all of these entry points if they do not support the operation
|
||||
* (or a default implementation is sufficient).
|
||||
*
|
||||
* Drivers *must* eventually call the corresponding function to finish the
|
||||
* operation. It is also acceptable to call the generic
|
||||
* fpi_device_action_error() function but doing so is not recommended in most
|
||||
* usecases.
|
||||
*
|
||||
* Drivers *must* also handle cancellation properly for any long running
|
||||
* operation (i.e. any operation that requires capturing). It is entirely fine
|
||||
* to ignore cancellation requests for short operations (e.g. open/close).
|
||||
*
|
||||
* This API is solely intended for drivers. It is purely internal and neither
|
||||
* API nor ABI stable.
|
||||
*/
|
||||
struct _FpImageDeviceClass
|
||||
{
|
||||
FpDeviceClass parent_class;
|
||||
|
||||
gint bz3_threshold;
|
||||
gint img_width;
|
||||
gint img_height;
|
||||
|
||||
void (*img_open) (FpImageDevice *dev);
|
||||
void (*img_close) (FpImageDevice *dev);
|
||||
void (*activate) (FpImageDevice *dev);
|
||||
void (*change_state) (FpImageDevice *dev,
|
||||
FpImageDeviceState state);
|
||||
void (*deactivate) (FpImageDevice *dev);
|
||||
};
|
||||
|
||||
void fpi_image_device_set_bz3_threshold (FpImageDevice *self,
|
||||
gint bz3_threshold);
|
||||
|
||||
void fpi_image_device_session_error (FpImageDevice *self,
|
||||
GError *error);
|
||||
|
||||
void fpi_image_device_open_complete (FpImageDevice *self,
|
||||
GError *error);
|
||||
void fpi_image_device_close_complete (FpImageDevice *self,
|
||||
GError *error);
|
||||
void fpi_image_device_activate_complete (FpImageDevice *self,
|
||||
GError *error);
|
||||
void fpi_image_device_deactivate_complete (FpImageDevice *self,
|
||||
GError *error);
|
||||
|
||||
void fpi_image_device_report_finger_status (FpImageDevice *self,
|
||||
gboolean present);
|
||||
void fpi_image_device_image_captured (FpImageDevice *self,
|
||||
FpImage *image);
|
||||
void fpi_image_device_retry_scan (FpImageDevice *self,
|
||||
FpDeviceRetry retry);
|
83
libfprint/fpi-image.h
Normal file
83
libfprint/fpi-image.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* FPrint Image
|
||||
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <config.h>
|
||||
#include "fp-image.h"
|
||||
|
||||
/**
|
||||
* FpiImageFlags:
|
||||
* @FPI_IMAGE_V_FLIPPED: the image is vertically flipped
|
||||
* @FPI_IMAGE_H_FLIPPED: the image is horizontally flipped
|
||||
* @FPI_IMAGE_COLORS_INVERTED: the colours are inverted
|
||||
*
|
||||
* Flags used in an #FpImage structure to describe the contained image.
|
||||
* This is useful for image drivers as they can simply set these flags and
|
||||
* rely on the image to be normalized by libfprint before further processing.
|
||||
*/
|
||||
typedef enum {
|
||||
FPI_IMAGE_V_FLIPPED = 1 << 0,
|
||||
FPI_IMAGE_H_FLIPPED = 1 << 1,
|
||||
FPI_IMAGE_COLORS_INVERTED = 1 << 2,
|
||||
} FpiImageFlags;
|
||||
|
||||
/**
|
||||
* FpImage:
|
||||
* @width: Width of the image
|
||||
* @height: Height of the image
|
||||
* @ppmm: Pixels per millimeter
|
||||
* @flags: #FpiImageFlags for required normalization
|
||||
*
|
||||
* Structure holding an image. The public fields are only public for internal
|
||||
* use by the drivers.
|
||||
*/
|
||||
struct _FpImage
|
||||
{
|
||||
/*< private >*/
|
||||
GObject parent;
|
||||
|
||||
/*< public >*/
|
||||
guint width;
|
||||
guint height;
|
||||
|
||||
gdouble ppmm;
|
||||
|
||||
FpiImageFlags flags;
|
||||
|
||||
/*< private >*/
|
||||
guint8 *data;
|
||||
guint8 *binarized;
|
||||
|
||||
GPtrArray *minutiae;
|
||||
guint ref_count;
|
||||
};
|
||||
|
||||
gint fpi_std_sq_dev (const guint8 *buf,
|
||||
gint size);
|
||||
gint fpi_mean_sq_diff_norm (const guint8 *buf1,
|
||||
const guint8 *buf2,
|
||||
gint size);
|
||||
|
||||
#if HAVE_PIXMAN
|
||||
FpImage *fpi_image_resize (FpImage *orig,
|
||||
guint w_factor,
|
||||
guint h_factor);
|
||||
#endif
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Imaging utility functions for libfprint
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2013 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
*
|
||||
* 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 <pixman.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
|
||||
/**
|
||||
* fpi_img_resize:
|
||||
* @img: an #fp_img image
|
||||
* @w_factor: horizontal factor to resize the image by
|
||||
* @h_factor: vertical factor to resize the image by
|
||||
*
|
||||
* Resizes the #fp_img image by scaling it by @w_factor times horizontally
|
||||
* and @h_factor times vertically.
|
||||
*
|
||||
* Returns: a newly allocated #fp_img, the original @img will not be modified
|
||||
* and will also need to be freed
|
||||
*/
|
||||
struct fp_img *fpi_img_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor)
|
||||
{
|
||||
int new_width = img->width * w_factor;
|
||||
int new_height = img->height * h_factor;
|
||||
pixman_image_t *orig, *resized;
|
||||
pixman_transform_t transform;
|
||||
struct fp_img *newimg;
|
||||
|
||||
orig = pixman_image_create_bits(PIXMAN_a8, img->width, img->height, (uint32_t *)img->data, img->width);
|
||||
resized = pixman_image_create_bits(PIXMAN_a8, new_width, new_height, NULL, new_width);
|
||||
|
||||
pixman_transform_init_identity(&transform);
|
||||
pixman_transform_scale(NULL, &transform, pixman_int_to_fixed(w_factor), pixman_int_to_fixed(h_factor));
|
||||
pixman_image_set_transform(orig, &transform);
|
||||
pixman_image_set_filter(orig, PIXMAN_FILTER_BILINEAR, NULL, 0);
|
||||
pixman_image_composite32(PIXMAN_OP_SRC,
|
||||
orig, /* src */
|
||||
NULL, /* mask */
|
||||
resized, /* dst */
|
||||
0, 0, /* src x y */
|
||||
0, 0, /* mask x y */
|
||||
0, 0, /* dst x y */
|
||||
new_width, new_height /* width height */
|
||||
);
|
||||
|
||||
newimg = fpi_img_new(new_width * new_height);
|
||||
newimg->width = new_width;
|
||||
newimg->height = new_height;
|
||||
newimg->flags = img->flags;
|
||||
|
||||
memcpy(newimg->data, pixman_image_get_data(resized), new_width * new_height);
|
||||
|
||||
pixman_image_unref(orig);
|
||||
pixman_image_unref(resized);
|
||||
|
||||
return newimg;
|
||||
}
|
||||
|
|
@ -1,682 +0,0 @@
|
|||
/*
|
||||
* 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"
|
||||
#include "nbis/include/bozorth.h"
|
||||
#include "nbis/include/lfs.h"
|
||||
|
||||
/**
|
||||
* SECTION:img
|
||||
* @title: Image operations
|
||||
* @short_description: Image operation functions
|
||||
*
|
||||
* libfprint offers several ways of retrieving images from imaging devices,
|
||||
* one example being the fp_dev_img_capture() function. The functions
|
||||
* documented below allow you to work with such images.
|
||||
*
|
||||
* # Image format # {#img_fmt}
|
||||
* All images are represented as 8-bit greyscale data.
|
||||
*
|
||||
* # Image standardization # {#img_std}
|
||||
* In some contexts, images you are provided through libfprint are raw images
|
||||
* from the hardware. The orientation of these varies from device-to-device,
|
||||
* as does the color scheme (black-on-white or white-on-black?). libfprint
|
||||
* provides the fp_img_standardize() function to convert images into standard
|
||||
* form, which is defined to be: finger flesh as black on white surroundings,
|
||||
* natural upright orientation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:fpi-img
|
||||
* @title: Driver Image operations
|
||||
* @short_description: Driver image operation functions
|
||||
*
|
||||
* Those are the driver-specific helpers for #fp_img manipulation. See #fp_img's
|
||||
* documentation for more information about data formats, and their uses in
|
||||
* front-end applications.
|
||||
*/
|
||||
|
||||
/**
|
||||
* fpi_img_new:
|
||||
* @length: the length of data to allocate
|
||||
*
|
||||
* Creates a new #fp_img structure with @length bytes of data allocated
|
||||
* to hold the image.
|
||||
*
|
||||
* Returns: a new #fp_img to free with fp_img_free()
|
||||
*/
|
||||
struct fp_img *fpi_img_new(size_t length)
|
||||
{
|
||||
struct fp_img *img = g_malloc0(sizeof(*img) + length);
|
||||
fp_dbg("length=%zd", length);
|
||||
img->length = length;
|
||||
return img;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_img_new_for_imgdev:
|
||||
* @imgdev: a #fp_img_dev imaging fingerprint device
|
||||
*
|
||||
* Creates a new #fp_img structure, like fpi_img_new(), but uses the
|
||||
* driver's advertised height and width to calculate the size of the
|
||||
* length of data to allocate.
|
||||
*
|
||||
* Returns: a new #fp_img to free with fp_img_free()
|
||||
*/
|
||||
struct fp_img *fpi_img_new_for_imgdev(struct fp_img_dev *imgdev)
|
||||
{
|
||||
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(FP_DEV(imgdev)->drv);
|
||||
int width = imgdrv->img_width;
|
||||
int height = imgdrv->img_height;
|
||||
struct fp_img *img = fpi_img_new(width * height);
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
return img;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_img_is_sane:
|
||||
* @img: a #fp_img image
|
||||
*
|
||||
* Checks whether an #fp_img structure passes some basic checks, such
|
||||
* as length, width and height being non-zero, and the buffer being
|
||||
* big enough to hold the image of said size.
|
||||
*
|
||||
* Returns: %TRUE if the image is sane, %FALSE otherwise
|
||||
*/
|
||||
gboolean fpi_img_is_sane(struct fp_img *img)
|
||||
{
|
||||
guint len;
|
||||
|
||||
/* basic checks */
|
||||
if (!img->length || img->width <= 0 || img->height <= 0)
|
||||
return FALSE;
|
||||
|
||||
/* Are width and height just too big? */
|
||||
if (!g_uint_checked_mul(&len, img->width, img->height) ||
|
||||
len > G_MAXINT)
|
||||
return FALSE;
|
||||
|
||||
/* buffer big enough? */
|
||||
if (len > img->length)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_img_realloc:
|
||||
* @img: an #fp_img image
|
||||
* @newsize: the new length of the image
|
||||
*
|
||||
* Changes the size of the data part of the #fp_img.
|
||||
*
|
||||
* Returns: the modified #fp_img, the same as the first argument to this function
|
||||
*/
|
||||
struct fp_img *fpi_img_realloc(struct fp_img *img, size_t newsize)
|
||||
{
|
||||
return g_realloc(img, sizeof(*img) + newsize);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_img_free:
|
||||
* @img: the image to destroy. If %NULL, function simply returns.
|
||||
*
|
||||
* Frees an image. Must be called when you are finished working with an image.
|
||||
*/
|
||||
API_EXPORTED void fp_img_free(struct fp_img *img)
|
||||
{
|
||||
if (!img)
|
||||
return;
|
||||
|
||||
if (img->minutiae)
|
||||
free_minutiae(img->minutiae);
|
||||
if (img->binarized)
|
||||
free(img->binarized);
|
||||
g_free(img);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_img_get_height:
|
||||
* @img: an image
|
||||
*
|
||||
* Gets the pixel height of an image.
|
||||
*
|
||||
* Returns: the height of the image
|
||||
*/
|
||||
API_EXPORTED int fp_img_get_height(struct fp_img *img)
|
||||
{
|
||||
return img->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_img_get_width:
|
||||
* @img: an image
|
||||
*
|
||||
* Gets the pixel width of an image.
|
||||
*
|
||||
* Returns: the width of the image
|
||||
*/
|
||||
API_EXPORTED int fp_img_get_width(struct fp_img *img)
|
||||
{
|
||||
return img->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_img_get_data:
|
||||
* @img: an image
|
||||
*
|
||||
* Gets the greyscale data for an image. This data must not be modified or
|
||||
* freed, and must not be used after fp_img_free() has been called.
|
||||
*
|
||||
* Returns: a pointer to libfprint's internal data for the image
|
||||
*/
|
||||
API_EXPORTED unsigned char *fp_img_get_data(struct fp_img *img)
|
||||
{
|
||||
return img->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_img_save_to_file:
|
||||
* @img: the image to save
|
||||
* @path: the path to save the image. Existing files will be overwritten.
|
||||
*
|
||||
* A quick convenience function to save an image to a file in
|
||||
* [PGM format](http://netpbm.sourceforge.net/doc/pgm.html).
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error.
|
||||
*/
|
||||
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) {
|
||||
fclose(fd);
|
||||
fp_err("pgm header write failed, error %d", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = fwrite(img->data, 1, write_size, fd);
|
||||
if (r < write_size) {
|
||||
fclose(fd);
|
||||
fp_err("short write (%d)", r);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
fp_dbg("written to '%s'", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vflip(struct fp_img *img)
|
||||
{
|
||||
int width = img->width;
|
||||
int data_len = img->width * img->height;
|
||||
unsigned char rowbuf[width];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < img->height / 2; i++) {
|
||||
int offset = i * width;
|
||||
int swap_offset = data_len - (width * (i + 1));
|
||||
|
||||
/* copy top row into buffer */
|
||||
memcpy(rowbuf, img->data + offset, width);
|
||||
|
||||
/* copy lower row over upper row */
|
||||
memcpy(img->data + offset, img->data + swap_offset, width);
|
||||
|
||||
/* copy buffer over lower row */
|
||||
memcpy(img->data + swap_offset, rowbuf, width);
|
||||
}
|
||||
}
|
||||
|
||||
static void hflip(struct fp_img *img)
|
||||
{
|
||||
int width = img->width;
|
||||
unsigned char rowbuf[width];
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < img->height; i++) {
|
||||
int offset = i * width;
|
||||
|
||||
memcpy(rowbuf, img->data + offset, width);
|
||||
for (j = 0; j < width; j++)
|
||||
img->data[offset + j] = rowbuf[width - j - 1];
|
||||
}
|
||||
}
|
||||
|
||||
static void invert_colors(struct fp_img *img)
|
||||
{
|
||||
int data_len = img->width * img->height;
|
||||
int i;
|
||||
for (i = 0; i < data_len; i++)
|
||||
img->data[i] = 0xff - img->data[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_img_standardize:
|
||||
* @img: the image to standardize
|
||||
*
|
||||
* [Standardizes](libfprint-Image-operations.html#img_std) an image by normalizing its orientation, colors,
|
||||
* etc. It is safe to call this multiple times on an image, libfprint keeps
|
||||
* track of the work it needs to do to make an image standard and will not
|
||||
* perform these operations more than once for a given image.
|
||||
*/
|
||||
API_EXPORTED void fp_img_standardize(struct fp_img *img)
|
||||
{
|
||||
if (img->flags & FP_IMG_V_FLIPPED) {
|
||||
vflip(img);
|
||||
img->flags &= ~FP_IMG_V_FLIPPED;
|
||||
}
|
||||
if (img->flags & FP_IMG_H_FLIPPED) {
|
||||
hflip(img);
|
||||
img->flags &= ~FP_IMG_H_FLIPPED;
|
||||
}
|
||||
if (img->flags & FP_IMG_COLORS_INVERTED) {
|
||||
invert_colors(img);
|
||||
img->flags &= ~FP_IMG_COLORS_INVERTED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Based on write_minutiae_XYTQ and bz_load */
|
||||
static void minutiae_to_xyt(struct fp_minutiae *minutiae, int bwidth,
|
||||
int bheight, unsigned char *buf)
|
||||
{
|
||||
int i;
|
||||
struct fp_minutia *minutia;
|
||||
struct minutiae_struct c[MAX_FILE_MINUTIAE];
|
||||
struct xyt_struct *xyt = (struct xyt_struct *) buf;
|
||||
|
||||
/* struct xyt_struct uses arrays of MAX_BOZORTH_MINUTIAE (200) */
|
||||
int nmin = min(minutiae->num, MAX_BOZORTH_MINUTIAE);
|
||||
|
||||
for (i = 0; i < nmin; i++){
|
||||
minutia = minutiae->list[i];
|
||||
|
||||
lfs2nist_minutia_XYT(&c[i].col[0], &c[i].col[1], &c[i].col[2],
|
||||
minutia, bwidth, bheight);
|
||||
c[i].col[3] = sround(minutia->reliability * 100.0);
|
||||
|
||||
if (c[i].col[2] > 180)
|
||||
c[i].col[2] -= 360;
|
||||
}
|
||||
|
||||
qsort((void *) &c, (size_t) nmin, sizeof(struct minutiae_struct),
|
||||
sort_x_y);
|
||||
|
||||
for (i = 0; i < nmin; i++) {
|
||||
xyt->xcol[i] = c[i].col[0];
|
||||
xyt->ycol[i] = c[i].col[1];
|
||||
xyt->thetacol[i] = c[i].col[2];
|
||||
}
|
||||
xyt->nrows = nmin;
|
||||
}
|
||||
|
||||
#define FP_IMG_STANDARDIZATION_FLAGS (FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED \
|
||||
| FP_IMG_COLORS_INVERTED)
|
||||
|
||||
static int fpi_img_detect_minutiae(struct fp_img *img)
|
||||
{
|
||||
struct fp_minutiae *minutiae;
|
||||
int r;
|
||||
int *direction_map, *low_contrast_map, *low_flow_map;
|
||||
int *high_curve_map, *quality_map;
|
||||
int map_w, map_h;
|
||||
unsigned char *bdata;
|
||||
int bw, bh, bd;
|
||||
GTimer *timer;
|
||||
|
||||
if (img->flags & FP_IMG_STANDARDIZATION_FLAGS) {
|
||||
fp_err("Cannot detect minutiae for non-standardized images");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 25.4 mm per inch */
|
||||
timer = g_timer_new();
|
||||
r = get_minutiae(&minutiae, &quality_map, &direction_map,
|
||||
&low_contrast_map, &low_flow_map, &high_curve_map,
|
||||
&map_w, &map_h, &bdata, &bw, &bh, &bd,
|
||||
img->data, img->width, img->height, 8,
|
||||
DEFAULT_PPI / (double)25.4, &g_lfsparms_V2);
|
||||
g_timer_stop(timer);
|
||||
fp_dbg("minutiae scan completed in %f secs", g_timer_elapsed(timer, NULL));
|
||||
g_timer_destroy(timer);
|
||||
if (r) {
|
||||
fp_err("get minutiae failed, code %d", r);
|
||||
return r;
|
||||
}
|
||||
fp_dbg("detected %d minutiae", minutiae->num);
|
||||
img->minutiae = minutiae;
|
||||
img->binarized = bdata;
|
||||
|
||||
free(quality_map);
|
||||
free(direction_map);
|
||||
free(low_contrast_map);
|
||||
free(low_flow_map);
|
||||
free(high_curve_map);
|
||||
return minutiae->num;
|
||||
}
|
||||
|
||||
int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
|
||||
struct fp_print_data **ret)
|
||||
{
|
||||
struct fp_print_data *print;
|
||||
struct fp_print_data_item *item;
|
||||
int r;
|
||||
|
||||
if (!img->minutiae) {
|
||||
r = fpi_img_detect_minutiae(img);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!img->minutiae) {
|
||||
fp_err("no minutiae after successful detection?");
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: space is wasted if we don't hit the max minutiae count. would
|
||||
* be good to make this dynamic. */
|
||||
print = fpi_print_data_new(FP_DEV(imgdev));
|
||||
item = fpi_print_data_item_new(sizeof(struct xyt_struct));
|
||||
print->type = PRINT_DATA_NBIS_MINUTIAE;
|
||||
minutiae_to_xyt(img->minutiae, img->width, img->height, item->data);
|
||||
print->prints = g_slist_prepend(print->prints, item);
|
||||
|
||||
/* FIXME: the print buffer at this point is endian-specific, and will
|
||||
* only work when loaded onto machines with identical endianness. not good!
|
||||
* data format should be platform-independent. */
|
||||
*ret = print;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
|
||||
struct fp_print_data *new_print)
|
||||
{
|
||||
int score, max_score = 0, probe_len;
|
||||
struct xyt_struct *pstruct = NULL;
|
||||
struct xyt_struct *gstruct = NULL;
|
||||
struct fp_print_data_item *data_item;
|
||||
GSList *list_item;
|
||||
|
||||
if (enrolled_print->type != PRINT_DATA_NBIS_MINUTIAE ||
|
||||
new_print->type != PRINT_DATA_NBIS_MINUTIAE) {
|
||||
fp_err("invalid print format");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (g_slist_length(new_print->prints) != 1) {
|
||||
fp_err("new_print contains more than one sample, is it enrolled print?");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_item = new_print->prints->data;
|
||||
pstruct = (struct xyt_struct *)data_item->data;
|
||||
|
||||
probe_len = bozorth_probe_init(pstruct);
|
||||
list_item = enrolled_print->prints;
|
||||
do {
|
||||
data_item = list_item->data;
|
||||
gstruct = (struct xyt_struct *)data_item->data;
|
||||
score = bozorth_to_gallery(probe_len, pstruct, gstruct);
|
||||
fp_dbg("score %d", score);
|
||||
max_score = max(score, max_score);
|
||||
list_item = g_slist_next(list_item);
|
||||
} while (list_item);
|
||||
|
||||
return max_score;
|
||||
}
|
||||
|
||||
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
|
||||
struct fp_print_data **gallery, int match_threshold, size_t *match_offset)
|
||||
{
|
||||
struct xyt_struct *pstruct;
|
||||
struct xyt_struct *gstruct;
|
||||
struct fp_print_data *gallery_print;
|
||||
struct fp_print_data_item *data_item;
|
||||
int probe_len;
|
||||
size_t i = 0;
|
||||
int r;
|
||||
GSList *list_item;
|
||||
|
||||
if (g_slist_length(print->prints) != 1) {
|
||||
fp_err("new_print contains more than one sample, is it enrolled print?");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_item = print->prints->data;
|
||||
pstruct = (struct xyt_struct *)data_item->data;
|
||||
|
||||
probe_len = bozorth_probe_init(pstruct);
|
||||
while ((gallery_print = gallery[i++])) {
|
||||
list_item = gallery_print->prints;
|
||||
do {
|
||||
data_item = list_item->data;
|
||||
gstruct = (struct xyt_struct *)data_item->data;
|
||||
r = bozorth_to_gallery(probe_len, pstruct, gstruct);
|
||||
if (r >= match_threshold) {
|
||||
*match_offset = i - 1;
|
||||
return FP_VERIFY_MATCH;
|
||||
}
|
||||
list_item = g_slist_next(list_item);
|
||||
} while (list_item);
|
||||
}
|
||||
return FP_VERIFY_NO_MATCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_img_binarize:
|
||||
* @img: a standardized image
|
||||
*
|
||||
* Get a binarized form of a standardized scanned image. This is where the
|
||||
* fingerprint image has been "enhanced" and is a set of pure black ridges
|
||||
* on a pure white background. Internally, image processing happens on top
|
||||
* of the binarized image.
|
||||
*
|
||||
* The image must have been [standardized](libfprint-Image-operations.html#img_std)
|
||||
* otherwise this function will fail.
|
||||
*
|
||||
* It is safe to binarize an image and free the original while continuing
|
||||
* to use the binarized version.
|
||||
*
|
||||
* You cannot binarize an image twice.
|
||||
*
|
||||
* Returns: a new image representing the binarized form of the original, or
|
||||
* %NULL on error. Must be freed with fp_img_free() after use.
|
||||
*/
|
||||
API_EXPORTED struct fp_img *fp_img_binarize(struct fp_img *img)
|
||||
{
|
||||
struct fp_img *ret;
|
||||
int height = img->height;
|
||||
int width = img->width;
|
||||
int imgsize = height * width;
|
||||
|
||||
if (img->flags & FP_IMG_BINARIZED_FORM) {
|
||||
fp_err("image already binarized");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!img->binarized) {
|
||||
int r = fpi_img_detect_minutiae(img);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
if (!img->binarized) {
|
||||
fp_err("no minutiae after successful detection?");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = fpi_img_new(imgsize);
|
||||
ret->flags |= FP_IMG_BINARIZED_FORM;
|
||||
ret->width = width;
|
||||
ret->height = height;
|
||||
memcpy(ret->data, img->binarized, imgsize);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_img_get_minutiae:
|
||||
* @img: a standardized image
|
||||
* @nr_minutiae: an output location to store minutiae list length
|
||||
*
|
||||
* Get a list of minutiae detected in an image. A minutia point is a feature
|
||||
* detected on a fingerprint, typically where ridges end or split.
|
||||
* libfprint's image processing code relies upon comparing sets of minutiae,
|
||||
* so accurate placement of minutia points is critical for good imaging
|
||||
* performance.
|
||||
*
|
||||
* The image must have been [standardized](libfprint-Image-operations.html#img_std)
|
||||
* otherwise this function will fail.
|
||||
*
|
||||
* You cannot pass a binarized image to this function. Instead, pass the
|
||||
* original image.
|
||||
*
|
||||
* Returns a list of pointers to minutiae, where the list is of length
|
||||
* indicated in the nr_minutiae output parameter. The returned list is only
|
||||
* valid while the parent image has not been freed, and the minutiae data
|
||||
* must not be modified or freed.
|
||||
*
|
||||
* Returns: a list of minutiae points. Must not be modified or freed.
|
||||
*/
|
||||
API_EXPORTED struct fp_minutia **fp_img_get_minutiae(struct fp_img *img,
|
||||
int *nr_minutiae)
|
||||
{
|
||||
if (img->flags & FP_IMG_BINARIZED_FORM) {
|
||||
fp_err("image is binarized");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!img->minutiae) {
|
||||
int r = fpi_img_detect_minutiae(img);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
if (!img->minutiae) {
|
||||
fp_err("no minutiae after successful detection?");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*nr_minutiae = img->minutiae->num;
|
||||
return img->minutiae->list;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_minutia_get_coords:
|
||||
* @minutia: a struct #fp_minutia
|
||||
* @coord_x: the return variable for the X coordinate of the minutia
|
||||
* @coord_y: the return variable for the Y coordinate of the minutia
|
||||
*
|
||||
* Sets @coord_x and @coord_y to be the coordinates of the detected minutia, so it
|
||||
* can be presented in a more verbose user interface. This is usually only
|
||||
* used for debugging purposes.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
API_EXPORTED int fp_minutia_get_coords(struct fp_minutia *minutia, int *coord_x, int *coord_y)
|
||||
{
|
||||
g_return_val_if_fail (minutia != NULL, -1);
|
||||
g_return_val_if_fail (coord_x != NULL, -1);
|
||||
g_return_val_if_fail (coord_y != NULL, -1);
|
||||
|
||||
*coord_x = minutia->x;
|
||||
*coord_y = minutia->y;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_std_sq_dev:
|
||||
* @buf: buffer (usually bitmap, one byte per pixel)
|
||||
* @size: size of @buffer
|
||||
*
|
||||
* Calculates the squared standard deviation of the individual
|
||||
* pixels in the buffer, as per the following formula:
|
||||
* |[<!-- -->
|
||||
* mean = sum (buf[0..size]) / size
|
||||
* sq_dev = sum ((buf[0.size] - mean) ^ 2)
|
||||
* ]|
|
||||
* This function is usually used to determine whether image
|
||||
* is empty.
|
||||
*
|
||||
* Returns: the squared standard deviation for @buffer
|
||||
*/
|
||||
int fpi_std_sq_dev(const unsigned char *buf, int size)
|
||||
{
|
||||
int res = 0, mean = 0, i;
|
||||
|
||||
if (size > (INT_MAX / 65536)) {
|
||||
fp_err("%s: we might get an overflow!", __func__);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
mean += buf[i];
|
||||
|
||||
mean /= size;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
int dev = (int)buf[i] - mean;
|
||||
res += dev*dev;
|
||||
}
|
||||
|
||||
return res / size;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_mean_sq_diff_norm:
|
||||
* @buf1: buffer (usually bitmap, one byte per pixel)
|
||||
* @buf2: buffer (usually bitmap, one byte per pixel)
|
||||
* @size: buffer size of smallest buffer
|
||||
*
|
||||
* This function calculates the normalized mean square difference of
|
||||
* two buffers, usually two lines, as per the following formula:
|
||||
* |[<!-- -->
|
||||
* sq_diff = sum ((buf1[0..size] - buf2[0..size]) ^ 2) / size
|
||||
* ]|
|
||||
*
|
||||
* This functions is usually used to get numerical difference
|
||||
* between two images.
|
||||
*
|
||||
* Returns: the normalized mean squared difference between @buf1 and @buf2
|
||||
*/
|
||||
int fpi_mean_sq_diff_norm(unsigned char *buf1, unsigned char *buf2, int size)
|
||||
{
|
||||
int res = 0, i;
|
||||
for (i = 0; i < size; i++) {
|
||||
int dev = (int)buf1[i] - (int)buf2[i];
|
||||
res += dev * dev;
|
||||
}
|
||||
|
||||
return res / size;
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __FPI_IMG_H__
|
||||
#define __FPI_IMG_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct fp_minutiae;
|
||||
|
||||
/**
|
||||
* FpiImgFlags:
|
||||
* @FP_IMG_V_FLIPPED: the image is vertically flipped
|
||||
* @FP_IMG_H_FLIPPED: the image is horizontally flipped
|
||||
* @FP_IMG_COLORS_INVERTED: the colours are inverted
|
||||
* @FP_IMG_BINARIZED_FORM: binarised image, see fp_img_binarize()
|
||||
* @FP_IMG_PARTIAL: the image is partial, useful for driver to keep track
|
||||
* of incomplete captures
|
||||
*
|
||||
* Flags used in the #fp_img structure to describe the image contained
|
||||
* into the structure. Note that a number of functions will refuse to
|
||||
* handle images which haven't been standardised through fp_img_standardize()
|
||||
* (meaning the @FP_IMG_V_FLIPPED, @FP_IMG_H_FLIPPED and @FP_IMG_COLORS_INVERTED
|
||||
* should all be unset when the image needs to be analysed).
|
||||
*/
|
||||
typedef enum {
|
||||
FP_IMG_V_FLIPPED = 1 << 0,
|
||||
FP_IMG_H_FLIPPED = 1 << 1,
|
||||
FP_IMG_COLORS_INVERTED = 1 << 2,
|
||||
FP_IMG_BINARIZED_FORM = 1 << 3,
|
||||
FP_IMG_PARTIAL = 1 << 4
|
||||
} FpiImgFlags;
|
||||
|
||||
/**
|
||||
* fp_img:
|
||||
* @width: the width of the image
|
||||
* @height: the height of the image
|
||||
* @length: the length of the data associated with the image
|
||||
* @flags: @FpiImgFlags flags describing the image contained in the structure
|
||||
* @minutiae: an opaque structure representing the detected minutiae
|
||||
* @binarized: the binarized image data
|
||||
* @data: the start of the image data, which will be of @length size.
|
||||
*
|
||||
* A structure representing a captured, or processed image. The @flags member
|
||||
* will show its current state, including whether whether the binarized form
|
||||
* if present, whether it is complete, and whether it needs particular changes
|
||||
* before being processed.
|
||||
*/
|
||||
struct fp_img {
|
||||
int width;
|
||||
int height;
|
||||
size_t length;
|
||||
FpiImgFlags flags;
|
||||
/*< private >*/
|
||||
struct fp_minutiae *minutiae;
|
||||
/*< public >*/
|
||||
unsigned char *binarized;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
struct fp_img *fpi_img_new(size_t length);
|
||||
struct fp_img *fpi_img_new_for_imgdev(struct fp_img_dev *imgdev);
|
||||
struct fp_img *fpi_img_realloc(struct fp_img *img, size_t newsize);
|
||||
struct fp_img *fpi_img_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor);
|
||||
|
||||
int fpi_std_sq_dev(const unsigned char *buf, int size);
|
||||
int fpi_mean_sq_diff_norm(unsigned char *buf1, unsigned char *buf2, int size);
|
||||
|
||||
#endif
|
|
@ -1,485 +0,0 @@
|
|||
/*
|
||||
* Polling/timing management
|
||||
* Copyright (C) 2008 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
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "poll"
|
||||
|
||||
#include "fp_internal.h"
|
||||
#include "fpi-poll.h"
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
/**
|
||||
* SECTION:events
|
||||
* @title: Initialisation and events handling
|
||||
* @short_description: Initialisation and events handling functions
|
||||
*
|
||||
* These functions are only applicable to users of libfprint's asynchronous
|
||||
* API.
|
||||
*
|
||||
* libfprint does not create internal library threads and hence can only
|
||||
* execute when your application is calling a libfprint function. However,
|
||||
* libfprint often has work to be do, such as handling of completed USB
|
||||
* transfers, and processing of timeouts required in order for the library
|
||||
* to function. Therefore it is essential that your own application must
|
||||
* regularly "phone into" libfprint so that libfprint can handle any pending
|
||||
* events.
|
||||
*
|
||||
* The function you must call is fp_handle_events() or a variant of it. This
|
||||
* function will handle any pending events, and it is from this context that
|
||||
* all asynchronous event callbacks from the library will occur. You can view
|
||||
* this function as a kind of iteration function.
|
||||
*
|
||||
* If there are no events pending, fp_handle_events() will block for a few
|
||||
* seconds (and will handle any new events should anything occur in that time).
|
||||
* If you wish to customise this timeout, you can use
|
||||
* fp_handle_events_timeout() instead. If you wish to do a non-blocking
|
||||
* iteration, call fp_handle_events_timeout() with a zero timeout.
|
||||
*
|
||||
* How to integrate events handling depends on your main loop implementation.
|
||||
* The sister fprintd project includes an implementation of main loop handling
|
||||
* that integrates into GLib's main loop. The
|
||||
* [libusb documentation](http://libusb.sourceforge.net/api-1.0/group__poll.html#details)
|
||||
* also includes more details about how to integrate libfprint events into
|
||||
* your main loop.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:fpi-poll
|
||||
* @title: Timeouts
|
||||
* @short_description: Timeout handling helpers
|
||||
*
|
||||
* Helper functions to schedule a function call to be made after a timeout. This
|
||||
* is useful to avoid making blocking calls while waiting for hardware to answer
|
||||
* for example.
|
||||
*/
|
||||
|
||||
/* this is a singly-linked list of pending timers, sorted with the timer that
|
||||
* is expiring soonest at the head. */
|
||||
static GSList *active_timers = NULL;
|
||||
|
||||
/* notifiers for added or removed poll fds */
|
||||
static fp_pollfd_added_cb fd_added_cb = NULL;
|
||||
static fp_pollfd_removed_cb fd_removed_cb = NULL;
|
||||
|
||||
struct fpi_timeout {
|
||||
struct timeval expiry;
|
||||
fpi_timeout_fn callback;
|
||||
struct fp_dev *dev;
|
||||
void *data;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static int timeout_sort_fn(gconstpointer _a, gconstpointer _b)
|
||||
{
|
||||
fpi_timeout *a = (fpi_timeout *) _a;
|
||||
fpi_timeout *b = (fpi_timeout *) _b;
|
||||
struct timeval *tv_a = &a->expiry;
|
||||
struct timeval *tv_b = &b->expiry;
|
||||
|
||||
if (timercmp(tv_a, tv_b, <))
|
||||
return -1;
|
||||
else if (timercmp(tv_a, tv_b, >))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_timeout_free(fpi_timeout *timeout)
|
||||
{
|
||||
if (timeout == NULL)
|
||||
return;
|
||||
|
||||
g_free(timeout->name);
|
||||
g_free(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_timeout_set_name:
|
||||
* @timeout: a #fpi_timeout
|
||||
* @name: the name to give the timeout
|
||||
*
|
||||
* Sets a name for a timeout, allowing that name to be printed
|
||||
* along with any timeout related debug.
|
||||
*/
|
||||
void
|
||||
fpi_timeout_set_name(fpi_timeout *timeout,
|
||||
const char *name)
|
||||
{
|
||||
g_return_if_fail (timeout != NULL);
|
||||
g_return_if_fail (name != NULL);
|
||||
g_return_if_fail (timeout->name == NULL);
|
||||
|
||||
timeout->name = g_strdup(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_timeout_add:
|
||||
* @msec: the time before calling the function, in milliseconds (1/1000ths of a second)
|
||||
* @callback: function to callback
|
||||
* @dev: a struct #fp_dev
|
||||
* @data: data to pass to @callback, or %NULL
|
||||
*
|
||||
* A timeout is the asynchronous equivalent of sleeping. You create a timeout
|
||||
* saying that you'd like to have a function invoked at a certain time in
|
||||
* the future.
|
||||
*
|
||||
* Note that you should hold onto the return value of this function to cancel it
|
||||
* use fpi_timeout_cancel(), otherwise the callback could be called while the driver
|
||||
* is being torn down.
|
||||
*
|
||||
* This function can be considered to never fail.
|
||||
*
|
||||
* Returns: an #fpi_timeout structure
|
||||
*/
|
||||
fpi_timeout *fpi_timeout_add(unsigned int msec,
|
||||
fpi_timeout_fn callback,
|
||||
struct fp_dev *dev,
|
||||
void *data)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timeval add_msec;
|
||||
fpi_timeout *timeout;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail (dev != NULL, NULL);
|
||||
|
||||
fp_dbg("in %dms", msec);
|
||||
|
||||
r = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (r < 0) {
|
||||
fp_err("failed to read monotonic clock, errno=%d", errno);
|
||||
BUG();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
timeout = g_new0(fpi_timeout, 1);
|
||||
timeout->callback = callback;
|
||||
timeout->dev = dev;
|
||||
timeout->data = data;
|
||||
TIMESPEC_TO_TIMEVAL(&timeout->expiry, &ts);
|
||||
|
||||
/* calculate timeout expiry by adding delay to current monotonic clock */
|
||||
timerclear(&add_msec);
|
||||
add_msec.tv_sec = msec / 1000;
|
||||
add_msec.tv_usec = (msec % 1000) * 1000;
|
||||
timeradd(&timeout->expiry, &add_msec, &timeout->expiry);
|
||||
|
||||
active_timers = g_slist_insert_sorted(active_timers, timeout,
|
||||
timeout_sort_fn);
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_timeout_cancel:
|
||||
* @timeout: an #fpi_timeout structure
|
||||
*
|
||||
* Cancels a timeout scheduled with fpi_timeout_add(), and frees the
|
||||
* @timeout structure.
|
||||
*/
|
||||
void fpi_timeout_cancel(fpi_timeout *timeout)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
active_timers = g_slist_remove(active_timers, timeout);
|
||||
fpi_timeout_free(timeout);
|
||||
}
|
||||
|
||||
/* get the expiry time and optionally the timeout structure for the next
|
||||
* timeout. returns 0 if there are no expired timers, or 1 if the
|
||||
* timeval/timeout output parameters were populated. if the returned timeval
|
||||
* is zero then it means the timeout has already expired and should be handled
|
||||
* ASAP. */
|
||||
static int get_next_timeout_expiry(struct timeval *out,
|
||||
struct fpi_timeout **out_timeout)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timeval tv;
|
||||
struct fpi_timeout *next_timeout;
|
||||
int r;
|
||||
|
||||
if (active_timers == NULL)
|
||||
return 0;
|
||||
|
||||
r = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (r < 0) {
|
||||
fp_err("failed to read monotonic clock, errno=%d", errno);
|
||||
return r;
|
||||
}
|
||||
TIMESPEC_TO_TIMEVAL(&tv, &ts);
|
||||
|
||||
next_timeout = active_timers->data;
|
||||
if (out_timeout)
|
||||
*out_timeout = next_timeout;
|
||||
|
||||
if (timercmp(&tv, &next_timeout->expiry, >=)) {
|
||||
if (next_timeout->name)
|
||||
fp_dbg("first timeout '%s' already expired", next_timeout->name);
|
||||
else
|
||||
fp_dbg("first timeout already expired");
|
||||
timerclear(out);
|
||||
} else {
|
||||
timersub(&next_timeout->expiry, &tv, out);
|
||||
if (next_timeout->name)
|
||||
fp_dbg("next timeout '%s' in %ld.%06lds", next_timeout->name,
|
||||
out->tv_sec, out->tv_usec);
|
||||
else
|
||||
fp_dbg("next timeout in %ld.%06lds", out->tv_sec, out->tv_usec);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* handle a timeout that has expired */
|
||||
static void handle_timeout(struct fpi_timeout *timeout)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
timeout->callback(timeout->dev, timeout->data);
|
||||
active_timers = g_slist_remove(active_timers, timeout);
|
||||
fpi_timeout_free(timeout);
|
||||
}
|
||||
|
||||
static int handle_timeouts(void)
|
||||
{
|
||||
struct timeval next_timeout_expiry;
|
||||
struct fpi_timeout *next_timeout;
|
||||
int r;
|
||||
|
||||
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (!timerisset(&next_timeout_expiry))
|
||||
handle_timeout(next_timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_handle_events_timeout:
|
||||
* @timeout: Maximum timeout for this blocking function
|
||||
*
|
||||
* Handle any pending events. If a non-zero timeout is specified, the function
|
||||
* will potentially block for the specified amount of time, although it may
|
||||
* return sooner if events have been handled. The function acts as non-blocking
|
||||
* for a zero timeout.
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error.
|
||||
*/
|
||||
API_EXPORTED int fp_handle_events_timeout(struct timeval *timeout)
|
||||
{
|
||||
struct timeval next_timeout_expiry;
|
||||
struct timeval select_timeout;
|
||||
struct fpi_timeout *next_timeout;
|
||||
int r;
|
||||
|
||||
r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r) {
|
||||
/* timer already expired? */
|
||||
if (!timerisset(&next_timeout_expiry)) {
|
||||
handle_timeout(next_timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* choose the smallest of next URB timeout or user specified timeout */
|
||||
if (timercmp(&next_timeout_expiry, timeout, <))
|
||||
select_timeout = next_timeout_expiry;
|
||||
else
|
||||
select_timeout = *timeout;
|
||||
} else {
|
||||
select_timeout = *timeout;
|
||||
}
|
||||
|
||||
r = libusb_handle_events_timeout(fpi_usb_ctx, &select_timeout);
|
||||
*timeout = select_timeout;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return handle_timeouts();
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_handle_events:
|
||||
*
|
||||
* Convenience function for calling fp_handle_events_timeout() with a sensible
|
||||
* default timeout value of two seconds (subject to change if we decide another
|
||||
* value is more sensible).
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error.
|
||||
*/
|
||||
API_EXPORTED int fp_handle_events(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 2;
|
||||
tv.tv_usec = 0;
|
||||
return fp_handle_events_timeout(&tv);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_get_next_timeout:
|
||||
* @tv: a #timeval structure containing the duration to the next timeout.
|
||||
*
|
||||
* A zero filled @tv timeout means events are to be handled immediately
|
||||
*
|
||||
* Returns: returns 0 if no timeouts active, or 1 if timeout returned.
|
||||
*/
|
||||
API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
|
||||
{
|
||||
struct timeval fprint_timeout = { 0, 0 };
|
||||
struct timeval libusb_timeout = { 0, 0 };
|
||||
int r_fprint;
|
||||
int r_libusb;
|
||||
|
||||
r_fprint = get_next_timeout_expiry(&fprint_timeout, NULL);
|
||||
r_libusb = libusb_get_next_timeout(fpi_usb_ctx, &libusb_timeout);
|
||||
|
||||
/* if we have no pending timeouts and the same is true for libusb,
|
||||
* indicate that we have no pending timouts */
|
||||
if (r_fprint <= 0 && r_libusb <= 0)
|
||||
return 0;
|
||||
|
||||
/* if fprint have no pending timeouts return libusb timeout */
|
||||
else if (r_fprint == 0)
|
||||
*tv = libusb_timeout;
|
||||
|
||||
/* if libusb have no pending timeouts return fprint timeout */
|
||||
else if (r_libusb == 0)
|
||||
*tv = fprint_timeout;
|
||||
|
||||
/* otherwise return the smaller of the 2 timeouts */
|
||||
else if (timercmp(&fprint_timeout, &libusb_timeout, <))
|
||||
*tv = fprint_timeout;
|
||||
else
|
||||
*tv = libusb_timeout;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_get_pollfds:
|
||||
* @pollfds: output location for a list of pollfds. If non-%NULL, must be
|
||||
* released with free() when done.
|
||||
*
|
||||
* Retrieve a list of file descriptors that should be polled for events
|
||||
* interesting to libfprint. This function is only for users who wish to
|
||||
* combine libfprint's file descriptor set with other event sources – more
|
||||
* simplistic users will be able to call fp_handle_events() or a variant
|
||||
* directly.
|
||||
*
|
||||
* Returns: the number of pollfds in the resultant list, or negative on error.
|
||||
*/
|
||||
API_EXPORTED ssize_t fp_get_pollfds(struct fp_pollfd **pollfds)
|
||||
{
|
||||
const struct libusb_pollfd **usbfds;
|
||||
const struct libusb_pollfd *usbfd;
|
||||
struct fp_pollfd *ret;
|
||||
ssize_t cnt = 0;
|
||||
size_t i = 0;
|
||||
|
||||
g_return_val_if_fail (fpi_usb_ctx != NULL, -EIO);
|
||||
|
||||
usbfds = libusb_get_pollfds(fpi_usb_ctx);
|
||||
if (!usbfds) {
|
||||
*pollfds = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while ((usbfd = usbfds[i++]) != NULL)
|
||||
cnt++;
|
||||
|
||||
ret = g_malloc(sizeof(struct fp_pollfd) * cnt);
|
||||
i = 0;
|
||||
while ((usbfd = usbfds[i]) != NULL) {
|
||||
ret[i].fd = usbfd->fd;
|
||||
ret[i].events = usbfd->events;
|
||||
i++;
|
||||
}
|
||||
|
||||
*pollfds = ret;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_set_pollfd_notifiers:
|
||||
* @added_cb: a #fp_pollfd_added_cb callback or %NULL
|
||||
* @removed_cb: a #fp_pollfd_removed_cb callback or %NULL
|
||||
*
|
||||
* This sets the callback functions to call for every new or removed
|
||||
* file descriptor used as an event source.
|
||||
*/
|
||||
API_EXPORTED void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
|
||||
fp_pollfd_removed_cb removed_cb)
|
||||
{
|
||||
fd_added_cb = added_cb;
|
||||
fd_removed_cb = removed_cb;
|
||||
}
|
||||
|
||||
static void add_pollfd(int fd, short events, void *user_data)
|
||||
{
|
||||
if (fd_added_cb)
|
||||
fd_added_cb(fd, events);
|
||||
}
|
||||
|
||||
static void remove_pollfd(int fd, void *user_data)
|
||||
{
|
||||
if (fd_removed_cb)
|
||||
fd_removed_cb(fd);
|
||||
}
|
||||
|
||||
void fpi_poll_init(void)
|
||||
{
|
||||
libusb_set_pollfd_notifiers(fpi_usb_ctx, add_pollfd, remove_pollfd, NULL);
|
||||
}
|
||||
|
||||
void fpi_poll_exit(void)
|
||||
{
|
||||
g_slist_free_full(active_timers, (GDestroyNotify) fpi_timeout_free);
|
||||
active_timers = NULL;
|
||||
fd_added_cb = NULL;
|
||||
fd_removed_cb = NULL;
|
||||
libusb_set_pollfd_notifiers(fpi_usb_ctx, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
fpi_timeout_cancel_all_for_dev(struct fp_dev *dev)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
g_return_if_fail (dev != NULL);
|
||||
|
||||
l = active_timers;
|
||||
while (l) {
|
||||
struct fpi_timeout *timeout = l->data;
|
||||
GSList *current = l;
|
||||
|
||||
l = l->next;
|
||||
if (timeout->dev == dev) {
|
||||
g_free (timeout);
|
||||
active_timers = g_slist_delete_link (active_timers, current);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __FPI_POLL_H__
|
||||
#define __FPI_POLL_H__
|
||||
|
||||
#include "fprint.h"
|
||||
|
||||
/**
|
||||
* fpi_timeout_fn:
|
||||
* @dev: the struct #fp_dev passed to fpi_timeout_add()
|
||||
* @data: the data passed to fpi_timeout_add()
|
||||
*
|
||||
* The prototype of the callback function for fpi_timeout_add().
|
||||
* Note that after the callback is called, the #fpi_timeout structure will
|
||||
* be freed.
|
||||
*/
|
||||
typedef void (*fpi_timeout_fn)(struct fp_dev *dev, void *data);
|
||||
|
||||
/**
|
||||
* fpi_timeout:
|
||||
*
|
||||
* An opaque structure representing a scheduled function call, created with
|
||||
* fpi_timeout_add().
|
||||
*/
|
||||
typedef struct fpi_timeout fpi_timeout;
|
||||
fpi_timeout *fpi_timeout_add(unsigned int msec,
|
||||
fpi_timeout_fn callback,
|
||||
struct fp_dev *dev,
|
||||
void *data);
|
||||
void fpi_timeout_set_name(fpi_timeout *timeout,
|
||||
const char *name);
|
||||
void fpi_timeout_cancel(fpi_timeout *timeout);
|
||||
|
||||
#endif
|
50
libfprint/fpi-print.h
Normal file
50
libfprint/fpi-print.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include "fpi-enums.h"
|
||||
#include "fp-device.h"
|
||||
#include "fp-print.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* FpPrintType:
|
||||
* @FP_PRINT_UNDEFINED: Undefined type, this happens prior to enrollment
|
||||
* @FP_PRINT_RAW: A raw print where the data is directly compared
|
||||
* @FP_PRINT_NBIS: NBIS minutiae comparison
|
||||
*/
|
||||
typedef enum {
|
||||
FP_PRINT_UNDEFINED = 0,
|
||||
FP_PRINT_RAW,
|
||||
FP_PRINT_NBIS,
|
||||
} FpPrintType;
|
||||
|
||||
/**
|
||||
* FpiMatchResult:
|
||||
* @FPI_MATCH_ERROR: An error occured during matching
|
||||
* @FPI_MATCH_SUCCESS: The prints matched
|
||||
* @FPI_MATCH_FAIL: The prints did not match
|
||||
*/
|
||||
typedef enum {
|
||||
FPI_MATCH_ERROR = 0,
|
||||
FPI_MATCH_SUCCESS,
|
||||
FPI_MATCH_FAIL,
|
||||
} FpiMatchResult;
|
||||
|
||||
void fpi_print_add_print (FpPrint *print,
|
||||
FpPrint *add);
|
||||
|
||||
void fpi_print_set_type (FpPrint *print,
|
||||
FpPrintType type);
|
||||
void fpi_print_set_device_stored (FpPrint *print,
|
||||
gboolean device_stored);
|
||||
|
||||
gboolean fpi_print_add_from_image (FpPrint *print,
|
||||
FpImage *image,
|
||||
GError **error);
|
||||
|
||||
FpiMatchResult fpi_print_bz3_match (FpPrint * template,
|
||||
FpPrint *print,
|
||||
gint bz3_threshold,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Functions to assist with asynchronous driver <---> library communications
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -17,13 +18,11 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "drv"
|
||||
#define FP_COMPONENT "SSM"
|
||||
|
||||
#include "fp_internal.h"
|
||||
#include "drivers_api.h"
|
||||
#include "fpi-ssm.h"
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
|
||||
/**
|
||||
* SECTION:fpi-ssm
|
||||
|
@ -32,7 +31,7 @@
|
|||
*
|
||||
* Asynchronous driver design encourages some kind of state machine behind it.
|
||||
* In most cases, the state machine is entirely linear - you only go to the
|
||||
* next state, you never jump or go backwards. The #fpi_ssm functions help you
|
||||
* next state, you never jump or go backwards. The #FpiSsm functions help you
|
||||
* implement such a machine.
|
||||
*
|
||||
* e.g. `S1` ↦ `S2` ↦ `S3` ↦ `S4`
|
||||
|
@ -72,20 +71,21 @@
|
|||
* upon success (or fails).
|
||||
*
|
||||
* Your completion callback should examine the return value of
|
||||
* fpi_ssm_get_error() in order to determine whether the #fpi_ssm completed or
|
||||
* fpi_ssm_get_error() in ordater to determine whether the #FpiSsm completed or
|
||||
* failed. An error code of zero indicates successful completion.
|
||||
*/
|
||||
|
||||
struct fpi_ssm {
|
||||
struct fp_dev *dev;
|
||||
fpi_ssm *parentsm;
|
||||
void *user_data;
|
||||
int nr_states;
|
||||
int cur_state;
|
||||
gboolean completed;
|
||||
int error;
|
||||
ssm_completed_fn callback;
|
||||
ssm_handler_fn handler;
|
||||
struct _FpiSsm
|
||||
{
|
||||
FpDevice *dev;
|
||||
FpiSsm *parentsm;
|
||||
void *user_data;
|
||||
int nr_states;
|
||||
int cur_state;
|
||||
gboolean completed;
|
||||
GError *error;
|
||||
FpiSsmCompletedCallback callback;
|
||||
FpiSsmHandlerCallback handler;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -98,28 +98,30 @@ struct fpi_ssm {
|
|||
* Allocate a new ssm, with @nr_states states. The @handler callback
|
||||
* will be called after each state transition.
|
||||
*
|
||||
* Returns: a new #fpi_ssm state machine
|
||||
* Returns: a new #FpiSsm state machine
|
||||
*/
|
||||
fpi_ssm *fpi_ssm_new(struct fp_dev *dev,
|
||||
ssm_handler_fn handler,
|
||||
int nr_states,
|
||||
void *user_data)
|
||||
FpiSsm *
|
||||
fpi_ssm_new (FpDevice *dev,
|
||||
FpiSsmHandlerCallback handler,
|
||||
int nr_states,
|
||||
void *user_data)
|
||||
{
|
||||
fpi_ssm *machine;
|
||||
BUG_ON(nr_states < 1);
|
||||
FpiSsm *machine;
|
||||
|
||||
machine = g_malloc0(sizeof(*machine));
|
||||
machine->handler = handler;
|
||||
machine->nr_states = nr_states;
|
||||
machine->dev = dev;
|
||||
machine->completed = TRUE;
|
||||
machine->user_data = user_data;
|
||||
return machine;
|
||||
BUG_ON (nr_states < 1);
|
||||
|
||||
machine = g_malloc0 (sizeof (*machine));
|
||||
machine->handler = handler;
|
||||
machine->nr_states = nr_states;
|
||||
machine->dev = dev;
|
||||
machine->completed = TRUE;
|
||||
machine->user_data = user_data;
|
||||
return machine;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_get_user_data:
|
||||
* @machine: an #fpi_ssm state machine
|
||||
* @machine: an #FpiSsm state machine
|
||||
*
|
||||
* Retrieve the pointer to user data set when fpi_ssm_new()
|
||||
* is called.
|
||||
|
@ -127,66 +129,73 @@ fpi_ssm *fpi_ssm_new(struct fp_dev *dev,
|
|||
* Returns: a pointer
|
||||
*/
|
||||
void *
|
||||
fpi_ssm_get_user_data(fpi_ssm *machine)
|
||||
fpi_ssm_get_user_data (FpiSsm *machine)
|
||||
{
|
||||
return machine->user_data;
|
||||
return machine->user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_free:
|
||||
* @machine: an #fpi_ssm state machine
|
||||
* @machine: an #FpiSsm state machine
|
||||
*
|
||||
* Frees a state machine. This does not call any error or success
|
||||
* callbacks, so you need to do this yourself.
|
||||
*/
|
||||
void fpi_ssm_free(fpi_ssm *machine)
|
||||
void
|
||||
fpi_ssm_free (FpiSsm *machine)
|
||||
{
|
||||
if (!machine)
|
||||
return;
|
||||
g_free(machine);
|
||||
if (!machine)
|
||||
return;
|
||||
|
||||
g_clear_pointer (&machine->error, g_error_free);
|
||||
g_free (machine);
|
||||
}
|
||||
|
||||
/* Invoke the state handler */
|
||||
static void __ssm_call_handler(fpi_ssm *machine)
|
||||
static void
|
||||
__ssm_call_handler (FpiSsm *machine)
|
||||
{
|
||||
fp_dbg("%p entering state %d", machine, machine->cur_state);
|
||||
machine->handler(machine, machine->dev, machine->user_data);
|
||||
fp_dbg ("%p entering state %d", machine, machine->cur_state);
|
||||
machine->handler (machine, machine->dev, machine->user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_start:
|
||||
* @ssm: an #fpi_ssm state machine
|
||||
* @callback: the #ssm_completed_fn callback to call on completion
|
||||
* @ssm: an #FpiSsm state machine
|
||||
* @callback: the #FpiSsmCompletedCallback callback to call on completion
|
||||
*
|
||||
* Starts a state machine. You can also use this function to restart
|
||||
* a completed or failed state machine. The @callback will be called
|
||||
* on completion.
|
||||
*/
|
||||
void fpi_ssm_start(fpi_ssm *ssm, ssm_completed_fn callback)
|
||||
void
|
||||
fpi_ssm_start (FpiSsm *ssm, FpiSsmCompletedCallback callback)
|
||||
{
|
||||
BUG_ON(!ssm->completed);
|
||||
ssm->callback = callback;
|
||||
ssm->cur_state = 0;
|
||||
ssm->completed = FALSE;
|
||||
ssm->error = 0;
|
||||
__ssm_call_handler(ssm);
|
||||
BUG_ON (!ssm->completed);
|
||||
ssm->callback = callback;
|
||||
ssm->cur_state = 0;
|
||||
ssm->completed = FALSE;
|
||||
ssm->error = NULL;
|
||||
__ssm_call_handler (ssm);
|
||||
}
|
||||
|
||||
static void __subsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
static void
|
||||
__subsm_complete (FpiSsm *ssm, FpDevice *_dev, void *user_data, GError *error)
|
||||
{
|
||||
fpi_ssm *parent = ssm->parentsm;
|
||||
BUG_ON(!parent);
|
||||
if (ssm->error)
|
||||
fpi_ssm_mark_failed(parent, ssm->error);
|
||||
else
|
||||
fpi_ssm_next_state(parent);
|
||||
fpi_ssm_free(ssm);
|
||||
FpiSsm *parent = ssm->parentsm;
|
||||
|
||||
BUG_ON (!parent);
|
||||
if (error)
|
||||
fpi_ssm_mark_failed (parent, error);
|
||||
else
|
||||
fpi_ssm_next_state (parent);
|
||||
fpi_ssm_free (ssm);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_start_subsm:
|
||||
* @parent: an #fpi_ssm state machine
|
||||
* @child: an #fpi_ssm state machine
|
||||
* @parent: an #FpiSsm state machine
|
||||
* @child: an #FpiSsm state machine
|
||||
*
|
||||
* Starts a state machine as a child of another. if the child completes
|
||||
* successfully, the parent will be advanced to the next state. if the
|
||||
|
@ -194,124 +203,185 @@ static void __subsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
|||
*
|
||||
* The child will be automatically freed upon completion or failure.
|
||||
*/
|
||||
void fpi_ssm_start_subsm(fpi_ssm *parent, fpi_ssm *child)
|
||||
void
|
||||
fpi_ssm_start_subsm (FpiSsm *parent, FpiSsm *child)
|
||||
{
|
||||
child->parentsm = parent;
|
||||
fpi_ssm_start(child, __subsm_complete);
|
||||
child->parentsm = parent;
|
||||
fpi_ssm_start (child, __subsm_complete);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_mark_completed:
|
||||
* @machine: an #fpi_ssm state machine
|
||||
* @machine: an #FpiSsm state machine
|
||||
*
|
||||
* Mark a ssm as completed successfully. The callback set when creating
|
||||
* the state machine with fpi_ssm_new() will be called synchronously.
|
||||
*/
|
||||
void fpi_ssm_mark_completed(fpi_ssm *machine)
|
||||
void
|
||||
fpi_ssm_mark_completed (FpiSsm *machine)
|
||||
{
|
||||
BUG_ON(machine->completed);
|
||||
machine->completed = TRUE;
|
||||
fp_dbg("%p completed with status %d", machine, machine->error);
|
||||
if (machine->callback)
|
||||
machine->callback(machine, machine->dev, machine->user_data);
|
||||
BUG_ON (machine->completed);
|
||||
machine->completed = TRUE;
|
||||
if (machine->error)
|
||||
fp_dbg ("%p completed with error: %s", machine, machine->error->message);
|
||||
else
|
||||
fp_dbg ("%p completed successfully", machine);
|
||||
if (machine->callback)
|
||||
{
|
||||
GError *error = machine->error ? g_error_copy (machine->error) : NULL;
|
||||
|
||||
machine->callback (machine, machine->dev, machine->user_data, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_mark_failed:
|
||||
* @machine: an #fpi_ssm state machine
|
||||
* @error: the error code
|
||||
* @machine: an #FpiSsm state machine
|
||||
* @error: a #GError
|
||||
*
|
||||
* Mark a state machine as failed with @error as the error code.
|
||||
*/
|
||||
void fpi_ssm_mark_failed(fpi_ssm *machine, int error)
|
||||
void
|
||||
fpi_ssm_mark_failed (FpiSsm *machine, GError *error)
|
||||
{
|
||||
fp_dbg("error %d from state %d", error, machine->cur_state);
|
||||
BUG_ON(error == 0);
|
||||
machine->error = error;
|
||||
fpi_ssm_mark_completed(machine);
|
||||
g_assert (error);
|
||||
if (machine->error)
|
||||
{
|
||||
fp_warn ("SSM already has an error set, ignoring new error %s", error->message);
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
|
||||
fp_dbg ("SSM failed in state %d with error: %s", machine->cur_state, error->message);
|
||||
machine->error = error;
|
||||
fpi_ssm_mark_completed (machine);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_next_state:
|
||||
* @machine: an #fpi_ssm state machine
|
||||
* @machine: an #FpiSsm state machine
|
||||
*
|
||||
* Iterate to next state of a state machine. If the current state is the
|
||||
* last state, then the state machine will be marked as completed, as
|
||||
* if calling fpi_ssm_mark_completed().
|
||||
*/
|
||||
void fpi_ssm_next_state(fpi_ssm *machine)
|
||||
{
|
||||
g_return_if_fail (machine != NULL);
|
||||
|
||||
BUG_ON(machine->completed);
|
||||
machine->cur_state++;
|
||||
if (machine->cur_state == machine->nr_states) {
|
||||
fpi_ssm_mark_completed(machine);
|
||||
} else {
|
||||
__ssm_call_handler(machine);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_next_state_timeout_cb:
|
||||
* @dev: a struct #fp_dev
|
||||
* @data: a pointer to an #fpi_ssm state machine
|
||||
*
|
||||
* Same as fpi_ssm_next_state(), but to be used as a callback
|
||||
* for an fpi_timeout_add() callback, when the state change needs
|
||||
* to happen after a timeout.
|
||||
*
|
||||
* Make sure to pass the #fpi_ssm as the `user_data` argument
|
||||
* for that fpi_timeout_add() call.
|
||||
*/
|
||||
void
|
||||
fpi_ssm_next_state_timeout_cb(struct fp_dev *dev,
|
||||
void *data)
|
||||
fpi_ssm_next_state (FpiSsm *machine)
|
||||
{
|
||||
g_return_if_fail (dev != NULL);
|
||||
g_return_if_fail (data != NULL);
|
||||
g_return_if_fail (machine != NULL);
|
||||
|
||||
fpi_ssm_next_state(data);
|
||||
BUG_ON (machine->completed);
|
||||
machine->cur_state++;
|
||||
if (machine->cur_state == machine->nr_states)
|
||||
fpi_ssm_mark_completed (machine);
|
||||
else
|
||||
__ssm_call_handler (machine);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_jump_to_state:
|
||||
* @machine: an #fpi_ssm state machine
|
||||
* @machine: an #FpiSsm state machine
|
||||
* @state: the state to jump to
|
||||
*
|
||||
* Jump to the @state state, bypassing intermediary states.
|
||||
*/
|
||||
void fpi_ssm_jump_to_state(fpi_ssm *machine, int state)
|
||||
void
|
||||
fpi_ssm_jump_to_state (FpiSsm *machine, int state)
|
||||
{
|
||||
BUG_ON(machine->completed);
|
||||
BUG_ON(state >= machine->nr_states);
|
||||
machine->cur_state = state;
|
||||
__ssm_call_handler(machine);
|
||||
BUG_ON (machine->completed);
|
||||
BUG_ON (state >= machine->nr_states);
|
||||
machine->cur_state = state;
|
||||
__ssm_call_handler (machine);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_get_cur_state:
|
||||
* @machine: an #fpi_ssm state machine
|
||||
* @machine: an #FpiSsm state machine
|
||||
*
|
||||
* Returns the value of the current state. Note that states are
|
||||
* 0-indexed, so a value of 0 means “the first state”.
|
||||
*
|
||||
* Returns: the current state.
|
||||
*/
|
||||
int fpi_ssm_get_cur_state(fpi_ssm *machine)
|
||||
int
|
||||
fpi_ssm_get_cur_state (FpiSsm *machine)
|
||||
{
|
||||
return machine->cur_state;
|
||||
return machine->cur_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_get_error:
|
||||
* @machine: an #fpi_ssm state machine
|
||||
* @machine: an #FpiSsm state machine
|
||||
*
|
||||
* Returns the error code set by fpi_ssm_mark_failed().
|
||||
*
|
||||
* Returns: a error code
|
||||
* Returns: (transfer none): a error code
|
||||
*/
|
||||
int fpi_ssm_get_error(fpi_ssm *machine)
|
||||
GError *
|
||||
fpi_ssm_get_error (FpiSsm *machine)
|
||||
{
|
||||
return machine->error;
|
||||
return machine->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_dup_error:
|
||||
* @machine: an #FpiSsm state machine
|
||||
*
|
||||
* Returns the error code set by fpi_ssm_mark_failed().
|
||||
*
|
||||
* Returns: (transfer full): a error code
|
||||
*/
|
||||
GError *
|
||||
fpi_ssm_dup_error (FpiSsm *machine)
|
||||
{
|
||||
if (machine->error)
|
||||
return g_error_copy (machine->error);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_next_state_timeout_cb:
|
||||
* @dev: a struct #fp_dev
|
||||
* @data: a pointer to an #FpiSsm state machine
|
||||
*
|
||||
* Same as fpi_ssm_next_state(), but to be used as a callback
|
||||
* for an fpi_timeout_add() callback, when the state change needs
|
||||
* to happen after a timeout.
|
||||
*
|
||||
* Make sure to pass the #FpiSsm as the `user_data` argument
|
||||
* for that fpi_timeout_add() call.
|
||||
*/
|
||||
void
|
||||
fpi_ssm_next_state_timeout_cb (FpDevice *dev,
|
||||
void *data)
|
||||
{
|
||||
g_return_if_fail (dev != NULL);
|
||||
g_return_if_fail (data != NULL);
|
||||
|
||||
fpi_ssm_next_state (data);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_ssm_usb_transfer_cb:
|
||||
* @transfer: a #FpiUsbTransfer
|
||||
* @device: a #FpDevice
|
||||
* @user_data: User data (unused)
|
||||
* @error: The #GError or %NULL
|
||||
*
|
||||
* Can be used in as a #FpiUsbTransfer callback handler to automatically
|
||||
* advance or fail a statemachine on transfer completion.
|
||||
*
|
||||
* Make sure to set the #FpiSsm on the transfer.
|
||||
*/
|
||||
void
|
||||
fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device,
|
||||
gpointer user_data, GError *error)
|
||||
{
|
||||
g_return_if_fail (transfer->ssm);
|
||||
|
||||
if (error)
|
||||
fpi_ssm_mark_failed (transfer->ssm, error);
|
||||
else
|
||||
fpi_ssm_next_state (transfer->ssm);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -17,70 +18,79 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __FPI_SSM_H__
|
||||
#define __FPI_SSM_H__
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
#include "fp-device.h"
|
||||
#include "fpi-usb-transfer.h"
|
||||
|
||||
/* async drv <--> lib comms */
|
||||
|
||||
/**
|
||||
* fpi_ssm:
|
||||
* FpiSsm:
|
||||
*
|
||||
* Sequential state machine that iterates sequentially over
|
||||
* a predefined series of states. Can be terminated by either completion or
|
||||
* failure error conditions.
|
||||
*/
|
||||
typedef struct fpi_ssm fpi_ssm;
|
||||
typedef struct _FpiSsm FpiSsm;
|
||||
|
||||
/**
|
||||
* ssm_completed_fn:
|
||||
* @ssm: a #fpi_ssm state machine
|
||||
* FpiSsmCompletedCallback:
|
||||
* @ssm: a #FpiSsm state machine
|
||||
* @dev: the #fp_dev fingerprint device
|
||||
* @user_data: the user data passed to fpi_ssm_new()
|
||||
* @error: The #GError or %NULL on successful completion
|
||||
*
|
||||
* The callback called when a state machine completes successfully,
|
||||
* as set when calling fpi_ssm_start().
|
||||
*/
|
||||
typedef void (*ssm_completed_fn)(fpi_ssm *ssm,
|
||||
struct fp_dev *dev,
|
||||
void *user_data);
|
||||
typedef void (*FpiSsmCompletedCallback)(FpiSsm *ssm,
|
||||
FpDevice *dev,
|
||||
void *user_data,
|
||||
GError *error);
|
||||
|
||||
/**
|
||||
* ssm_handler_fn:
|
||||
* @ssm: a #fpi_ssm state machine
|
||||
* FpiSsmHandlerCallback:
|
||||
* @ssm: a #FpiSsm state machine
|
||||
* @dev: the #fp_dev fingerprint device
|
||||
* @user_data: the user data passed to fpi_ssm_new()
|
||||
*
|
||||
* The callback called when a state machine transitions from one
|
||||
* state to the next, as set when calling fpi_ssm_new().
|
||||
*/
|
||||
typedef void (*ssm_handler_fn)(fpi_ssm *ssm,
|
||||
struct fp_dev *dev,
|
||||
void *user_data);
|
||||
typedef void (*FpiSsmHandlerCallback)(FpiSsm *ssm,
|
||||
FpDevice *dev,
|
||||
void *user_data);
|
||||
|
||||
/* for library and drivers */
|
||||
fpi_ssm *fpi_ssm_new(struct fp_dev *dev,
|
||||
ssm_handler_fn handler,
|
||||
int nr_states,
|
||||
void *user_data);
|
||||
void fpi_ssm_free(fpi_ssm *machine);
|
||||
void fpi_ssm_start(fpi_ssm *ssm, ssm_completed_fn callback);
|
||||
void fpi_ssm_start_subsm(fpi_ssm *parent, fpi_ssm *child);
|
||||
FpiSsm *fpi_ssm_new (FpDevice *dev,
|
||||
FpiSsmHandlerCallback handler,
|
||||
int nr_states,
|
||||
void *user_data);
|
||||
void fpi_ssm_free (FpiSsm *machine);
|
||||
void fpi_ssm_start (FpiSsm *ssm,
|
||||
FpiSsmCompletedCallback callback);
|
||||
void fpi_ssm_start_subsm (FpiSsm *parent,
|
||||
FpiSsm *child);
|
||||
|
||||
/* for drivers */
|
||||
void fpi_ssm_next_state(fpi_ssm *machine);
|
||||
void fpi_ssm_next_state_timeout_cb(struct fp_dev *dev, void *data);
|
||||
void fpi_ssm_jump_to_state(fpi_ssm *machine, int state);
|
||||
void fpi_ssm_mark_completed(fpi_ssm *machine);
|
||||
void fpi_ssm_mark_failed(fpi_ssm *machine, int error);
|
||||
void *fpi_ssm_get_user_data(fpi_ssm *machine);
|
||||
int fpi_ssm_get_error(fpi_ssm *machine);
|
||||
int fpi_ssm_get_cur_state(fpi_ssm *machine);
|
||||
void fpi_ssm_next_state (FpiSsm *machine);
|
||||
void fpi_ssm_jump_to_state (FpiSsm *machine,
|
||||
int state);
|
||||
void fpi_ssm_mark_completed (FpiSsm *machine);
|
||||
void fpi_ssm_mark_failed (FpiSsm *machine,
|
||||
GError *error);
|
||||
void *fpi_ssm_get_user_data (FpiSsm *machine);
|
||||
GError * fpi_ssm_get_error (FpiSsm *machine);
|
||||
GError * fpi_ssm_dup_error (FpiSsm *machine);
|
||||
int fpi_ssm_get_cur_state (FpiSsm *machine);
|
||||
|
||||
#endif
|
||||
/* Callbacks to be used by the driver instead of implementing their own
|
||||
* logic.
|
||||
*/
|
||||
void fpi_ssm_next_state_timeout_cb (FpDevice *dev,
|
||||
void *data);
|
||||
void fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer,
|
||||
FpDevice *device,
|
||||
gpointer user_data,
|
||||
GError *error);
|
||||
|
|
|
@ -1,690 +0,0 @@
|
|||
/*
|
||||
* Synchronous I/O functionality
|
||||
* Copyright (C) 2007-2008 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
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "sync"
|
||||
|
||||
#include "fp_internal.h"
|
||||
#include "fpi-dev.h"
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
|
||||
struct sync_open_data {
|
||||
struct fp_dev *dev;
|
||||
int status;
|
||||
};
|
||||
|
||||
static void sync_open_cb(struct fp_dev *dev, int status, void *user_data)
|
||||
{
|
||||
struct sync_open_data *odata = user_data;
|
||||
fp_dbg("status %d", status);
|
||||
odata->dev = dev;
|
||||
odata->status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_open:
|
||||
* @ddev: the struct #fp_dscv_dev discovered device to open
|
||||
*
|
||||
* Opens and initialises a device. This is the function you call in order
|
||||
* to convert a #fp_dscv_dev discovered device into an actual device handle
|
||||
* that you can perform operations with.
|
||||
*
|
||||
* Returns: the opened device handle, or %NULL on error
|
||||
*/
|
||||
API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev)
|
||||
{
|
||||
struct fp_dev *dev = NULL;
|
||||
struct sync_open_data *odata = g_malloc0(sizeof(*odata));
|
||||
int r;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
r = fp_async_dev_open(ddev, sync_open_cb, odata);
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
while (!odata->dev)
|
||||
if (fp_handle_events() < 0)
|
||||
goto out;
|
||||
|
||||
if (odata->status == 0)
|
||||
dev = odata->dev;
|
||||
else
|
||||
fp_dev_close(odata->dev);
|
||||
|
||||
out:
|
||||
g_free(odata);
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void sync_close_cb(struct fp_dev *dev, void *user_data)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
gboolean *closed = user_data;
|
||||
*closed = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_dev_close:
|
||||
* @dev: the struct #fp_dev device to close. If %NULL, function simply returns
|
||||
*
|
||||
* Closes a device. You must call this function when you have finished using
|
||||
* a fingerprint device.
|
||||
*/
|
||||
API_EXPORTED void fp_dev_close(struct fp_dev *dev)
|
||||
{
|
||||
gboolean closed = FALSE;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
fp_async_dev_close(dev, sync_close_cb, &closed);
|
||||
while (!closed)
|
||||
if (fp_handle_events() < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
struct sync_enroll_data {
|
||||
gboolean populated;
|
||||
int result;
|
||||
struct fp_print_data *data;
|
||||
struct fp_img *img;
|
||||
};
|
||||
|
||||
static void sync_enroll_cb(struct fp_dev *dev, int result,
|
||||
struct fp_print_data *data, struct fp_img *img, void *user_data)
|
||||
{
|
||||
struct sync_enroll_data *edata = user_data;
|
||||
fp_dbg("result %d", result);
|
||||
edata->result = result;
|
||||
edata->data = data;
|
||||
edata->img = img;
|
||||
edata->populated = TRUE;
|
||||
}
|
||||
|
||||
static void enroll_stop_cb(struct fp_dev *dev, void *user_data)
|
||||
{
|
||||
gboolean *stopped = user_data;
|
||||
G_DEBUG_HERE();
|
||||
*stopped = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_enroll_finger_img:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @print_data: a location to return the resultant enrollment data from
|
||||
* the final stage. Must be freed with fp_print_data_free() after use
|
||||
* @img: location to store the scan image. accepts %NULL for no image
|
||||
* storage. If an image is returned, it must be freed with fp_img_free() after
|
||||
* use
|
||||
*
|
||||
* Performs an enroll stage. See [Enrolling](libfprint-Devices-operations.html#enrolling)
|
||||
* for an explanation of enroll stages.
|
||||
*
|
||||
* If no enrollment is in process, this kicks of the process and runs the
|
||||
* first stage. If an enrollment is already in progress, calling this
|
||||
* function runs the next stage, which may well be the last.
|
||||
*
|
||||
* A negative error code may be returned from any stage. When this occurs,
|
||||
* further calls to the enroll function will start a new enrollment process,
|
||||
* i.e. a negative error code indicates that the enrollment process has been
|
||||
* aborted. These error codes only ever indicate unexpected internal errors
|
||||
* or I/O problems.
|
||||
*
|
||||
* The RETRY codes from #fp_enroll_result may be returned from any enroll
|
||||
* stage. These codes indicate that the scan was not successful in that the
|
||||
* user did not position their finger correctly or similar. When a RETRY code
|
||||
* is returned, the enrollment stage is <emphasis role="strong">not</emphasis> advanced, so the next call
|
||||
* into this function will retry the current stage again. The current stage may
|
||||
* need to be retried several times.
|
||||
*
|
||||
* The %FP_ENROLL_FAIL code may be returned from any enroll
|
||||
* stage. This code indicates that even though the scans themselves have been
|
||||
* acceptable, data processing applied to these scans produces incomprehensible
|
||||
* results. In other words, the user may have been scanning a different finger
|
||||
* for each stage or something like that. Like negative error codes, this
|
||||
* return code indicates that the enrollment process has been aborted.
|
||||
*
|
||||
* The %FP_ENROLL_PASS code will only ever be returned for
|
||||
* non-final stages. This return code indicates that the scan was acceptable
|
||||
* and the next call into this function will advance onto the next enroll
|
||||
* stage.
|
||||
*
|
||||
* The %FP_ENROLL_COMPLETE code will only ever be returned
|
||||
* from the final enroll stage. It indicates that enrollment completed
|
||||
* successfully, and that print_data has been assigned to point to the
|
||||
* resultant enrollment data. The print_data parameter will not be modified
|
||||
* during any other enrollment stages, hence it is actually legal to pass NULL
|
||||
* as this argument for all but the final stage.
|
||||
*
|
||||
* If the device is an imaging device, it can also return the image from
|
||||
* the scan, even when the enroll fails with a RETRY or FAIL code. It is legal
|
||||
* to call this function even on non-imaging devices, just don't expect them to
|
||||
* provide images.
|
||||
*
|
||||
* Returns: negative code on error, otherwise a code from #fp_enroll_result
|
||||
*/
|
||||
API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev,
|
||||
struct fp_print_data **print_data, struct fp_img **img)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int stage = dev->__enroll_stage;
|
||||
gboolean final = FALSE;
|
||||
gboolean stopped = FALSE;
|
||||
struct sync_enroll_data *edata = NULL;
|
||||
int r;
|
||||
G_DEBUG_HERE();
|
||||
|
||||
/* FIXME __enroll_stage is ugly, can we replace it by some function that
|
||||
* says whether we're enrolling or not, and then put __enroll_stage into
|
||||
* edata? */
|
||||
|
||||
if (stage == -1) {
|
||||
edata = g_malloc0(sizeof(struct sync_enroll_data));
|
||||
r = fp_async_enroll_start(dev, sync_enroll_cb, edata);
|
||||
if (r < 0) {
|
||||
g_free(edata);
|
||||
return r;
|
||||
}
|
||||
|
||||
dev->__enroll_stage = ++stage;
|
||||
} else if (stage >= dev->nr_enroll_stages) {
|
||||
fp_err("exceeding number of enroll stages for device claimed by "
|
||||
"driver %s (%d stages)", drv->name, dev->nr_enroll_stages);
|
||||
dev->__enroll_stage = -1;
|
||||
r = -EINVAL;
|
||||
final = TRUE;
|
||||
goto out;
|
||||
}
|
||||
fp_dbg("%s will handle enroll stage %d/%d", drv->name, stage,
|
||||
dev->nr_enroll_stages - 1);
|
||||
|
||||
/* FIXME this isn't very clean */
|
||||
edata = dev->enroll_stage_cb_data;
|
||||
|
||||
while (!edata->populated) {
|
||||
r = fp_handle_events();
|
||||
if (r < 0) {
|
||||
g_free(edata);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
edata->populated = FALSE;
|
||||
|
||||
if (img)
|
||||
*img = edata->img;
|
||||
else
|
||||
fp_img_free(edata->img);
|
||||
|
||||
r = edata->result;
|
||||
switch (r) {
|
||||
case FP_ENROLL_PASS:
|
||||
fp_dbg("enroll stage passed");
|
||||
dev->__enroll_stage = stage + 1;
|
||||
break;
|
||||
case FP_ENROLL_COMPLETE:
|
||||
fp_dbg("enroll complete");
|
||||
dev->__enroll_stage = -1;
|
||||
*print_data = edata->data;
|
||||
final = TRUE;
|
||||
break;
|
||||
case FP_ENROLL_RETRY:
|
||||
fp_dbg("enroll should retry");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_TOO_SHORT:
|
||||
fp_dbg("swipe was too short, enroll should retry");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_CENTER_FINGER:
|
||||
fp_dbg("finger was not centered, enroll should retry");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_REMOVE_FINGER:
|
||||
fp_dbg("scan failed, remove finger and retry");
|
||||
break;
|
||||
case FP_ENROLL_FAIL:
|
||||
fp_err("enroll failed");
|
||||
dev->__enroll_stage = -1;
|
||||
final = TRUE;
|
||||
break;
|
||||
default:
|
||||
fp_err("unrecognised return code %d", r);
|
||||
dev->__enroll_stage = -1;
|
||||
r = -EINVAL;
|
||||
final = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!final)
|
||||
return r;
|
||||
|
||||
out:
|
||||
if (final) {
|
||||
fp_dbg("ending enrollment");
|
||||
g_free(edata);
|
||||
}
|
||||
|
||||
err:
|
||||
if (fp_async_enroll_stop(dev, enroll_stop_cb, &stopped) == 0)
|
||||
while (!stopped)
|
||||
if (fp_handle_events() < 0)
|
||||
break;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_enroll_finger:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @print_data: a location to return the resultant enrollment data from
|
||||
* the final stage. Must be freed with fp_print_data_free() after use
|
||||
*
|
||||
* Performs an enroll stage. See [Enrolling](libfprint-Devices-operations.html#enrolling)
|
||||
* for an explanation of enroll stages. This function is just a shortcut to
|
||||
* calling fp_enroll_finger_img() with a %NULL image parameter. Be sure to read
|
||||
* the description of fp_enroll_finger_img() in order to understand its behaviour.
|
||||
*
|
||||
* Returns: negative code on error, otherwise a code from #fp_enroll_result
|
||||
*/
|
||||
API_EXPORTED int fp_enroll_finger(struct fp_dev *dev,
|
||||
struct fp_print_data **print_data)
|
||||
{
|
||||
return fp_enroll_finger_img(dev, print_data, NULL);
|
||||
}
|
||||
|
||||
struct sync_verify_data {
|
||||
gboolean populated;
|
||||
int result;
|
||||
struct fp_img *img;
|
||||
};
|
||||
|
||||
static void sync_verify_cb(struct fp_dev *dev, int result, struct fp_img *img,
|
||||
void *user_data)
|
||||
{
|
||||
struct sync_verify_data *vdata = user_data;
|
||||
vdata->result = result;
|
||||
vdata->img = img;
|
||||
vdata->populated = TRUE;
|
||||
}
|
||||
|
||||
static void verify_stop_cb(struct fp_dev *dev, void *user_data)
|
||||
{
|
||||
gboolean *stopped = user_data;
|
||||
G_DEBUG_HERE();
|
||||
*stopped = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_verify_finger_img:
|
||||
* @dev: the struct #fp_dev device to perform the scan on
|
||||
* @enrolled_print: the print to verify against. Must have been previously
|
||||
* enrolled with a device compatible to the device selected to perform the scan
|
||||
* @img: location to store the scan image. accepts %NULL for no image
|
||||
* storage. If an image is returned, it must be freed with fp_img_free() after
|
||||
* use
|
||||
|
||||
* Performs a new scan and verifies it against a previously enrolled print.
|
||||
* If the device is an imaging device, it can also return the image from
|
||||
* the scan, even when the verify fails with a RETRY code. It is legal to
|
||||
* call this function even on non-imaging devices, just don't expect them to
|
||||
* provide images.
|
||||
*
|
||||
* Returns: negative code on error, otherwise a code from #fp_verify_result
|
||||
*/
|
||||
API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
|
||||
struct fp_print_data *enrolled_print, struct fp_img **img)
|
||||
{
|
||||
struct sync_verify_data *vdata;
|
||||
gboolean stopped = FALSE;
|
||||
int r;
|
||||
|
||||
if (!enrolled_print) {
|
||||
fp_err("no print given");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!fp_dev_supports_print_data(dev, enrolled_print)) {
|
||||
fp_err("print is not compatible with device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fp_dbg("to be handled by %s", dev->drv->name);
|
||||
vdata = g_malloc0(sizeof(struct sync_verify_data));
|
||||
r = fp_async_verify_start(dev, enrolled_print, sync_verify_cb, vdata);
|
||||
if (r < 0) {
|
||||
fp_dbg("verify_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_VERIFY_NO_MATCH:
|
||||
fp_dbg("result: no match");
|
||||
break;
|
||||
case FP_VERIFY_MATCH:
|
||||
fp_dbg("result: match");
|
||||
break;
|
||||
case FP_VERIFY_RETRY:
|
||||
fp_dbg("verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_TOO_SHORT:
|
||||
fp_dbg("swipe was too short, verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_CENTER_FINGER:
|
||||
fp_dbg("finger was not centered, verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_REMOVE_FINGER:
|
||||
fp_dbg("scan failed, remove finger and retry");
|
||||
break;
|
||||
default:
|
||||
fp_err("unrecognised return code %d", r);
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
||||
err:
|
||||
fp_dbg("ending verification");
|
||||
if (fp_async_verify_stop(dev, verify_stop_cb, &stopped) == 0)
|
||||
while (!stopped)
|
||||
if (fp_handle_events() < 0)
|
||||
break;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_verify_finger:
|
||||
* @dev: the struct #fp_dev device to perform the scan on
|
||||
* @enrolled_print: the print to verify against. Must have been previously
|
||||
* enrolled with a device compatible to the device selected to perform the scan
|
||||
*
|
||||
* Performs a new scan and verify it against a previously enrolled print. This
|
||||
* function is just a shortcut to calling fp_verify_finger_img() with a NULL
|
||||
* image output parameter.
|
||||
*
|
||||
* See also fp_verify_finger_img().
|
||||
*
|
||||
* Returns: negative code on error, otherwise a code from #fp_verify_result
|
||||
*/
|
||||
API_EXPORTED int fp_verify_finger(struct fp_dev *dev,
|
||||
struct fp_print_data *enrolled_print)
|
||||
{
|
||||
return fp_verify_finger_img(dev, enrolled_print, NULL);
|
||||
}
|
||||
|
||||
struct sync_identify_data {
|
||||
gboolean populated;
|
||||
int result;
|
||||
size_t match_offset;
|
||||
struct fp_img *img;
|
||||
};
|
||||
|
||||
static void sync_identify_cb(struct fp_dev *dev, int result,
|
||||
size_t match_offset, struct fp_img *img, void *user_data)
|
||||
{
|
||||
struct sync_identify_data *idata = user_data;
|
||||
idata->result = result;
|
||||
idata->match_offset = match_offset;
|
||||
idata->img = img;
|
||||
idata->populated = TRUE;
|
||||
}
|
||||
|
||||
static void identify_stop_cb(struct fp_dev *dev, void *user_data)
|
||||
{
|
||||
gboolean *stopped = user_data;
|
||||
G_DEBUG_HERE();
|
||||
*stopped = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_identify_finger_img:
|
||||
* @dev: the struct #fp_dev device to perform the scan on
|
||||
* @print_gallery: NULL-terminated array of pointers to the prints to
|
||||
* identify against. Each one must have been previously enrolled with a device
|
||||
* compatible to the device selected to perform the scan
|
||||
* @match_offset: output location to store the array index of the matched
|
||||
* gallery print (if any was found). Only valid if %FP_VERIFY_MATCH was
|
||||
* returned
|
||||
* @img: location to store the scan image. accepts %NULL for no image
|
||||
* storage. If an image is returned, it must be freed with fp_img_free() after
|
||||
* use
|
||||
|
||||
* Performs a new scan and attempts to identify the scanned finger against
|
||||
* a collection of previously enrolled fingerprints.
|
||||
* If the device is an imaging device, it can also return the image from
|
||||
* the scan, even when identification fails with a RETRY code. It is legal to
|
||||
* call this function even on non-imaging devices, just don't expect them to
|
||||
* provide images.
|
||||
*
|
||||
* This function returns codes from #fp_verify_result. The return code
|
||||
* %FP_VERIFY_MATCH indicates that the scanned fingerprint
|
||||
* does appear in the print gallery, and the match_offset output parameter
|
||||
* will indicate the index into the print gallery array of the matched print.
|
||||
*
|
||||
* This function will not necessarily examine the whole print gallery, it
|
||||
* will return as soon as it finds a matching print.
|
||||
*
|
||||
* Not all devices support identification. -ENOTSUP will be returned when
|
||||
* this is the case.
|
||||
*
|
||||
* Returns: negative code on error, otherwise a code from #fp_verify_result
|
||||
*/
|
||||
API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev,
|
||||
struct fp_print_data **print_gallery, size_t *match_offset,
|
||||
struct fp_img **img)
|
||||
{
|
||||
gboolean stopped = FALSE;
|
||||
struct sync_identify_data *idata
|
||||
= g_malloc0(sizeof(struct sync_identify_data));
|
||||
int r;
|
||||
|
||||
fp_dbg("to be handled by %s", dev->drv->name);
|
||||
|
||||
r = fp_async_identify_start(dev, print_gallery, sync_identify_cb, idata);
|
||||
if (r < 0) {
|
||||
fp_err("identify_start error %d", r);
|
||||
goto err;
|
||||
}
|
||||
|
||||
while (!idata->populated) {
|
||||
r = fp_handle_events();
|
||||
if (r < 0)
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
if (img)
|
||||
*img = idata->img;
|
||||
else
|
||||
fp_img_free(idata->img);
|
||||
|
||||
r = idata->result;
|
||||
switch (idata->result) {
|
||||
case FP_VERIFY_NO_MATCH:
|
||||
fp_dbg("result: no match");
|
||||
break;
|
||||
case FP_VERIFY_MATCH:
|
||||
fp_dbg("result: match at offset %zd", idata->match_offset);
|
||||
*match_offset = idata->match_offset;
|
||||
break;
|
||||
case FP_VERIFY_RETRY:
|
||||
fp_dbg("verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_TOO_SHORT:
|
||||
fp_dbg("swipe was too short, verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_CENTER_FINGER:
|
||||
fp_dbg("finger was not centered, verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_REMOVE_FINGER:
|
||||
fp_dbg("scan failed, remove finger and retry");
|
||||
break;
|
||||
default:
|
||||
fp_err("unrecognised return code %d", r);
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
||||
err_stop:
|
||||
if (fp_async_identify_stop(dev, identify_stop_cb, &stopped) == 0)
|
||||
while (!stopped)
|
||||
if (fp_handle_events() < 0)
|
||||
break;
|
||||
|
||||
err:
|
||||
g_free(idata);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_identify_finger:
|
||||
* @dev: the struct #fp_dev device to perform the scan on
|
||||
* @print_gallery: %NULL-terminated array of pointers to the prints to
|
||||
* identify against. Each one must have been previously enrolled with a device
|
||||
* compatible to the device selected to perform the scan
|
||||
* @match_offset: output location to store the array index of the matched
|
||||
* gallery print (if any was found). Only valid if %FP_VERIFY_MATCH was
|
||||
* returned
|
||||
|
||||
* Performs a new scan and attempts to identify the scanned finger against a
|
||||
* collection of previously enrolled fingerprints. This function is just a
|
||||
* shortcut to calling fp_identify_finger_img() with a %NULL image output
|
||||
* parameter.
|
||||
*
|
||||
* See also fp_identify_finger_img().
|
||||
*
|
||||
* Returns: negative code on error, otherwise a code from #fp_verify_result
|
||||
*/
|
||||
API_EXPORTED int fp_identify_finger(struct fp_dev *dev,
|
||||
struct fp_print_data **print_gallery, size_t *match_offset)
|
||||
{
|
||||
return fp_identify_finger_img(dev, print_gallery, match_offset, NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
G_DEBUG_HERE();
|
||||
*stopped = TRUE;
|
||||
}
|
||||
/**
|
||||
* fp_dev_img_capture:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @unconditional: whether to unconditionally capture an image, or to only capture when a finger is detected
|
||||
* @img: a location to return the captured image. Must be freed with
|
||||
* fp_img_free() after use
|
||||
*
|
||||
* Captures a #fp_img from a device. The returned image is the raw
|
||||
* image provided by the device, you may wish to [standardize](libfprint-Image-operations.html#img_std) 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.
|
||||
*
|
||||
* See fp_dev_supports_imaging().
|
||||
*
|
||||
* Returns: 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
522
libfprint/fpi-usb-transfer.c
Normal file
522
libfprint/fpi-usb-transfer.c
Normal file
|
@ -0,0 +1,522 @@
|
|||
/*
|
||||
* FPrint USB transfer handling
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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 "fpi-usb-transfer.h"
|
||||
|
||||
/**
|
||||
* SECTION:fpi-usb-transfer
|
||||
* @title: USB transfer helpers
|
||||
* @short_description: Helpers for libgusb to ease transfer handling
|
||||
*
|
||||
* #FpiUsbTransfer is a structure to simplify the USB transfer handling.
|
||||
* The main goal is to ease memory management and provide more parameters
|
||||
* to callbacks that are useful for libfprint drivers.
|
||||
*
|
||||
* Drivers should use this API only rather than accessing the GUsbDevice
|
||||
* directly in most cases.
|
||||
*/
|
||||
|
||||
|
||||
G_DEFINE_BOXED_TYPE (FpiUsbTransfer, fpi_usb_transfer, fpi_usb_transfer_ref, fpi_usb_transfer_unref)
|
||||
|
||||
static void
|
||||
log_transfer (FpiUsbTransfer *transfer, gboolean submit, GError *error)
|
||||
{
|
||||
if (g_getenv ("FP_DEBUG_TRANSFER"))
|
||||
{
|
||||
if (!submit)
|
||||
{
|
||||
g_autofree gchar *error_str = NULL;
|
||||
if (error)
|
||||
error_str = g_strdup_printf ("with error (%s)", error->message);
|
||||
else
|
||||
error_str = g_strdup ("successfully");
|
||||
|
||||
g_debug ("Transfer %p completed %s, requested length %zd, actual length %zd, endpoint 0x%x",
|
||||
transfer,
|
||||
error_str,
|
||||
transfer->length,
|
||||
transfer->actual_length,
|
||||
transfer->endpoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_debug ("Transfer %p submitted, requested length %zd, endpoint 0x%x",
|
||||
transfer,
|
||||
transfer->length,
|
||||
transfer->endpoint);
|
||||
}
|
||||
|
||||
if (!submit == !!(transfer->endpoint & FPI_USB_ENDPOINT_IN))
|
||||
{
|
||||
g_autoptr(GString) line = NULL;
|
||||
gssize dump_len;
|
||||
|
||||
dump_len = (transfer->endpoint & FPI_USB_ENDPOINT_IN) ? transfer->actual_length : transfer->length;
|
||||
|
||||
line = g_string_new ("");
|
||||
/* Dump the buffer. */
|
||||
for (gint i = 0; i < dump_len; i++)
|
||||
{
|
||||
g_string_append_printf (line, "%02x ", transfer->buffer[i]);
|
||||
if ((i + 1) % 16 == 0)
|
||||
{
|
||||
g_debug ("%s", line->str);
|
||||
g_string_set_size (line, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (line->len)
|
||||
g_debug ("%s", line->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_new:
|
||||
* @device: The #FpDevice the transfer is for
|
||||
*
|
||||
* Creates a new #FpiUsbTransfer.
|
||||
*
|
||||
* Returns: (transfer full): A newly created #FpiUsbTransfer
|
||||
*/
|
||||
FpiUsbTransfer *
|
||||
fpi_usb_transfer_new (FpDevice * device)
|
||||
{
|
||||
FpiUsbTransfer *self;
|
||||
|
||||
g_assert (device != NULL);
|
||||
|
||||
self = g_slice_new0 (FpiUsbTransfer);
|
||||
self->ref_count = 1;
|
||||
|
||||
self->device = device;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_usb_transfer_free (FpiUsbTransfer *self)
|
||||
{
|
||||
g_assert (self);
|
||||
g_assert_cmpint (self->ref_count, ==, 0);
|
||||
|
||||
if (self->free_buffer && self->buffer)
|
||||
self->free_buffer (self->buffer);
|
||||
self->buffer = NULL;
|
||||
|
||||
g_slice_free (FpiUsbTransfer, self);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_ref:
|
||||
* @self: A #FpiUsbTransfer
|
||||
*
|
||||
* Increments the reference count of @self by one.
|
||||
*
|
||||
* Returns: (transfer full): @self
|
||||
*/
|
||||
FpiUsbTransfer *
|
||||
fpi_usb_transfer_ref (FpiUsbTransfer *self)
|
||||
{
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (self->ref_count, NULL);
|
||||
|
||||
g_atomic_int_inc (&self->ref_count);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_unref:
|
||||
* @self: A #FpiUsbTransfer
|
||||
*
|
||||
* Decrements the reference count of @self by one, freeing the structure when
|
||||
* the reference count reaches zero.
|
||||
*/
|
||||
void
|
||||
fpi_usb_transfer_unref (FpiUsbTransfer *self)
|
||||
{
|
||||
g_return_if_fail (self);
|
||||
g_return_if_fail (self->ref_count);
|
||||
|
||||
if (g_atomic_int_dec_and_test (&self->ref_count))
|
||||
fpi_usb_transfer_free (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_fill_bulk:
|
||||
* @transfer: The #FpiUsbTransfer
|
||||
* @endpoint: The endpoint to send the transfer to
|
||||
* @length: The buffer size to allocate
|
||||
*
|
||||
* Prepare a bulk transfer. A buffer will be created for you, use
|
||||
* fpi_usb_transfer_fill_bulk_full() if you want to send a static buffer
|
||||
* or receive a pre-defined buffer.
|
||||
*/
|
||||
void
|
||||
fpi_usb_transfer_fill_bulk (FpiUsbTransfer *transfer,
|
||||
guint8 endpoint,
|
||||
gsize length)
|
||||
{
|
||||
fpi_usb_transfer_fill_bulk_full (transfer,
|
||||
endpoint,
|
||||
g_malloc0 (length),
|
||||
length,
|
||||
g_free);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_fill_bulk_full:
|
||||
* @transfer: The #FpiUsbTransfer
|
||||
* @endpoint: The endpoint to send the transfer to
|
||||
* @buffer: The data to send. A buffer will be created and managed for you if you pass NULL.
|
||||
* @length: The size of @buffer
|
||||
* @free_func: (destroy buffer): Destroy notify for @buffer
|
||||
*
|
||||
* Prepare a bulk transfer.
|
||||
*/
|
||||
void
|
||||
fpi_usb_transfer_fill_bulk_full (FpiUsbTransfer *transfer,
|
||||
guint8 endpoint,
|
||||
guint8 *buffer,
|
||||
gsize length,
|
||||
GDestroyNotify free_func)
|
||||
{
|
||||
g_assert (transfer->type == FP_TRANSFER_NONE);
|
||||
g_assert (buffer != NULL);
|
||||
|
||||
transfer->type = FP_TRANSFER_BULK;
|
||||
transfer->endpoint = endpoint;
|
||||
|
||||
transfer->buffer = buffer;
|
||||
transfer->length = length;
|
||||
transfer->free_buffer = free_func;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_fill_control:
|
||||
* @transfer: The #FpiUsbTransfer
|
||||
* @direction: The direction of the control transfer
|
||||
* @request_type: The request type
|
||||
* @recipient: The recipient
|
||||
* @request: The control transfer request
|
||||
* @value: The control transfer value
|
||||
* @idx: The control transfer index
|
||||
* @length: The size of the transfer
|
||||
*
|
||||
* Prepare a control transfer. The function will create a new buffer,
|
||||
* you can initialize the buffer after calling this function.
|
||||
*/
|
||||
void
|
||||
fpi_usb_transfer_fill_control (FpiUsbTransfer *transfer,
|
||||
GUsbDeviceDirection direction,
|
||||
GUsbDeviceRequestType request_type,
|
||||
GUsbDeviceRecipient recipient,
|
||||
guint8 request,
|
||||
guint16 value,
|
||||
guint16 idx,
|
||||
gsize length)
|
||||
{
|
||||
g_assert (transfer->type == FP_TRANSFER_NONE);
|
||||
|
||||
transfer->type = FP_TRANSFER_CONTROL;
|
||||
transfer->direction = direction;
|
||||
transfer->request_type = request_type;
|
||||
transfer->recipient = recipient;
|
||||
transfer->request = request;
|
||||
transfer->value = value;
|
||||
transfer->idx = idx;
|
||||
|
||||
transfer->length = length;
|
||||
transfer->buffer = g_malloc0 (length);
|
||||
transfer->free_buffer = g_free;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_fill_interrupt:
|
||||
* @transfer: The #FpiUsbTransfer
|
||||
* @endpoint: The endpoint to send the transfer to
|
||||
* @length: The size of the transfer
|
||||
*
|
||||
* Prepare an interrupt transfer. The function will create a new buffer,
|
||||
* you can initialize the buffer after calling this function.
|
||||
*/
|
||||
void
|
||||
fpi_usb_transfer_fill_interrupt (FpiUsbTransfer *transfer,
|
||||
guint8 endpoint,
|
||||
gsize length)
|
||||
{
|
||||
fpi_usb_transfer_fill_interrupt_full (transfer,
|
||||
endpoint,
|
||||
g_malloc0 (length),
|
||||
length,
|
||||
g_free);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_fill_interrupt_full:
|
||||
* @transfer: The #FpiUsbTransfer
|
||||
* @endpoint: The endpoint to send the transfer to
|
||||
* @buffer: The data to send. A buffer will be created and managed for you if you pass NULL.
|
||||
* @length: The size of @buffer
|
||||
* @free_func: (destroy buffer): Destroy notify for @buffer
|
||||
*
|
||||
* Prepare an interrupt transfer.
|
||||
*/
|
||||
void
|
||||
fpi_usb_transfer_fill_interrupt_full (FpiUsbTransfer *transfer,
|
||||
guint8 endpoint,
|
||||
guint8 *buffer,
|
||||
gsize length,
|
||||
GDestroyNotify free_func)
|
||||
{
|
||||
g_assert (transfer->type == FP_TRANSFER_NONE);
|
||||
g_assert (buffer != NULL);
|
||||
|
||||
transfer->type = FP_TRANSFER_INTERRUPT;
|
||||
transfer->endpoint = endpoint;
|
||||
|
||||
transfer->buffer = buffer;
|
||||
transfer->length = length;
|
||||
transfer->free_buffer = free_func;
|
||||
}
|
||||
|
||||
void
|
||||
transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
FpiUsbTransfer *transfer = user_data;
|
||||
FpiUsbTransferCallback callback;
|
||||
|
||||
switch (transfer->type)
|
||||
{
|
||||
case FP_TRANSFER_BULK:
|
||||
transfer->actual_length =
|
||||
g_usb_device_bulk_transfer_finish (G_USB_DEVICE (source_object),
|
||||
res,
|
||||
&error);
|
||||
break;
|
||||
|
||||
case FP_TRANSFER_CONTROL:
|
||||
transfer->actual_length =
|
||||
g_usb_device_control_transfer_finish (G_USB_DEVICE (source_object),
|
||||
res,
|
||||
&error);
|
||||
break;
|
||||
|
||||
case FP_TRANSFER_INTERRUPT:
|
||||
transfer->actual_length =
|
||||
g_usb_device_interrupt_transfer_finish (G_USB_DEVICE (source_object),
|
||||
res,
|
||||
&error);
|
||||
break;
|
||||
|
||||
case FP_TRANSFER_NONE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
log_transfer (transfer, FALSE, error);
|
||||
|
||||
/* Check for short error, and set an error if requested */
|
||||
if (error == NULL &&
|
||||
transfer->short_is_error &&
|
||||
transfer->actual_length > 0 &&
|
||||
transfer->actual_length != transfer->length)
|
||||
{
|
||||
error = g_error_new (G_USB_DEVICE_ERROR,
|
||||
G_USB_DEVICE_ERROR_IO,
|
||||
"Unexpected short error of %zd size (expected %zd)", transfer->actual_length, transfer->length);
|
||||
}
|
||||
|
||||
callback = transfer->callback;
|
||||
transfer->callback = NULL;
|
||||
callback (transfer, transfer->device, transfer->user_data, error);
|
||||
|
||||
fpi_usb_transfer_unref (transfer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_submit:
|
||||
* @transfer: The transfer to submit, must have been filled.
|
||||
* @timeout_ms: Timeout for the transfer in ms
|
||||
* @cancellable: Cancellable to use, e.g. fpi_device_get_cancellable()
|
||||
* @callback: Callback on completion or error
|
||||
* @user_data: Data to pass to callback
|
||||
*
|
||||
* Submit a USB transfer with a specific timeout and callback functions.
|
||||
*
|
||||
* Note that #FpiUsbTransfer is owned by the user. In most cases, you
|
||||
* should call fpi_usb_transfer_unref() just after calling this function.
|
||||
* Doing so means that all associated data will be free'ed automatically
|
||||
* after the callback ran.
|
||||
*/
|
||||
void
|
||||
fpi_usb_transfer_submit (FpiUsbTransfer *transfer,
|
||||
guint timeout_ms,
|
||||
GCancellable *cancellable,
|
||||
FpiUsbTransferCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_return_if_fail (transfer);
|
||||
g_return_if_fail (callback);
|
||||
|
||||
/* Recycling is allowed, but not two at the same time. */
|
||||
g_return_if_fail (transfer->callback == NULL);
|
||||
|
||||
transfer->callback = callback;
|
||||
transfer->user_data = user_data;
|
||||
|
||||
/* Grab a reference, this means that one can simply unref after submit and
|
||||
* trust for the data to disappear without explicit management by the callback
|
||||
* function. */
|
||||
fpi_usb_transfer_ref (transfer);
|
||||
|
||||
log_transfer (transfer, TRUE, NULL);
|
||||
|
||||
switch (transfer->type)
|
||||
{
|
||||
case FP_TRANSFER_BULK:
|
||||
g_usb_device_bulk_transfer_async (fpi_device_get_usb_device (transfer->device),
|
||||
transfer->endpoint,
|
||||
transfer->buffer,
|
||||
transfer->length,
|
||||
timeout_ms,
|
||||
cancellable,
|
||||
transfer_finish_cb,
|
||||
transfer);
|
||||
break;
|
||||
|
||||
case FP_TRANSFER_CONTROL:
|
||||
g_usb_device_control_transfer_async (fpi_device_get_usb_device (transfer->device),
|
||||
transfer->direction,
|
||||
transfer->request_type,
|
||||
transfer->recipient,
|
||||
transfer->request,
|
||||
transfer->value,
|
||||
transfer->idx,
|
||||
transfer->buffer,
|
||||
transfer->length,
|
||||
timeout_ms,
|
||||
cancellable,
|
||||
transfer_finish_cb,
|
||||
transfer);
|
||||
break;
|
||||
|
||||
case FP_TRANSFER_INTERRUPT:
|
||||
g_usb_device_interrupt_transfer_async (fpi_device_get_usb_device (transfer->device),
|
||||
transfer->endpoint,
|
||||
transfer->buffer,
|
||||
transfer->length,
|
||||
timeout_ms,
|
||||
cancellable,
|
||||
transfer_finish_cb,
|
||||
transfer);
|
||||
break;
|
||||
|
||||
case FP_TRANSFER_NONE:
|
||||
default:
|
||||
fpi_usb_transfer_unref (transfer);
|
||||
g_return_if_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_submit_sync:
|
||||
* @transfer: The transfer to submit, must have been filled.
|
||||
* @timeout_ms: Timeout for the transfer in millisecnods
|
||||
* @error: Location to store #GError to
|
||||
*
|
||||
* Synchronously submit a USB transfer with a specific timeout.
|
||||
* Only use this function with short timeouts as the application will
|
||||
* be blocked otherwise.
|
||||
*
|
||||
* Note that you still need to fpi_usb_transfer_unref() the
|
||||
* #FpiUsbTransfer afterwards.
|
||||
*
|
||||
* Returns: #TRUE on success, otherwise #FALSE and @error will be set
|
||||
*/
|
||||
gboolean
|
||||
fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer,
|
||||
guint timeout_ms,
|
||||
GError **error)
|
||||
{
|
||||
gboolean res;
|
||||
|
||||
g_return_val_if_fail (transfer, FALSE);
|
||||
|
||||
/* Recycling is allowed, but not two at the same time. */
|
||||
g_return_val_if_fail (transfer->callback == NULL, FALSE);
|
||||
|
||||
log_transfer (transfer, TRUE, NULL);
|
||||
|
||||
switch (transfer->type)
|
||||
{
|
||||
case FP_TRANSFER_BULK:
|
||||
res = g_usb_device_bulk_transfer (fpi_device_get_usb_device (transfer->device),
|
||||
transfer->endpoint,
|
||||
transfer->buffer,
|
||||
transfer->length,
|
||||
&transfer->actual_length,
|
||||
timeout_ms,
|
||||
NULL,
|
||||
error);
|
||||
break;
|
||||
|
||||
case FP_TRANSFER_CONTROL:
|
||||
res = g_usb_device_control_transfer (fpi_device_get_usb_device (transfer->device),
|
||||
transfer->direction,
|
||||
transfer->request_type,
|
||||
transfer->recipient,
|
||||
transfer->request,
|
||||
transfer->value,
|
||||
transfer->idx,
|
||||
transfer->buffer,
|
||||
transfer->length,
|
||||
&transfer->actual_length,
|
||||
timeout_ms,
|
||||
NULL,
|
||||
error);
|
||||
break;
|
||||
|
||||
case FP_TRANSFER_INTERRUPT:
|
||||
res = g_usb_device_interrupt_transfer (fpi_device_get_usb_device (transfer->device),
|
||||
transfer->endpoint,
|
||||
transfer->buffer,
|
||||
transfer->length,
|
||||
&transfer->actual_length,
|
||||
timeout_ms,
|
||||
NULL,
|
||||
error);
|
||||
break;
|
||||
|
||||
case FP_TRANSFER_NONE:
|
||||
default:
|
||||
g_return_val_if_reached (FALSE);
|
||||
}
|
||||
|
||||
log_transfer (transfer, FALSE, *error);
|
||||
|
||||
if (!res)
|
||||
transfer->actual_length = -1;
|
||||
|
||||
return res;
|
||||
}
|
157
libfprint/fpi-usb-transfer.h
Normal file
157
libfprint/fpi-usb-transfer.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* FPrint USB transfer handling
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gusb.h>
|
||||
#include "fpi-device.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FPI_TYPE_USB_TRANSFER (fpi_usb_transfer_get_type ())
|
||||
|
||||
#define FPI_USB_ENDPOINT_IN 0x80
|
||||
#define FPI_USB_ENDPOINT_OUT 0x00
|
||||
|
||||
typedef struct _FpiUsbTransfer FpiUsbTransfer;
|
||||
|
||||
#include "fpi-ssm.h"
|
||||
|
||||
typedef void (*FpiUsbTransferCallback)(FpiUsbTransfer *transfer,
|
||||
FpDevice *dev,
|
||||
gpointer user_data,
|
||||
GError *error);
|
||||
|
||||
/**
|
||||
* FpiTransferType:
|
||||
* @FP_TRANSFER_NONE: Type not set
|
||||
* @FP_TRANSFER_BULK: Bulk transfer
|
||||
* @FP_TRANSFER_CONTROL: Control transfer
|
||||
* @FP_TRANSFER_INTERRUPT: Interrupt transfer
|
||||
*
|
||||
* Type of the transfer.
|
||||
*/
|
||||
typedef enum {
|
||||
FP_TRANSFER_NONE = 0,
|
||||
FP_TRANSFER_BULK,
|
||||
FP_TRANSFER_CONTROL,
|
||||
FP_TRANSFER_INTERRUPT,
|
||||
} FpiTransferType;
|
||||
|
||||
/**
|
||||
* FpiUsbTransfer:
|
||||
* @device: The #FpDevice that the transfer belongs to.
|
||||
* @ssm: Storage slot to associate the transfer with a state machine.
|
||||
* Used by fpi_ssm_usb_transfer_cb() to modify the given state machine.
|
||||
* @length: The requested length of the transfer in bytes.
|
||||
* @actual_length: The actual length of the transfer
|
||||
* (see also fpi_usb_transfer_set_short_error())
|
||||
* @buffer: The transfered data.
|
||||
*
|
||||
* Helper for handling USB transfers.
|
||||
*/
|
||||
struct _FpiUsbTransfer
|
||||
{
|
||||
/*< public >*/
|
||||
FpDevice *device;
|
||||
|
||||
FpiSsm *ssm;
|
||||
|
||||
gssize length;
|
||||
gssize actual_length;
|
||||
|
||||
guchar *buffer;
|
||||
|
||||
/*< private >*/
|
||||
guint ref_count;
|
||||
|
||||
/* USB Transfer information */
|
||||
FpiTransferType type;
|
||||
guint8 endpoint;
|
||||
|
||||
/* Control Transfer options */
|
||||
GUsbDeviceDirection direction;
|
||||
GUsbDeviceRequestType request_type;
|
||||
GUsbDeviceRecipient recipient;
|
||||
guint8 request;
|
||||
guint16 value;
|
||||
guint16 idx;
|
||||
|
||||
/* Flags */
|
||||
gboolean short_is_error;
|
||||
|
||||
/* Callbacks */
|
||||
gpointer user_data;
|
||||
FpiUsbTransferCallback callback;
|
||||
|
||||
/* Data free function */
|
||||
GDestroyNotify free_buffer;
|
||||
};
|
||||
|
||||
GType fpi_usb_transfer_get_type (void) G_GNUC_CONST;
|
||||
FpiUsbTransfer *fpi_usb_transfer_new (FpDevice *device);
|
||||
FpiUsbTransfer *fpi_usb_transfer_ref (FpiUsbTransfer *self);
|
||||
void fpi_usb_transfer_unref (FpiUsbTransfer *self);
|
||||
|
||||
void fpi_usb_transfer_set_short_error (FpiUsbTransfer *transfer,
|
||||
gboolean short_is_error);
|
||||
|
||||
void fpi_usb_transfer_fill_bulk (FpiUsbTransfer *transfer,
|
||||
guint8 endpoint,
|
||||
gsize length);
|
||||
|
||||
void fpi_usb_transfer_fill_bulk_full (FpiUsbTransfer *transfer,
|
||||
guint8 endpoint,
|
||||
guint8 *buffer,
|
||||
gsize length,
|
||||
GDestroyNotify free_func);
|
||||
|
||||
void fpi_usb_transfer_fill_control (FpiUsbTransfer *transfer,
|
||||
GUsbDeviceDirection direction,
|
||||
GUsbDeviceRequestType request_type,
|
||||
GUsbDeviceRecipient recipient,
|
||||
guint8 request,
|
||||
guint16 value,
|
||||
guint16 idx,
|
||||
gsize length);
|
||||
|
||||
void fpi_usb_transfer_fill_interrupt (FpiUsbTransfer *transfer,
|
||||
guint8 endpoint,
|
||||
gsize length);
|
||||
|
||||
void fpi_usb_transfer_fill_interrupt_full (FpiUsbTransfer *transfer,
|
||||
guint8 endpoint,
|
||||
guint8 *buffer,
|
||||
gsize length,
|
||||
GDestroyNotify free_func);
|
||||
|
||||
void fpi_usb_transfer_submit (FpiUsbTransfer *transfer,
|
||||
guint timeout_ms,
|
||||
GCancellable *cancellable,
|
||||
FpiUsbTransferCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer,
|
||||
guint timeout_ms,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpiUsbTransfer, fpi_usb_transfer_unref)
|
||||
|
||||
G_END_DECLS
|
|
@ -1,236 +0,0 @@
|
|||
/*
|
||||
* Driver API definitions
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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 "fpi-usb.h"
|
||||
#include "drivers_api.h"
|
||||
|
||||
/**
|
||||
* SECTION:fpi-usb
|
||||
* @title: Helpers for libusb
|
||||
* @short_description: libusb-related helpers
|
||||
*
|
||||
* A collection of [libusb helpers](http://libusb.sourceforge.net/api-1.0/group__poll.html#details)
|
||||
* to make driver development easier. Please refer to the libusb API documentation for more
|
||||
* information about the original API.
|
||||
*/
|
||||
|
||||
/* Helpers from glib */
|
||||
#include <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* special helpers to avoid gmessage.c dependency */
|
||||
static void mem_error (const char *format, ...) G_GNUC_PRINTF (1,2);
|
||||
#define mem_assert(cond) do { if (G_LIKELY (cond)) ; else mem_error ("assertion failed: %s", #cond); } while (0)
|
||||
|
||||
static void
|
||||
mem_error (const char *format,
|
||||
...)
|
||||
{
|
||||
const char *pname;
|
||||
va_list args;
|
||||
/* at least, put out "MEMORY-ERROR", in case we segfault during the rest of the function */
|
||||
fputs ("\n***MEMORY-ERROR***: ", stderr);
|
||||
pname = g_get_prgname();
|
||||
g_fprintf (stderr, "%s[%ld]: ", pname ? pname : "", (long)getpid());
|
||||
va_start (args, format);
|
||||
g_vfprintf (stderr, format, args);
|
||||
va_end (args);
|
||||
fputs ("\n", stderr);
|
||||
abort();
|
||||
_exit (1);
|
||||
}
|
||||
|
||||
struct fpi_usb_transfer {
|
||||
struct libusb_transfer *transfer;
|
||||
fpi_ssm *ssm;
|
||||
struct fp_dev *dev;
|
||||
fpi_usb_transfer_cb_fn callback;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* fpi_usb_alloc:
|
||||
*
|
||||
* Returns a struct libusb_transfer, similar to calling
|
||||
* `libusb_alloc_transfer(0)`[[1](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#ga13cc69ea40c702181c430c950121c000)]. As libfprint uses GLib internally,
|
||||
* and [memory allocation failures will make applications fail](https://developer.gnome.org/glib/stable/glib-Memory-Allocation.html#glib-Memory-Allocation.description),
|
||||
* this helper will assert when the libusb call fails.
|
||||
*/
|
||||
struct libusb_transfer *
|
||||
fpi_usb_alloc(void)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
|
||||
transfer = libusb_alloc_transfer(0);
|
||||
mem_assert(transfer);
|
||||
|
||||
return transfer;
|
||||
}
|
||||
|
||||
static fpi_usb_transfer *
|
||||
fpi_usb_transfer_new(struct fp_dev *dev,
|
||||
fpi_ssm *ssm,
|
||||
fpi_usb_transfer_cb_fn callback,
|
||||
void *user_data)
|
||||
{
|
||||
fpi_usb_transfer *transfer;
|
||||
|
||||
transfer = g_new0(fpi_usb_transfer, 1);
|
||||
transfer->transfer = fpi_usb_alloc();
|
||||
transfer->dev = dev;
|
||||
transfer->ssm = ssm;
|
||||
transfer->callback = callback;
|
||||
transfer->user_data = user_data;
|
||||
|
||||
return transfer;
|
||||
}
|
||||
|
||||
void
|
||||
fpi_usb_transfer_free(fpi_usb_transfer *transfer)
|
||||
{
|
||||
if (transfer == NULL)
|
||||
return;
|
||||
|
||||
g_free(transfer->transfer->buffer);
|
||||
libusb_free_transfer(transfer->transfer);
|
||||
g_free(transfer);
|
||||
}
|
||||
|
||||
static void
|
||||
fpi_usb_transfer_cb (struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_usb_transfer *t;
|
||||
|
||||
g_assert(transfer);
|
||||
g_assert(transfer->user_data);
|
||||
|
||||
t = transfer->user_data;
|
||||
BUG_ON(transfer->callback == NULL);
|
||||
(t->callback) (transfer, t->dev, t->ssm, t->user_data);
|
||||
fpi_usb_transfer_free(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_fill_bulk_transfer:
|
||||
* @dev: a struct #fp_dev fingerprint device
|
||||
* @ssm: the current #fpi_ssm state machine
|
||||
* @endpoint: the USB end point
|
||||
* @buffer: a buffer allocated with g_malloc() or another GLib function.
|
||||
* Note that the returned #fpi_usb_transfer will own this buffer, so it
|
||||
* should not be freed manually.
|
||||
* @length: the size of @buffer
|
||||
* @callback: the callback function that will be called once the fpi_usb_submit_transfer()
|
||||
* call finishes.
|
||||
* @user_data: a user data pointer to pass to the callback
|
||||
* @timeout: timeout for the transfer in milliseconds, or 0 for no timeout
|
||||
*
|
||||
* This function is similar to calling [`libusb_alloc_transfer(0)`](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#ga13cc69ea40c702181c430c950121c000)]
|
||||
* followed by calling [`libusb_fill_bulk_transfer()`](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#gad4ddb1a5c6c7fefc979a44d7300b95d7).
|
||||
* The #fpi_usb_transfer_cb_fn callback will however provide more arguments
|
||||
* relevant to libfprint drivers, making it a good replacement for the raw libusb
|
||||
* calls.
|
||||
*
|
||||
* Returns: a #fpi_usb_transfer transfer struct, to be passed to
|
||||
* fpi_usb_submit_transfer().
|
||||
*/
|
||||
fpi_usb_transfer *
|
||||
fpi_usb_fill_bulk_transfer (struct fp_dev *dev,
|
||||
fpi_ssm *ssm,
|
||||
unsigned char endpoint,
|
||||
unsigned char *buffer,
|
||||
int length,
|
||||
fpi_usb_transfer_cb_fn callback,
|
||||
void *user_data,
|
||||
unsigned int timeout)
|
||||
{
|
||||
fpi_usb_transfer *transfer;
|
||||
|
||||
g_return_val_if_fail (dev != NULL, NULL);
|
||||
g_return_val_if_fail (callback != NULL, NULL);
|
||||
|
||||
transfer = fpi_usb_transfer_new(dev,
|
||||
ssm,
|
||||
callback,
|
||||
user_data);
|
||||
|
||||
libusb_fill_bulk_transfer(transfer->transfer,
|
||||
fpi_dev_get_usb_dev(dev),
|
||||
endpoint,
|
||||
buffer,
|
||||
length,
|
||||
fpi_usb_transfer_cb,
|
||||
transfer,
|
||||
timeout);
|
||||
|
||||
return transfer;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_submit_transfer:
|
||||
* @transfer: a #fpi_usb_transfer struct
|
||||
*
|
||||
* Start a transfer to the device with the provided #fpi_usb_transfer.
|
||||
* On error, the #fpi_usb_transfer struct will be freed, otherwise it will
|
||||
* be freed once the callback provided to fpi_usb_fill_bulk_transfer() has
|
||||
* been called.
|
||||
*
|
||||
* Returns: 0 on success, or the same errors as [libusb_submit_transfer](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#gabb0932601f2c7dad2fee4b27962848ce)
|
||||
* on failure.
|
||||
*/
|
||||
int
|
||||
fpi_usb_submit_transfer(fpi_usb_transfer *transfer)
|
||||
{
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail (transfer != NULL, LIBUSB_ERROR_INVALID_PARAM);
|
||||
|
||||
r = libusb_submit_transfer(transfer->transfer);
|
||||
if (r < 0)
|
||||
fpi_usb_transfer_free(transfer);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_usb_cancel_transfer:
|
||||
* @transfer: a #fpi_usb_transfer struct
|
||||
*
|
||||
* Cancel a transfer to the device with the provided #fpi_usb_transfer.
|
||||
* Note that this will not complete the cancellation, as your transfer
|
||||
* callback will be called with the `LIBUSB_TRANSFER_CANCELLED` status,
|
||||
* as [libusb_cancel_transfer](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#ga685eb7731f9a0593f75beb99727bbe54)
|
||||
* would.
|
||||
*
|
||||
* You should not access anything but the given struct #libusb_transfer
|
||||
* in the callback before checking whether `LIBUSB_TRANSFER_CANCELLED` has
|
||||
* been called, as that might cause memory access violations.
|
||||
*
|
||||
* Returns: 0 on success, or the same errors as [libusb_cancel_transfer](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#ga685eb7731f9a0593f75beb99727bbe54)
|
||||
* on failure.
|
||||
*/
|
||||
int
|
||||
fpi_usb_cancel_transfer(fpi_usb_transfer *transfer)
|
||||
{
|
||||
g_return_val_if_fail (transfer != NULL, LIBUSB_ERROR_NOT_FOUND);
|
||||
|
||||
return libusb_cancel_transfer(transfer->transfer);
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __FPI_USB_H__
|
||||
#define __FPI_USB_H__
|
||||
|
||||
#include <libusb.h>
|
||||
#include "fpi-dev.h"
|
||||
#include "fpi-ssm.h"
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer:
|
||||
*
|
||||
* A structure containing the arguments passed to fpi_usb_fill_bulk_transfer()
|
||||
* to be used with fpi_usb_submit_transfer().
|
||||
*/
|
||||
typedef struct fpi_usb_transfer fpi_usb_transfer;
|
||||
|
||||
/**
|
||||
* fpi_usb_transfer_cb_fn:
|
||||
* @transfer: a struct #libusb_transfer
|
||||
* @dev: the struct #fp_dev on which the operation was performed
|
||||
* @ssm: the #fpi_ssm state machine
|
||||
* @user_data: the user data passed to fpi_usb_fill_bulk_transfer()
|
||||
*
|
||||
* This callback will be called in response to a libusb bulk transfer
|
||||
* triggered via fpi_usb_fill_bulk_transfer() finishing. Note that the
|
||||
* struct #libusb_transfer does not need to be freed, as it will be
|
||||
* freed after the callback returns, similarly to
|
||||
* the [LIBUSB_TRANSFER_FREE_TRANSFER flag](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#gga1fb47dd0f7c209b60a3609ff0c03d56dacf3f064997b283a14097c9f4d6f8ccc1).
|
||||
*
|
||||
* Note that the cancelled status of the transfer should be checked
|
||||
* first thing, as the @dev, @ssm and @user_data pointers might not
|
||||
* be pointing to valid values anymore. See fpi_usb_cancel_transfer()
|
||||
* for more information.
|
||||
*/
|
||||
typedef void(*fpi_usb_transfer_cb_fn) (struct libusb_transfer *transfer,
|
||||
struct fp_dev *dev,
|
||||
fpi_ssm *ssm,
|
||||
void *user_data);
|
||||
|
||||
struct libusb_transfer *fpi_usb_alloc(void) __attribute__((returns_nonnull));
|
||||
|
||||
fpi_usb_transfer *fpi_usb_fill_bulk_transfer (struct fp_dev *dev,
|
||||
fpi_ssm *ssm,
|
||||
unsigned char endpoint,
|
||||
unsigned char *buffer,
|
||||
int length,
|
||||
fpi_usb_transfer_cb_fn callback,
|
||||
void *user_data,
|
||||
unsigned int timeout);
|
||||
int fpi_usb_submit_transfer (fpi_usb_transfer *transfer);
|
||||
int fpi_usb_cancel_transfer (fpi_usb_transfer *transfer);
|
||||
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (C) 2009 Red Hat <mjg@redhat.com>
|
||||
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
|
||||
* Copyright (C) 2008 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net>
|
||||
* Coypright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -22,28 +23,47 @@
|
|||
#include <stdio.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
#include "fpi-context.h"
|
||||
#include "fpi-device.h"
|
||||
|
||||
GHashTable *printed = NULL;
|
||||
|
||||
static GList *insert_driver (GList *list,
|
||||
struct fp_driver *driver)
|
||||
static GList *insert_drivers (GList *list)
|
||||
{
|
||||
int i;
|
||||
g_autoptr(GArray) drivers = g_array_new (FALSE, FALSE, sizeof(GType));
|
||||
gint i;
|
||||
|
||||
for (i = 0; driver->id_table[i].vendor != 0; i++) {
|
||||
char *key;
|
||||
fpi_get_driver_types (drivers);
|
||||
|
||||
key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product);
|
||||
/* Find the best driver to handle this USB device. */
|
||||
for (i = 0; i < drivers->len; i++)
|
||||
{
|
||||
GType driver = g_array_index (drivers, GType, i);
|
||||
FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver));
|
||||
const FpIdEntry *entry;
|
||||
|
||||
if (g_hash_table_lookup (printed, key) != NULL) {
|
||||
if (cls->type != FP_DEVICE_TYPE_USB) {
|
||||
g_type_class_unref (cls);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (entry = cls->id_table; entry->vid; entry++)
|
||||
{
|
||||
char *key;
|
||||
|
||||
key = g_strdup_printf ("%04x:%04x", entry->vid, entry->pid);
|
||||
|
||||
if (g_hash_table_lookup (printed, key) != NULL) {
|
||||
g_free (key);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
|
||||
g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
|
||||
|
||||
list = g_list_prepend (list, g_strdup_printf ("%s | %s\n", key, driver->full_name));
|
||||
list = g_list_prepend (list, g_strdup_printf ("%s | %s\n", key, cls->full_name));
|
||||
}
|
||||
|
||||
g_type_class_unref (cls);
|
||||
}
|
||||
|
||||
return list;
|
||||
|
@ -51,14 +71,10 @@ static GList *insert_driver (GList *list,
|
|||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
struct fp_driver **driver_list;
|
||||
guint i;
|
||||
GList *list, *l;
|
||||
|
||||
setlocale (LC_ALL, "");
|
||||
|
||||
driver_list = fprint_get_drivers ();
|
||||
|
||||
printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
|
||||
g_print ("%% lifprint — Supported Devices\n");
|
||||
|
@ -76,8 +92,7 @@ int main (int argc, char **argv)
|
|||
g_print ("------------ | ------------\n");
|
||||
|
||||
list = NULL;
|
||||
for (i = 0; driver_list[i] != NULL; i++)
|
||||
list = insert_driver (list, driver_list[i]);
|
||||
list = insert_drivers (list);
|
||||
|
||||
list = g_list_sort (list, (GCompareFunc) g_strcmp0);
|
||||
for (l = list; l != NULL; l = l->next)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (C) 2009 Red Hat <mjg@redhat.com>
|
||||
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
|
||||
* Copyright (C) 2008 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net>
|
||||
* Coypright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -19,54 +20,55 @@
|
|||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
#include "fpi-context.h"
|
||||
#include "fpi-device.h"
|
||||
|
||||
static const struct usb_id whitelist_id_table[] = {
|
||||
static const FpIdEntry whitelist_id_table[] = {
|
||||
/* Unsupported (for now) Validity Sensors finger print readers */
|
||||
{ .vendor = 0x138a, .product = 0x0090 }, /* Found on e.g. Lenovo T460s */
|
||||
{ .vendor = 0x138a, .product = 0x0091 },
|
||||
{ .vendor = 0x138a, .product = 0x0094 },
|
||||
{ .vendor = 0x138a, .product = 0x0097 }, /* Found on e.g. Lenovo T470s */
|
||||
{ 0, 0, 0, },
|
||||
{ .vid = 0x138a, .pid = 0x0090 }, /* Found on e.g. Lenovo T460s */
|
||||
{ .vid = 0x138a, .pid = 0x0091 },
|
||||
{ .vid = 0x138a, .pid = 0x0094 },
|
||||
{ .vid = 0x138a, .pid = 0x0097 }, /* Found on e.g. Lenovo T470s */
|
||||
{ .vid = 0 },
|
||||
};
|
||||
|
||||
static const struct usb_id blacklist_id_table[] = {
|
||||
{ .vendor = 0x0483, .product = 0x2016 },
|
||||
static const FpIdEntry blacklist_id_table[] = {
|
||||
{ .vid = 0x0483, .pid = 0x2016 },
|
||||
/* https://bugs.freedesktop.org/show_bug.cgi?id=66659 */
|
||||
{ .vendor = 0x045e, .product = 0x00bb },
|
||||
{ 0, 0, 0 },
|
||||
{ .vid = 0x045e, .pid = 0x00bb },
|
||||
{ .vid = 0 },
|
||||
};
|
||||
|
||||
struct fp_driver whitelist = {
|
||||
static const FpDeviceClass whitelist = {
|
||||
.type = FP_DEVICE_TYPE_USB,
|
||||
.id_table = whitelist_id_table,
|
||||
.full_name = "Hardcoded whitelist"
|
||||
};
|
||||
|
||||
GHashTable *printed = NULL;
|
||||
|
||||
static void print_driver (struct fp_driver *driver)
|
||||
static void print_driver (const FpDeviceClass *cls)
|
||||
{
|
||||
int i, j, blacklist, num_printed;
|
||||
const FpIdEntry *entry;
|
||||
gint num_printed = 0;
|
||||
|
||||
num_printed = 0;
|
||||
if (cls->type != FP_DEVICE_TYPE_USB)
|
||||
return;
|
||||
|
||||
for (i = 0; driver->id_table[i].vendor != 0; i++) {
|
||||
for (entry = cls->id_table; entry->vid != 0; entry++) {
|
||||
const FpIdEntry *bl_entry;
|
||||
char *key;
|
||||
|
||||
blacklist = 0;
|
||||
for (j = 0; blacklist_id_table[j].vendor != 0; j++) {
|
||||
if (driver->id_table[i].vendor == blacklist_id_table[j].vendor &&
|
||||
driver->id_table[i].product == blacklist_id_table[j].product) {
|
||||
blacklist = 1;
|
||||
for (bl_entry = blacklist_id_table; bl_entry->vid != 0; bl_entry++) {
|
||||
if (entry->vid == bl_entry->vid && entry->pid == bl_entry->pid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (blacklist)
|
||||
if (bl_entry->vid != 0)
|
||||
continue;
|
||||
|
||||
key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product);
|
||||
key = g_strdup_printf ("%04x:%04x", entry->vid, entry->pid);
|
||||
|
||||
if (g_hash_table_lookup (printed, key) != NULL) {
|
||||
g_free (key);
|
||||
|
@ -76,28 +78,42 @@ static void print_driver (struct fp_driver *driver)
|
|||
g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
|
||||
|
||||
if (num_printed == 0)
|
||||
printf ("# %s\n", driver->full_name);
|
||||
g_print ("# %s\n", cls->full_name);
|
||||
|
||||
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n", driver->id_table[i].vendor, driver->id_table[i].product);
|
||||
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n", driver->id_table[i].vendor, driver->id_table[i].product, driver->full_name);
|
||||
g_print ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n",
|
||||
entry->vid, entry->pid);
|
||||
g_print ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n",
|
||||
entry->vid, entry->pid, cls->full_name);
|
||||
num_printed++;
|
||||
}
|
||||
|
||||
if (num_printed > 0)
|
||||
printf ("\n");
|
||||
g_print ("\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
struct fp_driver **list;
|
||||
g_autoptr(GArray) drivers = g_array_new (FALSE, FALSE, sizeof(GType));
|
||||
guint i;
|
||||
|
||||
list = fprint_get_drivers ();
|
||||
g_print ("%p\n", drivers);
|
||||
g_print ("%p\n", fpi_get_driver_types);
|
||||
fpi_get_driver_types (drivers);
|
||||
|
||||
printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
|
||||
for (i = 0; list[i] != NULL; i++) {
|
||||
print_driver (list[i]);
|
||||
for (i = 0; i < drivers->len; i++) {
|
||||
GType driver = g_array_index (drivers, GType, i);
|
||||
FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver));
|
||||
|
||||
if (cls->type != FP_DEVICE_TYPE_USB) {
|
||||
g_type_class_unref (cls);
|
||||
continue;
|
||||
}
|
||||
|
||||
print_driver (cls);
|
||||
|
||||
g_type_class_unref (cls);
|
||||
}
|
||||
|
||||
print_driver (&whitelist);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Main definitions for libfprint
|
||||
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -17,411 +17,9 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __FPRINT_H__
|
||||
#define __FPRINT_H__
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
/**
|
||||
* LIBFPRINT_DEPRECATED:
|
||||
*
|
||||
* Expands to the GNU C deprecated attribute if the compiler is `gcc`. When
|
||||
* called with the `-Wdeprecated-declarations` option, `gcc` will generate warnings
|
||||
* when deprecated interfaces are used.
|
||||
*/
|
||||
#define LIBFPRINT_DEPRECATED __attribute__((__deprecated__))
|
||||
|
||||
/**
|
||||
* fp_dscv_dev:
|
||||
*
|
||||
* #fp_dscv_dev is an opaque structure type. You must access it using the
|
||||
* functions in this section.
|
||||
*/
|
||||
struct fp_dscv_dev;
|
||||
|
||||
/**
|
||||
* fp_dev:
|
||||
*
|
||||
* #fp_dev is an opaque structure type. You must access it using the
|
||||
* functions in this section.
|
||||
*/
|
||||
struct fp_dev;
|
||||
|
||||
/**
|
||||
* fp_driver:
|
||||
*
|
||||
* #fp_driver is an opaque structure type. You must access it using the
|
||||
* functions in this section.
|
||||
*/
|
||||
struct fp_driver;
|
||||
|
||||
/**
|
||||
* fp_print_data:
|
||||
*
|
||||
* #fp_print_data is an opaque structure type. You must access it using the
|
||||
* functions in this section.
|
||||
*/
|
||||
struct fp_print_data;
|
||||
|
||||
/**
|
||||
* fp_img:
|
||||
*
|
||||
* #fp_img is an opaque structure type. You must access it using the
|
||||
* functions in this section.
|
||||
*/
|
||||
struct fp_img;
|
||||
|
||||
/* misc/general stuff */
|
||||
|
||||
/**
|
||||
* fp_finger:
|
||||
* @LEFT_THUMB: Left thumb
|
||||
* @LEFT_INDEX: Left index finger
|
||||
* @LEFT_MIDDLE: Left middle finger
|
||||
* @LEFT_RING: Left ring finger
|
||||
* @LEFT_LITTLE: Left little finger
|
||||
* @RIGHT_THUMB: Right thumb
|
||||
* @RIGHT_INDEX: Right index finger
|
||||
* @RIGHT_MIDDLE: Right middle finger
|
||||
* @RIGHT_RING: Right ring finger
|
||||
* @RIGHT_LITTLE: Right little finger
|
||||
*
|
||||
* Numeric codes used to refer to fingers (and thumbs) of a human. These are
|
||||
* purposely not available as strings, to avoid getting the library tangled up
|
||||
* in localization efforts.
|
||||
*/
|
||||
enum fp_finger {
|
||||
LEFT_THUMB = 1,
|
||||
LEFT_INDEX,
|
||||
LEFT_MIDDLE,
|
||||
LEFT_RING,
|
||||
LEFT_LITTLE,
|
||||
RIGHT_THUMB,
|
||||
RIGHT_INDEX,
|
||||
RIGHT_MIDDLE,
|
||||
RIGHT_RING,
|
||||
RIGHT_LITTLE,
|
||||
};
|
||||
|
||||
/**
|
||||
* fp_scan_type:
|
||||
* @FP_SCAN_TYPE_PRESS: the reader has a surface area that covers the whole finger
|
||||
* @FP_SCAN_TYPE_SWIPE: the reader requires swiping the finger on a smaller area
|
||||
*
|
||||
* Numeric codes used to refer to the scan type of the device. Devices require
|
||||
* either swiping or pressing the finger on the device. This is useful for
|
||||
* front-ends.
|
||||
*/
|
||||
enum fp_scan_type {
|
||||
FP_SCAN_TYPE_PRESS = 0,
|
||||
FP_SCAN_TYPE_SWIPE,
|
||||
};
|
||||
|
||||
/* Drivers */
|
||||
const char *fp_driver_get_name(struct fp_driver *drv);
|
||||
const char *fp_driver_get_full_name(struct fp_driver *drv);
|
||||
uint16_t fp_driver_get_driver_id(struct fp_driver *drv);
|
||||
enum fp_scan_type fp_driver_get_scan_type(struct fp_driver *drv);
|
||||
int fp_driver_supports_imaging(struct fp_driver *drv);
|
||||
|
||||
/* Device discovery */
|
||||
struct fp_dscv_dev **fp_discover_devs(void);
|
||||
void fp_dscv_devs_free(struct fp_dscv_dev **devs);
|
||||
struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev);
|
||||
uint16_t fp_dscv_dev_get_driver_id(struct fp_dscv_dev *dev);
|
||||
uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev);
|
||||
int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev,
|
||||
struct fp_print_data *print);
|
||||
|
||||
/* Device handling */
|
||||
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);
|
||||
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);
|
||||
|
||||
/**
|
||||
* fp_capture_result:
|
||||
* @FP_CAPTURE_COMPLETE: Capture completed successfully, the capture data has been returned to the caller.
|
||||
* @FP_CAPTURE_FAIL: Capture failed
|
||||
*
|
||||
* Whether a capture failed or completed.
|
||||
*/
|
||||
enum fp_capture_result {
|
||||
FP_CAPTURE_COMPLETE = 0,
|
||||
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 **img);
|
||||
int fp_dev_get_img_width(struct fp_dev *dev);
|
||||
int fp_dev_get_img_height(struct fp_dev *dev);
|
||||
|
||||
/**
|
||||
* fp_enroll_result:
|
||||
* @FP_ENROLL_COMPLETE: Enrollment completed successfully, the enrollment data has been
|
||||
* returned to the caller.
|
||||
* @FP_ENROLL_FAIL: Enrollment failed due to incomprehensible data; this may occur when
|
||||
* the user scans a different finger on each enroll stage.
|
||||
* @FP_ENROLL_PASS: Enroll stage passed; more stages are need to complete the process.
|
||||
* @FP_ENROLL_RETRY: The enrollment scan did not succeed due to poor scan quality or
|
||||
* other general user scanning problem.
|
||||
* @FP_ENROLL_RETRY_TOO_SHORT: The enrollment scan did not succeed because the finger swipe was
|
||||
* too short.
|
||||
* @FP_ENROLL_RETRY_CENTER_FINGER: The enrollment scan did not succeed because the finger was not
|
||||
* centered on the scanner.
|
||||
* @FP_ENROLL_RETRY_REMOVE_FINGER: The verification scan did not succeed due to quality or pressure
|
||||
* problems; the user should remove their finger from the scanner before
|
||||
* retrying.
|
||||
*
|
||||
*
|
||||
* Enrollment result codes returned from fp_enroll_finger().
|
||||
* Result codes with RETRY in the name suggest that the scan failed due to
|
||||
* user error. Applications will generally want to inform the user of the
|
||||
* problem and then retry the enrollment stage. For more info on the semantics
|
||||
* of interpreting these result codes and tracking enrollment process, see
|
||||
* [Enrolling](libfprint-Devices-operations.html#enrolling)
|
||||
*/
|
||||
enum fp_enroll_result {
|
||||
FP_ENROLL_COMPLETE = 1,
|
||||
FP_ENROLL_FAIL,
|
||||
FP_ENROLL_PASS,
|
||||
FP_ENROLL_RETRY = 100,
|
||||
FP_ENROLL_RETRY_TOO_SHORT,
|
||||
FP_ENROLL_RETRY_CENTER_FINGER,
|
||||
FP_ENROLL_RETRY_REMOVE_FINGER,
|
||||
};
|
||||
|
||||
int fp_enroll_finger_img(struct fp_dev *dev, struct fp_print_data **print_data,
|
||||
struct fp_img **img);
|
||||
int fp_enroll_finger(struct fp_dev *dev,
|
||||
struct fp_print_data **print_data);
|
||||
|
||||
/**
|
||||
* fp_verify_result:
|
||||
* @FP_VERIFY_NO_MATCH: The scan completed successfully, but the newly scanned fingerprint
|
||||
* does not match the fingerprint being verified against.
|
||||
* In the case of identification, this return code indicates that the
|
||||
* scanned finger could not be found in the print gallery.
|
||||
* @FP_VERIFY_MATCH: The scan completed successfully and the newly scanned fingerprint does
|
||||
* match the fingerprint being verified, or in the case of identification,
|
||||
* the scanned fingerprint was found in the print gallery.
|
||||
* @FP_VERIFY_RETRY: The scan did not succeed due to poor scan quality or other general
|
||||
* user scanning problem.
|
||||
* @FP_VERIFY_RETRY_TOO_SHORT: The scan did not succeed because the finger swipe was too short.
|
||||
* @FP_VERIFY_RETRY_CENTER_FINGER: The scan did not succeed because the finger was not centered on the
|
||||
* scanner.
|
||||
* @FP_VERIFY_RETRY_REMOVE_FINGER: The scan did not succeed due to quality or pressure problems; the user
|
||||
* should remove their finger from the scanner before retrying.
|
||||
*
|
||||
* Verification result codes returned from fp_verify_finger(). Return codes
|
||||
* are also shared with fp_identify_finger().
|
||||
* Result codes with RETRY in the name suggest that the scan failed due to
|
||||
* user error. Applications will generally want to inform the user of the
|
||||
* problem and then retry the verify operation.
|
||||
*/
|
||||
enum fp_verify_result {
|
||||
FP_VERIFY_NO_MATCH = 0,
|
||||
FP_VERIFY_MATCH = 1,
|
||||
FP_VERIFY_RETRY = FP_ENROLL_RETRY,
|
||||
FP_VERIFY_RETRY_TOO_SHORT = FP_ENROLL_RETRY_TOO_SHORT,
|
||||
FP_VERIFY_RETRY_CENTER_FINGER = FP_ENROLL_RETRY_CENTER_FINGER,
|
||||
FP_VERIFY_RETRY_REMOVE_FINGER = FP_ENROLL_RETRY_REMOVE_FINGER,
|
||||
};
|
||||
|
||||
int fp_verify_finger_img(struct fp_dev *dev,
|
||||
struct fp_print_data *enrolled_print, struct fp_img **img);
|
||||
int fp_verify_finger(struct fp_dev *dev,
|
||||
struct fp_print_data *enrolled_print);
|
||||
|
||||
int fp_dev_supports_identification(struct fp_dev *dev);
|
||||
int fp_identify_finger_img(struct fp_dev *dev,
|
||||
struct fp_print_data **print_gallery, size_t *match_offset,
|
||||
struct fp_img **img);
|
||||
int fp_identify_finger(struct fp_dev *dev,
|
||||
struct fp_print_data **print_gallery, size_t *match_offset);
|
||||
|
||||
/* Data handling */
|
||||
void fp_print_data_free(struct fp_print_data *data);
|
||||
size_t fp_print_data_get_data(struct fp_print_data *data, unsigned char **ret);
|
||||
struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
|
||||
size_t buflen);
|
||||
uint16_t fp_print_data_get_driver_id(struct fp_print_data *data);
|
||||
uint32_t fp_print_data_get_devtype(struct fp_print_data *data);
|
||||
|
||||
/* Image handling */
|
||||
|
||||
/**
|
||||
* fp_minutia:
|
||||
*
|
||||
* #fp_minutia is an opaque structure type. You must access it using the
|
||||
* functions in this section.
|
||||
*/
|
||||
struct fp_minutia;
|
||||
|
||||
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);
|
||||
void fp_img_standardize(struct fp_img *img);
|
||||
struct fp_img *fp_img_binarize(struct fp_img *img);
|
||||
struct fp_minutia **fp_img_get_minutiae(struct fp_img *img, int *nr_minutiae);
|
||||
int fp_minutia_get_coords(struct fp_minutia *minutia, int *coord_x, int *coord_y);
|
||||
void fp_img_free(struct fp_img *img);
|
||||
|
||||
/* Polling and timing */
|
||||
|
||||
/**
|
||||
* fp_pollfd:
|
||||
* @fd: a file descriptor
|
||||
* @events: Event flags to poll for from `<poll.h>`
|
||||
*
|
||||
* A structure representing a file descriptor and the @events to poll
|
||||
* for, as returned by fp_get_pollfds().
|
||||
*/
|
||||
struct fp_pollfd {
|
||||
int fd;
|
||||
short int events;
|
||||
};
|
||||
|
||||
int fp_handle_events_timeout(struct timeval *timeout);
|
||||
int fp_handle_events(void);
|
||||
ssize_t fp_get_pollfds(struct fp_pollfd **pollfds);
|
||||
int fp_get_next_timeout(struct timeval *tv);
|
||||
|
||||
/**
|
||||
* fp_pollfd_added_cb:
|
||||
* @fd: the new file descriptor
|
||||
* @events: events to monitor for, see `<poll.h>` for the possible values
|
||||
*
|
||||
* Type definition for a function that will be called when a new
|
||||
* event source is added. The @events argument is a flag as defined in
|
||||
* `<poll.h>` such as `POLLIN`, or `POLLOUT`. See fp_set_pollfd_notifiers().
|
||||
*/
|
||||
typedef void (*fp_pollfd_added_cb)(int fd, short int events);
|
||||
|
||||
/**
|
||||
* fp_pollfd_removed_cb:
|
||||
* @fd: the file descriptor to stop monitoring
|
||||
*
|
||||
* Type definition for a function that will be called when an
|
||||
* event source is removed. See fp_set_pollfd_notifiers().
|
||||
*/
|
||||
typedef void (*fp_pollfd_removed_cb)(int fd);
|
||||
void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
|
||||
fp_pollfd_removed_cb removed_cb);
|
||||
|
||||
/* Library */
|
||||
int fp_init(void);
|
||||
void fp_exit(void);
|
||||
|
||||
/* Asynchronous I/O */
|
||||
|
||||
/**
|
||||
* fp_operation_stop_cb:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @user_data: user data passed to the callback
|
||||
*
|
||||
* Type definition for a function that will be called when fp_async_dev_close(),
|
||||
* fp_async_verify_stop(), fp_async_identify_stop() or fp_async_capture_stop()
|
||||
* finishes.
|
||||
*/
|
||||
typedef void (*fp_operation_stop_cb)(struct fp_dev *dev, void *user_data);
|
||||
|
||||
/**
|
||||
* fp_img_operation_cb:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @result: an #fp_verify_result for fp_async_verify_start(), or an #fp_capture_result
|
||||
* for fp_async_capture_start(), or a negative value on error
|
||||
* @img: the captured #fp_img if capture or verification was successful
|
||||
* @user_data: user data passed to the callback
|
||||
*
|
||||
* Type definition for a function that will be called when fp_async_verify_start()
|
||||
* or fp_async_capture_start() finished.
|
||||
*/
|
||||
typedef void (*fp_img_operation_cb)(struct fp_dev *dev, int result,
|
||||
struct fp_img *img, void *user_data);
|
||||
|
||||
/**
|
||||
* fp_dev_open_cb:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @status: 0 on success, or a negative value on error
|
||||
* @user_data: user data passed to the callback
|
||||
*
|
||||
* Type definition for a function that will be called when fp_async_dev_open
|
||||
* finishes.
|
||||
*/
|
||||
typedef void (*fp_dev_open_cb)(struct fp_dev *dev, int status, void *user_data);
|
||||
|
||||
int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb callback,
|
||||
void *user_data);
|
||||
|
||||
void fp_async_dev_close(struct fp_dev *dev, fp_operation_stop_cb callback,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* fp_enroll_stage_cb:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @result: a #fp_enroll_result on success, or a negative value on failure
|
||||
* @print: the enrollment data from the final stage
|
||||
* @img: an #fp_img to free with fp_img_free()
|
||||
* @user_data: user data passed to the callback
|
||||
*
|
||||
* Type definition for a function that will be called when
|
||||
* fp_async_enroll_start() finishes. See fp_enroll_finger_img() for
|
||||
* the expected behaviour of your program based on the @result.
|
||||
*/
|
||||
typedef void (*fp_enroll_stage_cb)(struct fp_dev *dev, int result,
|
||||
struct fp_print_data *print, struct fp_img *img, void *user_data);
|
||||
|
||||
int fp_async_enroll_start(struct fp_dev *dev, fp_enroll_stage_cb callback,
|
||||
void *user_data);
|
||||
|
||||
int fp_async_enroll_stop(struct fp_dev *dev, fp_operation_stop_cb callback,
|
||||
void *user_data);
|
||||
|
||||
int fp_async_verify_start(struct fp_dev *dev, struct fp_print_data *data,
|
||||
fp_img_operation_cb callback, void *user_data);
|
||||
|
||||
int fp_async_verify_stop(struct fp_dev *dev, fp_operation_stop_cb callback,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* fp_identify_cb:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @result: a #fp_verify_result on success, or a negative value on error.
|
||||
* @match_offset: the array index of the matched gallery print (if any was found).
|
||||
* Only valid if %FP_VERIFY_MATCH was returned.
|
||||
* @img: the scan image, it must be freed with fp_img_free() after use.
|
||||
* @user_data: user data passed to the callback
|
||||
*
|
||||
* Type definition for a function that will be called when fp_async_identify_start()
|
||||
* finishes.
|
||||
*/
|
||||
typedef void (*fp_identify_cb)(struct fp_dev *dev, int result,
|
||||
size_t match_offset, struct fp_img *img, void *user_data);
|
||||
int fp_async_identify_start(struct fp_dev *dev, struct fp_print_data **gallery,
|
||||
fp_identify_cb callback, void *user_data);
|
||||
|
||||
int fp_async_identify_stop(struct fp_dev *dev, fp_operation_stop_cb callback,
|
||||
void *user_data);
|
||||
|
||||
int fp_async_capture_start(struct fp_dev *dev, int unconditional, fp_img_operation_cb callback, void *user_data);
|
||||
|
||||
int fp_async_capture_stop(struct fp_dev *dev, fp_operation_stop_cb callback, void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#include "fp-context.h"
|
||||
#include "fp-device.h"
|
||||
#include "fp-image.h"
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
LIBFPRINT_2.0.0 {
|
||||
global:
|
||||
fp_*;
|
||||
|
||||
/* Needs to be public for the listing commands. */
|
||||
fpi_get_driver_types;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
libfprint_sources = [
|
||||
'fpi-async.c',
|
||||
'fp-context.c',
|
||||
'fp-device.c',
|
||||
'fp-image.c',
|
||||
'fp-print.c',
|
||||
'fp-image-device.c',
|
||||
'fpi-assembling.c',
|
||||
'fpi-core.c',
|
||||
'fpi-data.c',
|
||||
'fpi-dev.c',
|
||||
'fpi-dev-img.c',
|
||||
'fpi-img.c',
|
||||
'fpi-ssm.c',
|
||||
'fpi-sync.c',
|
||||
'fpi-poll.c',
|
||||
'fpi-usb.c',
|
||||
'fpi-usb-transfer.c',
|
||||
]
|
||||
|
||||
libfprint_public_headers = [
|
||||
'fp-context.h',
|
||||
'fp-device.h',
|
||||
'fp-image.h',
|
||||
'fp-print.h',
|
||||
]
|
||||
|
||||
libfprint_private_headers = [
|
||||
'fpi-assembling.h',
|
||||
'fpi-device.h',
|
||||
'fpi-image.h',
|
||||
'fpi-image-device.h',
|
||||
'fpi-print.h',
|
||||
]
|
||||
|
||||
nbis_sources = [
|
||||
|
@ -134,29 +146,32 @@ if aes3k
|
|||
endif
|
||||
|
||||
other_sources = []
|
||||
if imaging_dep.found()
|
||||
other_sources += [ 'fpi-img-pixman.c' ]
|
||||
endif
|
||||
|
||||
libfprint_sources += configure_file(input: 'empty_file',
|
||||
output: 'drivers_definitions.h',
|
||||
capture: true,
|
||||
command: [
|
||||
'echo',
|
||||
drivers_struct_list
|
||||
])
|
||||
fp_enums = gnome.mkenums_simple('fp-enums',
|
||||
sources: libfprint_public_headers,
|
||||
install_header : true)
|
||||
fp_enums_h = fp_enums[1]
|
||||
|
||||
libfprint_sources += configure_file(input: 'empty_file',
|
||||
output: 'drivers_arrays.h',
|
||||
capture: true,
|
||||
command: [
|
||||
'echo',
|
||||
drivers_primitive_array + '\n\n' + drivers_img_array
|
||||
])
|
||||
fpi_enums = gnome.mkenums_simple('fpi-enums',
|
||||
sources: libfprint_private_headers,
|
||||
install_header : true)
|
||||
fpi_enums_h = fpi_enums[1]
|
||||
|
||||
deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep ]
|
||||
drivers_sources += configure_file(input: 'empty_file',
|
||||
output: 'fp-drivers.c',
|
||||
capture: true,
|
||||
command: [
|
||||
'echo',
|
||||
drivers_type_list + '\n\n' + drivers_type_func
|
||||
])
|
||||
|
||||
mapfile = 'libfprint.ver'
|
||||
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
|
||||
|
||||
deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep, gio_dep ]
|
||||
libfprint = library('fprint',
|
||||
libfprint_sources + drivers_sources + nbis_sources + other_sources,
|
||||
libfprint_sources + fp_enums + fpi_enums +
|
||||
drivers_sources + nbis_sources + other_sources,
|
||||
soversion: soversion,
|
||||
version: libversion,
|
||||
c_args: common_cflags + drivers_cflags,
|
||||
|
@ -164,14 +179,17 @@ libfprint = library('fprint',
|
|||
root_inc,
|
||||
include_directories('nbis/include'),
|
||||
],
|
||||
link_args : vflag,
|
||||
link_depends : mapfile,
|
||||
dependencies: deps,
|
||||
install: true)
|
||||
|
||||
libfprint_dep = declare_dependency(link_with: libfprint,
|
||||
sources: [ fp_enums_h ],
|
||||
include_directories: root_inc,
|
||||
dependencies: glib_dep)
|
||||
dependencies: [glib_dep, gusb_dep, gio_dep])
|
||||
|
||||
install_headers(['fprint.h'], subdir: 'libfprint')
|
||||
install_headers(['fprint.h'] + libfprint_public_headers, subdir: 'libfprint')
|
||||
|
||||
udev_rules = executable('fprint-list-udev-rules',
|
||||
'fprint-list-udev-rules.c',
|
||||
|
@ -197,3 +215,35 @@ supported_devices = executable('fprint-list-supported-devices',
|
|||
],
|
||||
dependencies: [ deps, libfprint_dep ],
|
||||
install: false)
|
||||
|
||||
|
||||
if get_option('introspection')
|
||||
# We do *not* include the private header here
|
||||
libfprint_girtarget = gnome.generate_gir(libfprint,
|
||||
sources : fp_enums + [
|
||||
libfprint_public_headers,
|
||||
libfprint_sources,
|
||||
],
|
||||
nsversion : '2.0',
|
||||
namespace : 'FPrint',
|
||||
symbol_prefix : 'fp_',
|
||||
identifier_prefix : 'Fp',
|
||||
export_packages : 'fprint',
|
||||
extra_args : [
|
||||
'--c-include=fprint.h',
|
||||
],
|
||||
link_with : libfprint,
|
||||
dependencies : [
|
||||
gio_dep,
|
||||
gusb_dep,
|
||||
],
|
||||
includes : [
|
||||
'Gio-2.0',
|
||||
'GObject-2.0',
|
||||
'GUsb-1.0',
|
||||
],
|
||||
install : true
|
||||
)
|
||||
libfprint_gir = libfprint_girtarget[0]
|
||||
libfprint_typelib = libfprint_girtarget[1]
|
||||
endif
|
||||
|
|
28
meson.build
28
meson.build
|
@ -8,6 +8,8 @@ project('libfprint', [ 'c', 'cpp' ],
|
|||
],
|
||||
meson_version: '>= 0.45.0')
|
||||
|
||||
gnome = import('gnome')
|
||||
|
||||
add_project_arguments([ '-D_GNU_SOURCE' ], language: 'c')
|
||||
add_project_arguments([ '-DG_LOG_DOMAIN="libfprint"' ], language: 'c')
|
||||
|
||||
|
@ -19,7 +21,6 @@ host_system = host_machine.system()
|
|||
|
||||
common_cflags = cc.get_supported_arguments([
|
||||
'-fgnu89-inline',
|
||||
'-fvisibility=hidden',
|
||||
'-std=gnu99',
|
||||
'-Wall',
|
||||
'-Wundef',
|
||||
|
@ -42,6 +43,7 @@ libversion = '@0@.@1@.@2@'.format(soversion, current, revision)
|
|||
|
||||
# Dependencies
|
||||
glib_dep = dependency('glib-2.0', version: '>= 2.50')
|
||||
gio_dep = dependency('gio-unix-2.0', version: '>= 2.44.0')
|
||||
gusb_dep = dependency('gusb', version: '>= 0.3.0')
|
||||
mathlib_dep = cc.find_library('m', required: false)
|
||||
|
||||
|
@ -67,6 +69,7 @@ endif
|
|||
|
||||
nss_dep = dependency('', required: false)
|
||||
imaging_dep = dependency('', required: false)
|
||||
libfprint_conf.set10('HAVE_PIXMAN', false)
|
||||
foreach driver: drivers
|
||||
if driver == 'uru4000'
|
||||
nss_dep = dependency('nss', required: false)
|
||||
|
@ -79,27 +82,23 @@ foreach driver: drivers
|
|||
if not imaging_dep.found()
|
||||
error('pixman is required for imaging support')
|
||||
endif
|
||||
|
||||
libfprint_conf.set10('HAVE_PIXMAN', true)
|
||||
endif
|
||||
if not all_drivers.contains(driver)
|
||||
error('Invalid driver \'' + driver + '\'')
|
||||
endif
|
||||
endforeach
|
||||
|
||||
# Export the drivers' structures to the core code
|
||||
drivers_struct_list = ''
|
||||
drivers_img_array = 'static struct fp_img_driver * const img_drivers[] = {\n'
|
||||
drivers_primitive_array = 'static struct fp_driver * const primitive_drivers[] = {\n'
|
||||
# Export the drivers' types to the core code
|
||||
drivers_type_list = '#include <glib-object.h>\n'
|
||||
drivers_type_func = 'void fpi_get_driver_types(GArray *drivers)\n{\n\tGType t;\n'
|
||||
foreach driver: drivers
|
||||
if primitive_drivers.contains(driver)
|
||||
drivers_struct_list += 'extern struct fp_driver ' + driver + '_driver;\n'
|
||||
drivers_primitive_array += ' &' + driver + '_driver,\n'
|
||||
else
|
||||
drivers_struct_list += 'extern struct fp_img_driver ' + driver + '_driver;\n'
|
||||
drivers_img_array += ' &' + driver + '_driver,\n'
|
||||
endif
|
||||
drivers_type_list += 'extern GType (fpi_device_' + driver + '_get_type) (void);\n'
|
||||
drivers_type_func += ' t = fpi_device_' + driver + '_get_type(); g_array_append_val (drivers, t);\n'
|
||||
endforeach
|
||||
drivers_img_array += '};'
|
||||
drivers_primitive_array += '};'
|
||||
drivers_type_list += ''
|
||||
drivers_type_func += '};'
|
||||
|
||||
root_inc = include_directories('.')
|
||||
|
||||
|
@ -129,7 +128,6 @@ if get_option('gtk-examples')
|
|||
endif
|
||||
endif
|
||||
|
||||
libfprint_conf.set('API_EXPORTED', '__attribute__((visibility("default")))')
|
||||
configure_file(output: 'config.h', configuration: libfprint_conf)
|
||||
|
||||
subdir('libfprint')
|
||||
|
|
|
@ -2,6 +2,10 @@ option('drivers',
|
|||
description: 'Drivers to integrate, "default" selects the default set, "all" selects all drivers',
|
||||
type: 'string',
|
||||
value: 'default')
|
||||
option('introspection',
|
||||
description: 'Build GObject Introspection repository',
|
||||
type: 'boolean',
|
||||
value: true)
|
||||
option('udev_rules',
|
||||
description: 'Whether to create a udev rules file',
|
||||
type: 'boolean',
|
||||
|
|
Loading…
Reference in a new issue