From 91d8de40f1e23f7f3ac11da3f407a112999dc913 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pawe=C5=82=20Szewczyk?= Date: Mon, 7 Mar 2016 15:23:23 +0100 Subject: [PATCH] libhusb: Add async transfer API MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: Ia1a09dfaa32e6f420eb64f3df22825e9f6349e6d Signed-off-by: Paweł Szewczyk --- include/libhusb.h | 179 +++++++++++++++++++++++++++++++++++++ include/libhusb_internal.h | 4 + src/libhusb.c | 155 ++++++++++++++++++++++++++++++++ 3 files changed, 338 insertions(+) diff --git a/include/libhusb.h b/include/libhusb.h index 13d6306..f323a43 100644 --- a/include/libhusb.h +++ b/include/libhusb.h @@ -631,6 +631,185 @@ int libhusb_interrupt_transfer(libhusb_device_handle *handle, uint8_t endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout); +/* Async IO */ + +/** + * @brief Asynchronous transfer callback function + */ +typedef void (*libhusb_transfer_cb_fn)(int status, unsigned char *buffer, int len, void *user_data); + +/** + * @brief Asynchronous transfer handler + */ +struct libhusb_transfer; + +typedef struct libhusb_transfer libhusb_transfer; + +/** + * @brief Create an asynchronous transfer handler + * @details To perform asynchronous transfer, we need a handler structure which will + * be later passed to libhusb_submit_transfer(). This function allocate memory for the handler, + * but it will be uninitialized until libhusb_fill_* function will be called. + * + * For isochronous transfer, iso_packets should be set to appropriate number of packet descriptors. + * If you do not intend to use isochronous transfer, it should be set to 0. + * + * @param transfer Pointer to be filled with pointer to newly allocated transfer structure + * @param iso_packets Number of iso packets descriptors to be allocated. + * @return 0 on success, negative error code otherwise + */ +int libhusb_create_transfer(libhusb_transfer **transfer, int iso_packets); + +/** + * @brief Submit an asynchronous transfer1 + * @details The transfer structure should be initialized with transfer parameters and + * callback function. When transfer is completed, the callback function is called. + * + * @param transfer Transfer handler to be submited + * @return 0 on success, negative error code otherwise + */ +int libhusb_submit_transfer(libhusb_transfer *transfer); + +/** + * @brief Cancel a submited asynchronous transfer + * @details If transfer was submitted, this will cancel it. Callback function will be + * called with CANCEL status + * + * @param transfer Transfer handler to be canceled + * @return 0 on success, negative error code otherwise + */ +int libhusb_cancel_transfer(libhusb_transfer *transfer); + +/** + * @brief Destroy asynchronous transfer handler + * @details Deallocate memory allocated by libhusb_create_transfer + * + * @param transfer Transfer handler to be destroyed + */ +void libhusb_destroy_transfer(libhusb_transfer *transfer); + +/** + * @brief Fill an interrupt asynchronous transfer + * @details Before submitting transfer it should be initialized. This function initialize it with + * values required for interrupt transfer. + * + * @param transfer Transfer handle + * @param dev_handle Device handle + * @param callback Function to be called when transfer is finished + * @param endpoint Number of endpoint + * @param buffer Data buffer + * @param length Length of data buffer + * @param user_data Pointer to additional user data that will be passed to callback function + * @param timeout Timeout for the transfer in miliseconds + * @return 0 on success, negative error code otherwise + */ +int libhusb_fill_interrupt_transfer(libhusb_transfer *transfer, libhusb_device_handle *dev_handle, + libhusb_transfer_cb_fn callback, uint8_t endpoint, unsigned char *buffer, int length, + void *user_data, unsigned int timeout); + +/** + * @brief Fill a bulk asynchronous transfer + * @details Before submitting transfer it should be initialized. This funtion initialize it with + * values required for bulk transfer. + * + * @param transfer Transfer handle + * @param dev_handle Device handle + * @param callback Function to be called when transfer is finished + * @param endpoint Number of endpoint + * @param buffer Data buffer + * @param length Length of data buffer + * @param user_data Pointer to additional user data that will be passed to callback function + * @param timeout Timeout for the transfer in miliseconds + * @return 0 on success, negative error code otherwise + */ +int libhusb_fill_bulk_transfer(libhusb_transfer *transfer, libhusb_device_handle *dev_handle, + libhusb_transfer_cb_fn callback, uint8_t endpoint, unsigned char *buffer, int length, + void *user_data, unsigned int timeout); + +/** + * @brief Fill setup packet for a control transfer + * @details This is helper function to populate first 8 bytes of control transfer buffer + * with given setup values. + * + * @param buffer Buffer to write the setup packet into + * @param bmRequestType Request type + * @param bRequest Request + * @param wValue Value + * @param wIndex Index + * @param wLength Length + */ +void libhusb_fill_control_setup(unsigned char *buffer, uint8_t bmRequestType, + uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength); + +/** + * @brief Fill an interrupt asynchronous transfer + * @details Before submitting transfer it should be initialized. This funtion initialize it with + * values required for control transfer. + * + * The first 8 bytes of buffer is interpreted as control setup packet. It contains information about + * length of the buffer. Use libhusb_fill_control_setup() function to initialize setup packet. + * + * @param transfer Transfer handle + * @param dev_handle Device handle + * @param callback Function to be called when transfer is finished + * @param buffer Data buffer + * @param user_data Pointer to additional user data that will be passed to callback function + * @param timeout Timeout for the transfer in miliseconds + * + * @par Example + * @code + * ... + * + * r = libhusb_create_transfer(&transfer); + * if (r < 0) { + * printf("Failed to create transfer)\n"; + * exit(1); + * } + * + * ... + * + * libhusb_fill_control_setup(buffer, request_type, request, value, index, length); + * r = libhusb_fill_control_transfer(transfer, dev_handle, buffer, callback, user_data, timeout); + * if (r < 0) { + * printf("Failed to fill control transfer\n"); + * libhusb_destroy_transfer(transfer); + * exit(1); + * } + * + * ... + * @endcode + * @return 0 on success, negative error code otherwise + */ +int libhusb_fill_control_transfer(libhusb_transfer *transfer, libhusb_device_handle *dev_handle, + libhusb_transfer_cb_fn callback, unsigned char *buffer, void *user_data, unsigned int timeout); + +/** + * @brief Fill an interrupt asynchronous transfer + * @details Before submitting transfer it should be initialized. This funtion initialize it with + * values required for isochronous transfer. + * + * @param transfer Transfer handle + * @param dev_handle Device handle + * @param callback Function to be called when transfer is finished + * @param endpoint Number of endpoint + * @param buffer Buffer to store data + * @param length Length of data buffer + * @param iso_packets Number of iso packets + * @param user_data Pointer to additional user data that will be passed to callback function + * @param timeout Timeout for the transfer in miliseconds + * @return 0 on success, negative error code otherwise + */ +int libhusb_fill_iso_transfer(libhusb_transfer *transfer, libhusb_device_handle *dev_handle, + libhusb_transfer_cb_fn callback, uint8_t endpoint, unsigned char *buffer, int length, + int iso_packets, void *user_data, unsigned int timeout); +/** + * @brief Handle all pending events + * @param ctx Library context + * @param completed Pointer to completion integer check, or NULL + * @return 0 on success, negative error code otherwise + */ +int libhusb_handle_events(libhusb_context *ctx, int *completed); + /** * @brief Translate error name * @details Translates error code to NULL terminated libhusb diff --git a/include/libhusb_internal.h b/include/libhusb_internal.h index 92cfb5f..4e0c3c7 100644 --- a/include/libhusb_internal.h +++ b/include/libhusb_internal.h @@ -39,3 +39,7 @@ struct libhusb_device_handle { /* TODO: replace with bit fields */ unsigned char driver_detached[MAX_NMB_OF_CONFIGS]; }; + +struct libhusb_transfer { + struct libusb_transfer *lusb_transfer; +}; diff --git a/src/libhusb.c b/src/libhusb.c index e36dd77..b905cb7 100644 --- a/src/libhusb.c +++ b/src/libhusb.c @@ -923,6 +923,161 @@ int libhusb_interrupt_transfer(libhusb_device_handle *handle, return ret; } +struct async_transfer_data { + libhusb_transfer_cb_fn cb; + void *user_data; +}; + +static void generic_transfer_cb(struct libusb_transfer *transfer) +{ + struct async_transfer_data *data; + + data = transfer->user_data; + data->cb(translate_error(transfer->status), transfer->buffer, transfer->actual_length, data->user_data); +} + +int libhusb_create_transfer(libhusb_transfer **transfer, int iso_packets) +{ + libhusb_transfer *rtransfer; + + assert(transfer); + + rtransfer = malloc(sizeof(*rtransfer)); + if (!rtransfer) { + _E("Failed to alloc transfer structure"); + return LIBHUSB_ERROR_NO_MEM; + } + + rtransfer->lusb_transfer = libusb_alloc_transfer(iso_packets); + rtransfer->lusb_transfer->user_data = NULL; + *transfer = rtransfer; + + return 0; +} + +int libhusb_submit_transfer(libhusb_transfer *transfer) +{ + int ret; + + ret = libusb_submit_transfer(transfer->lusb_transfer); + if (ret < 0) { + _E("Failed to submit async transfer: %d", ret); + ret = translate_error(ret); + } + + return ret; +} + +int libhusb_cancel_transfer(libhusb_transfer *transfer) +{ + int ret; + + ret = libusb_cancel_transfer(transfer->lusb_transfer); + if (ret < 0) { + _E("Failed to cancel async transfer: %d", ret); + ret = translate_error(ret); + } + + return ret; +} + +void libhusb_destroy_transfer(libhusb_transfer *transfer) +{ + libusb_free_transfer(transfer->lusb_transfer); + free(transfer->lusb_transfer->user_data); + free(transfer); +} + +void libhusb_fill_control_setup(unsigned char *buffer, uint8_t bmRequestType, + uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) +{ + assert(buffer); + + libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex, wLength); +} + +int libhusb_fill_interrupt_transfer(libhusb_transfer *transfer, libhusb_device_handle *handle, + libhusb_transfer_cb_fn callback, uint8_t endpoint, unsigned char *buffer, int length, + void *user_data, unsigned int timeout) +{ + struct async_transfer_data *data; + assert(transfer); + assert(transfer->lsusb_transfer); + assert(handle); + + data = malloc(sizeof(*data)); + if (!data) { + _E("malloc failed"); + return LIBHUSB_ERROR_NO_MEM; + } + + data->cb = callback; + data->user_data = user_data; + + /* it's either NULL, or was allocated by library */ + free(transfer->lusb_transfer->user_data); + libusb_fill_interrupt_transfer(transfer->lusb_transfer, handle->lusb_dev_handle, endpoint, buffer, length, generic_transfer_cb, data, timeout); + + return 0; +} + +int libhusb_fill_bulk_transfer(libhusb_transfer *transfer, libhusb_device_handle *handle, + libhusb_transfer_cb_fn callback, uint8_t endpoint, unsigned char *buffer, int length, + void *user_data, unsigned int timeout) +{ + struct async_transfer_data *data; + assert(transfer); + assert(transfer->lsusb_transfer); + assert(handle); + + data = malloc(sizeof(*data)); + if (!data) { + _E("malloc failed"); + return LIBHUSB_ERROR_NO_MEM; + } + + data->cb = callback; + data->user_data = user_data; + + /* it's either NULL, or was allocated by library */ + free(transfer->lusb_transfer->user_data); + libusb_fill_bulk_transfer(transfer->lusb_transfer, handle->lusb_dev_handle, endpoint, buffer, length, generic_transfer_cb, data, timeout); + + return 0; +} + +int libhusb_fill_control_transfer(libhusb_transfer *transfer, libhusb_device_handle *handle, + libhusb_transfer_cb_fn callback, unsigned char *buffer, void *user_data, unsigned int timeout) +{ + struct async_transfer_data *data; + assert(transfer); + assert(transfer->lsusb_transfer); + assert(handle); + + data = malloc(sizeof(*data)); + if (!data) { + _E("malloc failed"); + return LIBHUSB_ERROR_NO_MEM; + } + + data->cb = callback; + data->user_data = user_data; + + /* it's either NULL, or was allocated by library */ + free(transfer->lusb_transfer->user_data); + libusb_fill_control_transfer(transfer->lusb_transfer, handle->lusb_dev_handle, buffer, generic_transfer_cb, data, timeout); + + return 0; +} + +int libhusb_handle_events(libhusb_context *ctx, int *completed) +{ + int ret; + + ret = libusb_handle_events_completed(ctx->lusb_ctx, completed); + return translate_error(ret); +} + const char *libhusb_error_name(int error_code) { switch (error_code) { -- 2.34.1