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.
This commit is contained in:
Bastien Nocera 2018-09-26 14:11:09 +02:00
parent 192c4f3cfc
commit 96f2e07cdd
3 changed files with 208 additions and 0 deletions

View file

@ -197,5 +197,11 @@ FP_INSTANCE_DATA
<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>

View file

@ -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);
}

View file

@ -21,7 +21,51 @@
#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