libhusb: Add async transfer API 06/61406/4
authorPaweł Szewczyk <p.szewczyk@samsung.com>
Mon, 7 Mar 2016 14:23:23 +0000 (15:23 +0100)
committerPaweł Szewczyk <p.szewczyk@samsung.com>
Thu, 10 Mar 2016 12:30:05 +0000 (13:30 +0100)
Change-Id: Ia1a09dfaa32e6f420eb64f3df22825e9f6349e6d
Signed-off-by: Paweł Szewczyk <p.szewczyk@samsung.com>
include/libhusb.h
include/libhusb_internal.h
src/libhusb.c

index 13d6306ae92481a21b050cc7a18a9a742dc7d727..f323a438fd2c2fd674ebba355270c87d83eed03c 100644 (file)
@@ -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
index 92cfb5fb26e4ef6992755ae522b9fb20097af9be..4e0c3c70cba92f4cb5601a15381d24c7d3f98b05 100644 (file)
@@ -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;
+};
index e36dd77bf59188689d1d8cc1a7a2e4e9f49e674d..b905cb76364504574335aa4f8962a44fa359928e 100644 (file)
@@ -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) {