usb: Change API and refactor tests

This commit updates the USB Host Library API in the following wasy:
- usb_host_client_handle_t and usb_device_handle_t made into struct pointers
  to generate compiler warnings about conflicting handle types
- usb_host_client_config_t changed to future proof API for Synchronous Clients
- Added usb_host_lib_unblock()
- Added usb_host_device_addr_list_fill()
- Return of usb_host_device_free_all() updated to indicate whether there
  are still devices yet to be freed.
- Blockg APIs are now marked explicitly
- Fixed a bug in usb_host_transfer_submit_control() when checking the bEndpointAddress
  of a control transfer.

Tests are also refactored to move some common macros into shared headers

Closes https://github.com/espressif/esp-idf/issues/7786
pull/6613/merge
Darian Leung 2021-10-28 00:54:27 +08:00
rodzic 54b6c902d3
commit 189899180f
10 zmienionych plików z 158 dodań i 42 usunięć

Wyświetl plik

@ -27,7 +27,14 @@ extern "C" {
// ----------------------- Handles -------------------------
typedef void * usb_host_client_handle_t; /**< Handle to a client using the USB Host Library */
/**
* @brief Handle to a USB Host Library asynchronous client
*
* An asynchronous client can be registered using usb_host_client_register()
*
* @note Asynchronous API
*/
typedef struct usb_host_client_handle_s * usb_host_client_handle_t;
// ----------------------- Events --------------------------
@ -93,9 +100,14 @@ typedef struct {
* Configuration structure for a USB Host Library client. Provided in usb_host_client_register()
*/
typedef struct {
usb_host_client_event_cb_t client_event_callback; /**< Client's event callback function */
void *callback_arg; /**< Event callback function argument */
int max_num_event_msg; /**< Maximum number of event messages that can be stored (e.g., 3) */
bool is_synchronous; /**< Whether the client is asynchronous or synchronous or not. Set to false for now. */
int max_num_event_msg; /**< Maximum number of event messages that can be stored (e.g., 3) */
union { //Note: Made into union or future expansion
struct {
usb_host_client_event_cb_t client_event_callback; /**< Client's event callback function */
void *callback_arg; /**< Event callback function argument */
} async;
};
} usb_host_client_config_t;
// ------------------------------------------------ Library Functions --------------------------------------------------
@ -129,12 +141,22 @@ esp_err_t usb_host_uninstall(void);
* - This function handles all of the USB Host Library's processing and should be called repeatedly in a loop
* - Check event_flags_ret to see if an flags are set indicating particular USB Host Library events
*
* @note This function can block
* @param[in] timeout_ticks Timeout in ticks to wait for an event to occur
* @param[out] event_flags_ret Event flags that indicate what USB Host Library event occurred
* @return esp_err_t
*/
esp_err_t usb_host_lib_handle_events(TickType_t timeout_ticks, uint32_t *event_flags_ret);
/**
* @brief Unblock the USB Host Library handler
*
* - This function simply unblocks the USB Host Library event handling function (usb_host_lib_handle_events())
*
* @return esp_err_t
*/
esp_err_t usb_host_lib_unblock(void);
// ------------------------------------------------ Client Functions ---------------------------------------------------
/**
@ -165,6 +187,7 @@ esp_err_t usb_host_client_deregister(usb_host_client_handle_t client_hdl);
*
* - This function handles all of a client's processing and should be called repeatedly in a loop
*
* @note This function can block
* @param[in] client_hdl Client handle
* @param[in] timeout_ticks Timeout in ticks to wait for an event to occur
* @return esp_err_t
@ -204,6 +227,7 @@ esp_err_t usb_host_device_open(usb_host_client_handle_t client_hdl, uint8_t dev_
* - A client must close a device after it has finished using the device (claimed interfaces must also be released)
* - A client must close all devices it has opened before deregistering
*
* @note This function can block
* @param[in] client_hdl Client handle
* @param[in] dev_hdl Device handle
* @return esp_err_t
@ -220,10 +244,28 @@ esp_err_t usb_host_device_close(usb_host_client_handle_t client_hdl, usb_device_
* when all devices have been freed
* - This function is useful when cleaning up devices before uninstalling the USB Host Library
*
* @return esp_err_t
* @return
* - ESP_ERR_NOT_FINISHED: There are one or more devices that still need to be freed. Wait for USB_HOST_LIB_EVENT_FLAGS_ALL_FREE event
* - ESP_OK: All devices already freed (i.e., there were no devices)
* - Other: Error
*/
esp_err_t usb_host_device_free_all(void);
/**
* @brief Fill a list of device address
*
* - This function fills an empty list with the address of connected devices
* - The Device addresses can then used in usb_host_device_open()
* - If there are more devices than the list_len, this function will only fill
* up to list_len number of devices.
*
* @param[in] list_len Length of the empty list
* @param[inout] dev_addr_list Empty list to be filled
* @param[out] num_dev_ret Number of devices
* @return esp_err_t
*/
esp_err_t usb_host_device_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret);
// ------------------------------------------------- Device Requests ---------------------------------------------------
// ------------------- Cached Requests ---------------------
@ -234,6 +276,7 @@ esp_err_t usb_host_device_free_all(void);
* - This function gets some basic information of a device
* - The device must be opened first before attempting to get its information
*
* @note This function can block
* @param[in] dev_hdl Device handle
* @param[out] dev_info Device information
* @return esp_err_t
@ -265,6 +308,7 @@ esp_err_t usb_host_get_device_descriptor(usb_device_handle_t dev_hdl, const usb_
* - No control transfer is sent. The device's active configuration descriptor is cached on enumeration
* - This function simple returns a pointer to the cached descriptor
*
* @note This function can block
* @note No control transfer is sent. A device's active configuration descriptor is cached on enumeration
* @param[in] dev_hdl Device handle
* @param[out] config_desc Configuration descriptor
@ -280,6 +324,7 @@ esp_err_t usb_host_get_active_config_descriptor(usb_device_handle_t dev_hdl, con
* - A client must claim a device's interface before attempting to communicate with any of its endpoints
* - Once an interface is claimed by a client, it cannot be claimed by any other client.
*
* @note This function can block
* @param[in] client_hdl Client handle
* @param[in] dev_hdl Device handle
* @param[in] bInterfaceNumber Interface number
@ -294,6 +339,7 @@ esp_err_t usb_host_interface_claim(usb_host_client_handle_t client_hdl, usb_devi
* - A client should release a device's interface after it no longer needs to communicate with the interface
* - A client must release all of its interfaces of a device it has claimed before being able to close the device
*
* @note This function can block
* @param[in] client_hdl Client handle
* @param[in] dev_hdl Device handle
* @param[in] bInterfaceNumber Interface number
@ -308,6 +354,7 @@ esp_err_t usb_host_interface_release(usb_host_client_handle_t client_hdl, usb_de
* - The endpoint must be part of an interface claimed by a client
* - Once halted, the endpoint must be cleared using usb_host_endpoint_clear() before it can communicate again
*
* @note This function can block
* @param dev_hdl Device handle
* @param bEndpointAddress Endpoint address
* @return esp_err_t
@ -322,6 +369,7 @@ esp_err_t usb_host_endpoint_halt(usb_device_handle_t dev_hdl, uint8_t bEndpointA
* - The endpoint must have been halted (either through a transfer error, or usb_host_endpoint_halt())
* - Flushing an endpoint will caused an queued up transfers to be canceled
*
* @note This function can block
* @param dev_hdl Device handle
* @param bEndpointAddress Endpoint address
* @return esp_err_t
@ -336,6 +384,7 @@ esp_err_t usb_host_endpoint_flush(usb_device_handle_t dev_hdl, uint8_t bEndpoint
* - The endpoint must have been halted (either through a transfer error, or usb_host_endpoint_halt())
* - If the endpoint has any queued up transfers, clearing a halt will resume their execution
*
* @note This function can block
* @param dev_hdl Device handle
* @param bEndpointAddress Endpoint address
* @return esp_err_t
@ -396,17 +445,6 @@ esp_err_t usb_host_transfer_submit(usb_transfer_t *transfer);
*/
esp_err_t usb_host_transfer_submit_control(usb_host_client_handle_t client_hdl, usb_transfer_t *transfer);
/**
* @brief Cancel a submitted transfer
*
* - Cancel a previously submitted transfer
* - In its current implementation, any transfer that is already in-flight will not be canceled
*
* @param transfer Transfer object
* @return esp_err_t
*/
esp_err_t usb_host_transfer_cancel(usb_transfer_t *transfer);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -43,7 +43,7 @@ typedef enum {
/**
* @brief Handle of a USB Device connected to a USB Host
*/
typedef void * usb_device_handle_t;
typedef struct usb_device_handle_s * usb_device_handle_t;
/**
* @brief Basic information of an enumerated device

Wyświetl plik

@ -119,6 +119,21 @@ esp_err_t usbh_process(void);
// --------------------- Device Pool -----------------------
/**
* @brief Fill list with address of currently connected devices
*
* - This function fills the provided list with the address of current connected devices
* - Device address can then be used in usbh_dev_open()
* - If there are more devices than the list_len, this function will only fill
* up to list_len number of devices.
*
* @param[in] list_len Length of empty list
* @param[inout] dev_addr_list Empty list to be filled
* @param[out] num_dev_ret Number of devices filled into list
* @return esp_err_t
*/
esp_err_t usbh_dev_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret);
/**
* @brief Open a device by address
*
@ -145,7 +160,9 @@ esp_err_t usbh_dev_close(usb_device_handle_t dev_hdl);
*
* A device marked as free will not be freed until the last client using the device has called usbh_dev_close()
*
* @return esp_err_t
* @return
* - ESP_OK: There were no devices to free to begin with. Current state is all free
* - ESP_ERR_NOT_FINISHED: One or more devices still need to be freed (but have been marked "to be freed")
*/
esp_err_t usbh_dev_mark_all_free(void);

Wyświetl plik

@ -10,6 +10,7 @@
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "test_usb_common.h"
#include "ctrl_client.h"
#include "usb/usb_host.h"
#include "unity.h"
@ -32,7 +33,6 @@ Implementation of a control transfer client used for USB Host Tests.
- Deregister control client
*/
#define MAX(x,y) (((x) >= (y)) ? (x) : (y))
#define CTRL_CLIENT_MAX_EVENT_MSGS 5
#define NUM_TRANSFER_OBJ 3
#define MAX_TRANSFER_BYTES 256
@ -97,9 +97,12 @@ void ctrl_client_async_seq_task(void *arg)
//Register client
usb_host_client_config_t client_config = {
.client_event_callback = ctrl_client_event_cb,
.callback_arg = (void *)&ctrl_obj,
.is_synchronous = false,
.max_num_event_msg = CTRL_CLIENT_MAX_EVENT_MSGS,
.async = {
.client_event_callback = ctrl_client_event_cb,
.callback_arg = (void *)&ctrl_obj,
},
};
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &ctrl_obj.client_hdl));
@ -153,7 +156,7 @@ void ctrl_client_async_seq_task(void *arg)
usb_transfer_t *transfer = ctrl_xfer[ctrl_obj.num_xfer_sent % NUM_TRANSFER_OBJ];
USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, 0, MAX_TRANSFER_BYTES);
transfer->num_bytes = sizeof(usb_setup_packet_t) + MAX_TRANSFER_BYTES;
transfer->bEndpointAddress = 0;
transfer->bEndpointAddress = 0x80;
TEST_ASSERT_EQUAL(ESP_OK, usb_host_transfer_submit_control(ctrl_obj.client_hdl, transfer));
ctrl_obj.num_xfer_sent++;
ctrl_obj.next_stage = TEST_STAGE_CTRL_XFER_WAIT;

Wyświetl plik

@ -6,6 +6,8 @@
#include <stdint.h>
#define MSC_ASYNC_CLIENT_MAX_EVENT_MSGS 5
typedef struct {
int num_sectors_to_read;
int num_sectors_per_xfer;

Wyświetl plik

@ -7,6 +7,7 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
@ -35,9 +36,6 @@ Implementation of an asynchronous MSC client used for USB Host disconnection tes
- Deregister MSC client
*/
#define MAX(x,y) (((x) >= (y)) ? (x) : (y))
#define MSC_CLIENT_MAX_EVENT_MSGS 5
typedef enum {
TEST_STAGE_WAIT_CONN,
TEST_STAGE_DEV_OPEN,
@ -130,9 +128,12 @@ void msc_client_async_dconn_task(void *arg)
//Register client
usb_host_client_config_t client_config = {
.client_event_callback = msc_client_event_cb,
.callback_arg = (void *)&msc_obj,
.max_num_event_msg = MSC_CLIENT_MAX_EVENT_MSGS,
.is_synchronous = false,
.max_num_event_msg = MSC_ASYNC_CLIENT_MAX_EVENT_MSGS,
.async = {
.client_event_callback = msc_client_event_cb,
.callback_arg = (void *)&msc_obj,
},
};
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &msc_obj.client_hdl));

Wyświetl plik

@ -7,10 +7,12 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "test_usb_common.h"
#include "test_usb_mock_classes.h"
#include "msc_client.h"
#include "usb/usb_host.h"
@ -34,9 +36,6 @@ Implementation of an MSC client used for USB Host Tests
- Deregister MSC client
*/
#define MAX(x,y) (((x) >= (y)) ? (x) : (y))
#define MSC_CLIENT_MAX_EVENT_MSGS 5
typedef enum {
TEST_STAGE_WAIT_CONN,
TEST_STAGE_DEV_OPEN,
@ -130,9 +129,12 @@ void msc_client_async_seq_task(void *arg)
//Register client
usb_host_client_config_t client_config = {
.client_event_callback = msc_client_event_cb,
.callback_arg = (void *)&msc_obj,
.max_num_event_msg = MSC_CLIENT_MAX_EVENT_MSGS,
.is_synchronous = false,
.max_num_event_msg = MSC_ASYNC_CLIENT_MAX_EVENT_MSGS,
.async = {
.client_event_callback = msc_client_event_cb,
.callback_arg = (void *)&msc_obj,
},
};
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &msc_obj.client_hdl));

Wyświetl plik

@ -72,7 +72,7 @@ TEST_CASE("Test USB Host async (single client)", "[usb_host][ignore]")
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
printf("No more clients\n");
TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_free_all());
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all());
}
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
break;
@ -144,7 +144,7 @@ TEST_CASE("Test USB Host async (multi client)", "[usb_host][ignore]")
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
printf("No more clients\n");
TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_free_all());
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all());
}
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
break;

Wyświetl plik

@ -508,6 +508,16 @@ exit:
return ret;
}
esp_err_t usb_host_lib_unblock(void)
{
//All devices must have been freed at this point
HOST_ENTER_CRITICAL();
HOST_CHECK_FROM_CRIT(p_host_lib_obj != NULL, ESP_ERR_INVALID_STATE);
_unblock_lib(false);
HOST_EXIT_CRITICAL();
return ESP_OK;
}
// ------------------------------------------------ Client Functions ---------------------------------------------------
// ----------------------- Private -------------------------
@ -562,7 +572,11 @@ static void _handle_pending_ep(client_t *client_obj)
esp_err_t usb_host_client_register(const usb_host_client_config_t *client_config, usb_host_client_handle_t *client_hdl_ret)
{
HOST_CHECK(client_config != NULL && client_hdl_ret != NULL, ESP_ERR_INVALID_ARG);
HOST_CHECK(client_config->client_event_callback != NULL && client_config->max_num_event_msg > 0, ESP_ERR_INVALID_ARG);
HOST_CHECK(client_config->max_num_event_msg > 0, ESP_ERR_INVALID_ARG);
if (!client_config->is_synchronous) {
//Asynchronous clients must provide a
HOST_CHECK(client_config->async.client_event_callback != NULL, ESP_ERR_INVALID_ARG);
}
esp_err_t ret;
//Create client object
@ -579,8 +593,8 @@ esp_err_t usb_host_client_register(const usb_host_client_config_t *client_config
TAILQ_INIT(&client_obj->mux_protected.interface_tailq);
TAILQ_INIT(&client_obj->dynamic.done_ctrl_xfer_tailq);
client_obj->constant.event_sem = event_sem;
client_obj->constant.event_callback = client_config->client_event_callback;
client_obj->constant.callback_arg = client_config->callback_arg;
client_obj->constant.event_callback = client_config->async.client_event_callback;
client_obj->constant.callback_arg = client_config->async.callback_arg;
client_obj->constant.event_msg_queue = event_msg_queue;
//Add client to the host library's list of clients
@ -819,10 +833,16 @@ esp_err_t usb_host_device_free_all(void)
HOST_EXIT_CRITICAL();
esp_err_t ret;
ret = usbh_dev_mark_all_free();
//Wait for USB_HOST_LIB_EVENT_FLAGS_ALL_FREE to confirm all devices free
//If ESP_ERR_NOT_FINISHED is returned, caller must wait for USB_HOST_LIB_EVENT_FLAGS_ALL_FREE to confirm all devices are free
return ret;
}
esp_err_t usb_host_device_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret)
{
HOST_CHECK(dev_addr_list != NULL && num_dev_ret != NULL, ESP_ERR_INVALID_ARG);
return usbh_dev_addr_list_fill(list_len, dev_addr_list, num_dev_ret);
}
// ------------------------------------------------- Device Requests ---------------------------------------------------
// ------------------- Cached Requests ---------------------
@ -1266,7 +1286,8 @@ esp_err_t usb_host_transfer_submit_control(usb_host_client_handle_t client_hdl,
usb_device_info_t dev_info;
ESP_ERROR_CHECK(usbh_dev_get_info(dev_hdl, &dev_info));
HOST_CHECK(transfer_check(transfer, USB_TRANSFER_TYPE_CTRL, dev_info.bMaxPacketSize0, xfer_is_in), ESP_ERR_INVALID_ARG);
HOST_CHECK(transfer->bEndpointAddress == 0, ESP_ERR_INVALID_ARG);
//Control transfers must be targeted at EP 0
HOST_CHECK((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) == 0, ESP_ERR_INVALID_ARG);
//Save client handle into URB
urb_t *urb_obj = __containerof(transfer, urb_t, transfer);
urb_obj->usb_host_client = (void *)client_hdl;

Wyświetl plik

@ -468,6 +468,36 @@ esp_err_t usbh_process(void)
// --------------------- Device Pool -----------------------
esp_err_t usbh_dev_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret)
{
USBH_CHECK(dev_addr_list != NULL && num_dev_ret != NULL, ESP_ERR_INVALID_ARG);
USBH_ENTER_CRITICAL();
int num_filled = 0;
device_t *dev_obj;
//Fill list with devices from idle tailq
TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_idle_tailq, dynamic.tailq_entry) {
if (num_filled < list_len) {
dev_addr_list[num_filled] = dev_obj->constant.address;
num_filled++;
} else {
break;
}
}
//Fill list with devices from pending tailq
TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_pending_tailq, dynamic.tailq_entry) {
if (num_filled < list_len) {
dev_addr_list[num_filled] = dev_obj->constant.address;
num_filled++;
} else {
break;
}
}
USBH_EXIT_CRITICAL();
//Write back number of devices filled
*num_dev_ret = num_filled;
return ESP_OK;
}
esp_err_t usbh_dev_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl)
{
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
@ -547,6 +577,7 @@ esp_err_t usbh_dev_mark_all_free(void)
Note: We manually traverse the list because we need to add/remove items while traversing
*/
bool call_notif_cb = false;
bool wait_for_free = false;
for (int i = 0; i < 2; i++) {
device_t *dev_obj_cur;
device_t *dev_obj_next;
@ -568,6 +599,7 @@ esp_err_t usbh_dev_mark_all_free(void)
//Device is still opened. Just mark it as waiting to be closed
dev_obj_cur->dynamic.flags.waiting_close = 1;
}
wait_for_free = true; //As long as there is still a device, we need to wait for an event indicating it is freed
dev_obj_cur = dev_obj_next;
}
}
@ -576,7 +608,7 @@ esp_err_t usbh_dev_mark_all_free(void)
if (call_notif_cb) {
p_usbh_obj->constant.notif_cb(USB_NOTIF_SOURCE_USBH, false, p_usbh_obj->constant.notif_cb_arg);
}
return ESP_OK;
return (wait_for_free) ? ESP_ERR_NOT_FINISHED : ESP_OK;
}
// ------------------- Single Device ----------------------