From 96f2e07cddc17ab95235c4b828d4b66ddcf49bbe Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 26 Sep 2018 14:11:09 +0200 Subject: [PATCH] lib: Add USB transfer helpers Those helpers will provide more arguments in their callbacks, and handle the libusb_transfer lifecycle, making it easier to use for drivers. --- doc/libfprint-sections.txt | 6 ++ libfprint/fpi-usb.c | 158 +++++++++++++++++++++++++++++++++++++ libfprint/fpi-usb.h | 44 +++++++++++ 3 files changed, 208 insertions(+) diff --git a/doc/libfprint-sections.txt b/doc/libfprint-sections.txt index 6b02bcd..0a95c2d 100644 --- a/doc/libfprint-sections.txt +++ b/doc/libfprint-sections.txt @@ -197,5 +197,11 @@ FP_INSTANCE_DATA
fpi-usb.h fpi-usb +fpi_usb_transfer + +fpi_usb_transfer_cb_fn fpi_usb_alloc +fpi_usb_fill_bulk_transfer +fpi_usb_submit_transfer +fpi_usb_cancel_transfer
diff --git a/libfprint/fpi-usb.c b/libfprint/fpi-usb.c index 1b8a92e..8b06ebb 100644 --- a/libfprint/fpi-usb.c +++ b/libfprint/fpi-usb.c @@ -18,6 +18,7 @@ */ #include "fpi-usb.h" +#include "drivers_api.h" /** * SECTION:fpi-usb @@ -57,6 +58,14 @@ mem_error (const char *format, _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: * @@ -75,3 +84,152 @@ fpi_usb_alloc(void) 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); +} diff --git a/libfprint/fpi-usb.h b/libfprint/fpi-usb.h index c966678..625a7e7 100644 --- a/libfprint/fpi-usb.h +++ b/libfprint/fpi-usb.h @@ -21,7 +21,51 @@ #define __FPI_USB_H__ #include +#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