From 3cef3baeba52cdba43885a70dbd8292a0a111b32 Mon Sep 17 00:00:00 2001 From: "peter.marcisovsky" Date: Wed, 6 Nov 2024 15:15:08 +0100 Subject: [PATCH] feat(usb_host): Mock USB device open/close - CMock callbacks for USB device opening and closing --- components/usb/private_include/usb_private.h | 3 +- components/usb/usb_private.c | 10 +- tools/mocks/usb/CMakeLists.txt | 6 + tools/mocks/usb/include/mock_add_usb_device.h | 294 ++++++++++++++ tools/mocks/usb/mock/mock_config.yaml | 1 + tools/mocks/usb/mock_add_usb_device.c | 376 ++++++++++++++++++ 6 files changed, 688 insertions(+), 2 deletions(-) create mode 100644 tools/mocks/usb/include/mock_add_usb_device.h create mode 100644 tools/mocks/usb/mock_add_usb_device.c diff --git a/components/usb/private_include/usb_private.h b/components/usb/private_include/usb_private.h index 71fb23db7e..7295883dd1 100644 --- a/components/usb/private_include/usb_private.h +++ b/components/usb/private_include/usb_private.h @@ -10,6 +10,7 @@ #include #include #include +#include "esp_assert.h" #include "usb/usb_types_ch9.h" #include "usb/usb_types_stack.h" @@ -34,7 +35,7 @@ typedef struct { int num_isoc_packets; usb_isoc_packet_desc_t isoc_packet_desc[0]; } usb_transfer_dummy_t; -_Static_assert(sizeof(usb_transfer_dummy_t) == sizeof(usb_transfer_t), "usb_transfer_dummy_t does not match usb_transfer_t"); +ESP_STATIC_ASSERT(sizeof(usb_transfer_dummy_t) == sizeof(usb_transfer_t), "usb_transfer_dummy_t does not match usb_transfer_t"); struct urb_s { TAILQ_ENTRY(urb_s) tailq_entry; diff --git a/components/usb/usb_private.c b/components/usb/usb_private.c index 56d6b3d719..0eeef5da61 100644 --- a/components/usb/usb_private.c +++ b/components/usb/usb_private.c @@ -15,10 +15,18 @@ urb_t *urb_alloc(size_t data_buffer_size, int num_isoc_packets) if (urb == NULL || data_buffer == NULL) { goto err; } + +#if CONFIG_IDF_TARGET_LINUX + // heap_caps_get_allocated_size() return 0 on Linux target + const size_t allocated_size = data_buffer_size; +#else + const size_t allocated_size = heap_caps_get_allocated_size(data_buffer); +#endif + // Cast as dummy transfer so that we can assign to const fields usb_transfer_dummy_t *dummy_transfer = (usb_transfer_dummy_t *)&urb->transfer; dummy_transfer->data_buffer = data_buffer; - dummy_transfer->data_buffer_size = heap_caps_get_allocated_size(data_buffer); + dummy_transfer->data_buffer_size = allocated_size; dummy_transfer->num_isoc_packets = num_isoc_packets; return urb; err: diff --git a/tools/mocks/usb/CMakeLists.txt b/tools/mocks/usb/CMakeLists.txt index 2ae39ff7b5..0e0d0b0e0c 100644 --- a/tools/mocks/usb/CMakeLists.txt +++ b/tools/mocks/usb/CMakeLists.txt @@ -8,6 +8,7 @@ idf_component_mock(INCLUDE_DIRS "${original_usb_dir}/include" "${original_usb_dir}/include/esp_private" "${original_usb_dir}/include/usb" "${original_usb_dir}/private_include" + "include" MOCK_HEADER_FILES ${original_usb_dir}/include/usb/usb_host.h ${original_usb_dir}/include/esp_private/usb_phy.h REQUIRES freertos) @@ -16,3 +17,8 @@ idf_component_mock(INCLUDE_DIRS "${original_usb_dir}/include" # We do not mock usb_helpers. We use the original implementation. # This way, we can test Class drivers descriptor parsing target_sources(${COMPONENT_LIB} PRIVATE "${original_usb_dir}/usb_helpers.c") +# Original implementation of usb_private.c to allocate memory for transfers +target_sources(${COMPONENT_LIB} PRIVATE "${original_usb_dir}/usb_private.c") + +# Add the extra src files for USB device mocking +target_sources(${COMPONENT_LIB} PRIVATE "mock_add_usb_device.c") diff --git a/tools/mocks/usb/include/mock_add_usb_device.h b/tools/mocks/usb/include/mock_add_usb_device.h new file mode 100644 index 0000000000..bf49e3bfa8 --- /dev/null +++ b/tools/mocks/usb/include/mock_add_usb_device.h @@ -0,0 +1,294 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "usb_host.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Add mocked USB device to device list + * + * @param[in] dev_address device address + * @param[in] dev_desc device descriptor + * @param[in] config_desc configuration descriptor + * + * @return + * - ESP_OK: Mocked device added successfully + * - ESP_ERR_INVALID_ARG: Invalid input argument(s) + */ +esp_err_t usb_host_mock_add_device(uint8_t dev_address, const usb_device_desc_t *dev_desc, const usb_config_desc_t *config_desc); + +/** + * @brief Get number of mocked USB devices in device list + * + * @return number of devices in devices list + */ +int usb_host_mock_get_devs_count(void); + +/** + * @brief Print mocked devices info + * @note if 0xFF is passed as dev_address, function prints all the mocked devices + * + * - printf all info about mocked device(s) + * - device handler, device address, opened status, device descriptor, configuration descriptor + * + * @param[in] dev_address device address (if 0xFF is passed as dev_address, function prints all the mocked devices) + * + * @return + * - ESP_OK: Mocked device found and info printed + * - ESP_ERR_INVALID_SIZE: Mocked devices list empty + * - ESP_ERR_INVALID_ARG: Mocked device does not exist at the provided address + */ +esp_err_t usb_host_mock_print_mocked_devices(uint8_t dev_address); + +/** + * @brief Initialize mocked devices list before use + */ +void usb_host_mock_dev_list_init(void); + +/** + * @brief Get Mocked device config descriptor by address + * + * @param[in] dev_address device address + * @param[in] config_desc configuration descriptor + * + * @return + * - ESP_OK: Configuration descriptor successfully provided + * - ESP_ERR_INVALID_ARG: Invalid input argument(s), or Mocked device does not exist at the provided address + * - ESP_ERR_INVALID_SIZE: Mocked devices list empty + */ +esp_err_t usb_host_mock_get_config_descriptor_by_address(uint8_t dev_address, const usb_config_desc_t **config_desc); + +/** + * @brief Get Mocked device device descriptor by address + * + * @param[in] dev_address device address + * @param[in] config_desc device descriptor + * + * @return + * - ESP_OK: Device descriptor successfully provided + * - ESP_ERR_INVALID_ARG: Invalid input argument(s), or Mocked device does not exist at the provided address + * - ESP_ERR_INVALID_SIZE: Mocked devices list empty + */ +esp_err_t usb_host_mock_get_device_descriptor_by_address(uint8_t dev_address, const usb_device_desc_t **device_desc); + +// ------------------------------------------------- CMock callback functions ------------------------------------------ + +/** + * @brief Allocate a transfer object + * @note CMock callback function, registered to usb_host_transfer_alloc() + * + * @param[in] data_buffer_size Size of the transfer's data buffer + * @param[in] num_isoc_packets Number of isochronous packets in transfer (set to 0 for non-isochronous transfers) + * @param[out] transfer Transfer object + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Transfer object allocated successfully + * - ESP_ERR_NO_MEM: Insufficient memory + */ +esp_err_t usb_host_transfer_alloc_mock_callback(size_t data_buffer_size, int num_isoc_packets, usb_transfer_t **transfer, int call_count); + +/** + * @brief Free a transfer object + * @note CMock callback function, registered to usb_host_transfer_free() + * + * @param[in] transfer Transfer object + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Transfer object freed successfully + */ +esp_err_t usb_host_transfer_free_mock_callback(usb_transfer_t *transfer, int call_count); + +/** + * @brief Register Mocked USB Host client + * @note CMock callback function, registered to usb_host_client_register() + * + * @param[in] client_config USB Host client config + * @param[out] client_hdl_ret USB Host client handle + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Client registered successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_NO_MEM: Insufficient memory + */ +esp_err_t usb_host_client_register_mock_callback(const usb_host_client_config_t *client_config, usb_host_client_handle_t *client_hdl_ret, int call_count); + +/** + * @brief Mocked USB Host Library client processing function + * @note CMock callback function, registered to usb_host_client_handle_events() + * + * - This function just blocks indefinitely, so the cdc_acm_client_task() does not loop + * - If we only had usb_host_client_handle_events_ExpectAnyArgsAndReturn(ESP_OK) instead of this callback, the cdc_acm_client_task() would loop infinitely + * - The only event implemented (for now) is client unblock, while uninstalling CDC-ACM driver + * + * @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 + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: No event to be handled + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_TIMEOUT: Semaphore waiting for events has timed out + */ +esp_err_t usb_host_client_handle_events_mock_callback(usb_host_client_handle_t client_hdl, TickType_t timeout_ticks, int call_count); + +/** + * @brief Unblock a Mocked client + * @note CMock callback function, registered to usb_host_client_unblock() + * + * @param[in] client_hdl Client handle + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Client unblocked successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + */ +esp_err_t usb_host_client_unblock_mock_callback(usb_host_client_handle_t client_hdl, int call_count); + +/** + * @brief Deregister a Mocked client + * @note CMock callback function, registered to usb_host_client_deregister() + * + * @param[in] client_hdl Client handle + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Client deregistered successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + */ +esp_err_t usb_host_client_deregister_mock_callback(usb_host_client_handle_t client_hdl, int call_count); + +/** + * @brief Open a device + * @note CMock callback function, registered to usb_host_device_open() + * + * @param[in] client_hdl Client handle + * @param[in] dev_address Device's address + * @param[out] dev_hdl_ret Device's handle + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Device opened successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_NOT_FOUND: Device with specified dev_address not found + */ +esp_err_t usb_host_device_open_mock_callback(usb_host_client_handle_t client_hdl, uint8_t dev_address, usb_device_handle_t *dev_hdl_ret, int call_count); + +/** + * @brief Close a device + * @note CMock callback function, registered to usb_host_device_close() + * + * @param[in] client_hdl Client handle + * @param[in] dev_hdl Device's handle + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Device closed successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Device was never opened + */ +esp_err_t usb_host_device_close_mock_callback(usb_host_client_handle_t client_hdl, usb_device_handle_t dev_hdl, int call_count); + +/** + * @brief Fill a list of device address + * @note CMock callback function, registered to usb_host_device_addr_list_fill() + * + * @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 + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Device list filled successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + */ +esp_err_t usb_host_device_addr_list_fill_mock_callback(int list_len, uint8_t *dev_addr_list, int *num_dev_ret, int call_count); + +/** + * @brief Get device's device descriptor + * @note CMock callback function, registered to usb_host_get_device_descriptor() + * + * @param[in] dev_hdl Device handle + * @param[out] device_desc Device descriptor + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Device descriptor obtained successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + */ +esp_err_t usb_host_get_device_descriptor_mock_callback(usb_device_handle_t dev_hdl, const usb_device_desc_t **device_desc, int call_count); + +/** + * @brief Get device's active configuration descriptor + * @note CMock callback function, registered to usb_host_get_active_config_descriptor() + * + * @param[in] dev_hdl Device handle + * @param[out] config_desc Configuration descriptor + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Active configuration descriptor obtained successfully + * - ESP_ERR_INVALID_ARG: Invalid argument + */ +esp_err_t usb_host_get_active_config_descriptor_mock_callback(usb_device_handle_t dev_hdl, const usb_config_desc_t **config_desc, int call_count); + +/** + * @brief Submit data transfer successfully + * @note CMock callback function, registered to usb_host_transfer_submit() + * + * Transfer callback is called, transfer status marked as completed, correct number of transferred bytes + * + * @param[in] transfer Pointer to USB transfer + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Transfer submitted + * - ESP_ERR_INVALID_ARG: Invalid input argument + */ +esp_err_t usb_host_transfer_submit_success_mock_callback(usb_transfer_t *transfer, int call_count); + +/** + * @brief Submit data transfer, but fail to deliver + * @note CMock callback function, registered to usb_host_transfer_submit() + * + * Transfer callback is called, transfer status marked as error, incorrect number of transferred bytes + * + * @param[in] transfer Pointer to USB transfer + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Transfer submitted + * - ESP_ERR_INVALID_ARG: Invalid input argument + */ +esp_err_t usb_host_transfer_submit_invalid_response_mock_callback(usb_transfer_t *transfer, int call_count); + +/** + * @brief Submit data transfer, time the transfer out + * @note CMock callback function, registered to usb_host_transfer_submit() + * + * Transfer callback is not called, because transfer timeout is not implemented in the USB Host Library + * + * @param[in] transfer Pointer to USB transfer + * @param[out] call_count Call count of the callback function (CMock mandatory argument) + * + * @return + * - ESP_OK: Transfer submitted + * - ESP_ERR_INVALID_ARG: Invalid input argument + */ +esp_err_t usb_host_transfer_submit_timeout_mock_callback(usb_transfer_t *transfer, int call_count); + +#ifdef __cplusplus +} +#endif diff --git a/tools/mocks/usb/mock/mock_config.yaml b/tools/mocks/usb/mock/mock_config.yaml index bbca5f4e3c..da27941d51 100644 --- a/tools/mocks/usb/mock/mock_config.yaml +++ b/tools/mocks/usb/mock/mock_config.yaml @@ -5,3 +5,4 @@ - return_thru_ptr - ignore - ignore_arg + - callback diff --git a/tools/mocks/usb/mock_add_usb_device.c b/tools/mocks/usb/mock_add_usb_device.c new file mode 100644 index 0000000000..b8a0e5fba9 --- /dev/null +++ b/tools/mocks/usb/mock_add_usb_device.c @@ -0,0 +1,376 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_log.h" +#include "usb_private.h" +#include "usb/usb_types_ch9.h" +#include "mock_add_usb_device.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + + +#define MOCK_CHECK(cond, ret_val) ({ \ + if (!(cond)) { \ + return (ret_val); \ + } \ +}) + +#define MOCK_CHECK_EMPTY_DEV_LIST ({ \ + if (!mocked_devices_count) { \ + ESP_LOGW(MOCK_TAG, "Mocked devices list empty"); \ + return ESP_ERR_INVALID_SIZE; \ + } \ +}) + +#define MAX_DEV_COUNT 128 +#define IS_VALID_ADDRESS(address) ((address) != 0xFF) +#define IS_EMPTY_ADDRESS(address) ((address) == 0xFF) + + +const char *MOCK_TAG_CB = "USB MOCK CB"; // Tag for callback functions +const char *MOCK_TAG = "USB MOCK"; // Tag for the rest of the functions + +typedef struct client_s client_t; +struct client_s { + struct { + SemaphoreHandle_t event_sem; + } constant; +}; + +/** + * @brief Mocked devices structure + */ +typedef struct { + int address; /*!< Device address */ + unsigned opened; /*!< Device opened status */ + const usb_device_desc_t *dev_desc; /*!< Device descriptor */ + const usb_config_desc_t *config_desc; /*!< Config descriptor */ +} device_list_t; + +static device_list_t device_list[MAX_DEV_COUNT]; +static unsigned mocked_devices_count = 0; + + +void usb_host_mock_dev_list_init(void) +{ + for(int index = 0; index < MAX_DEV_COUNT; index++) { + device_list[index].address = 0xFF; + device_list[index].opened = 0; + device_list[index].dev_desc = NULL; + device_list[index].config_desc = NULL; + } + mocked_devices_count = 0; +} + +int usb_host_mock_get_devs_count(void) +{ + return mocked_devices_count; +} + +esp_err_t usb_host_mock_add_device(uint8_t dev_address, const usb_device_desc_t *dev_desc, const usb_config_desc_t *config_desc) +{ + MOCK_CHECK(dev_address < MAX_DEV_COUNT && dev_desc != NULL && config_desc != NULL, ESP_ERR_INVALID_ARG); + + // Increase mocked_devices_count, only for new device addresses + if (IS_EMPTY_ADDRESS(device_list[dev_address].address)) { + mocked_devices_count++; + } + + // Fill device_list with new device parameters + device_list[dev_address].address = dev_address; + device_list[dev_address].opened = 0; + device_list[dev_address].dev_desc = dev_desc; + device_list[dev_address].config_desc = config_desc; + + return ESP_OK; +} + +/** + * @brief Print devices from device_list by index + * + * @param[in] index device index + */ +static void _print_mocked_device(int index) +{ + ESP_LOGI(MOCK_TAG, "Device handle = %p", (void*)(&device_list[index])); + ESP_LOGI(MOCK_TAG, "Device address = %d", device_list[index].address); + ESP_LOGI(MOCK_TAG, "Device opened by = %d clients", device_list[index].opened); + + usb_print_device_descriptor(device_list[index].dev_desc); + usb_print_config_descriptor(device_list[index].config_desc, NULL); + printf("\n"); +} + +esp_err_t usb_host_mock_print_mocked_devices(uint8_t dev_address) +{ + MOCK_CHECK_EMPTY_DEV_LIST; + + // dev_address is 0xFF, print all devices from device_list + if (IS_EMPTY_ADDRESS(dev_address)) { + for(int index = 0; index < MAX_DEV_COUNT; index++) { + if(IS_VALID_ADDRESS(device_list[index].address)) { + _print_mocked_device(index); + } + } + // Print only device at dev_address + } else { + if (IS_VALID_ADDRESS(device_list[dev_address].address)) { + _print_mocked_device(dev_address); + } else { + ESP_LOGE(MOCK_TAG, "Mocked device at address %d does not exist", dev_address); + return ESP_ERR_INVALID_ARG; + } + } + return ESP_OK; +} + +esp_err_t usb_host_mock_get_device_descriptor_by_address(uint8_t dev_address, const usb_device_desc_t **device_desc) +{ + MOCK_CHECK(device_desc != NULL && dev_address < MAX_DEV_COUNT, ESP_ERR_INVALID_ARG); + ESP_LOGD(MOCK_TAG_CB, "Get device descriptor by device address"); + MOCK_CHECK_EMPTY_DEV_LIST; + + if (IS_VALID_ADDRESS(device_list[dev_address].address)) { + *device_desc = device_list[dev_address].dev_desc; + } else { + ESP_LOGE(MOCK_TAG, "Mocked device at address %d does not exist", dev_address); + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; +} + +esp_err_t usb_host_mock_get_config_descriptor_by_address(uint8_t dev_address, const usb_config_desc_t **config_desc) +{ + MOCK_CHECK(config_desc != NULL && dev_address < MAX_DEV_COUNT, ESP_ERR_INVALID_ARG); + ESP_LOGD(MOCK_TAG_CB, "Get configuration descriptor by device address"); + MOCK_CHECK_EMPTY_DEV_LIST; + + if (IS_VALID_ADDRESS(device_list[dev_address].address)) { + *config_desc = device_list[dev_address].config_desc; + } else { + ESP_LOGE(MOCK_TAG, "Mocked device at address %d does not exist", dev_address); + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; +} + +// ------------------------------------------------- CMock callback functions ------------------------------------------ + +esp_err_t usb_host_transfer_alloc_mock_callback(size_t data_buffer_size, int num_isoc_packets, usb_transfer_t **transfer, int call_count) +{ + urb_t *urb = urb_alloc(data_buffer_size, num_isoc_packets); + if (urb == NULL) { + return ESP_ERR_NO_MEM; + } + *transfer = &urb->transfer; + + ESP_LOGD(MOCK_TAG_CB, "New transfer allocated: %p", *transfer); + return ESP_OK; +} + +esp_err_t usb_host_transfer_free_mock_callback(usb_transfer_t *transfer, int call_count) +{ + if (transfer == NULL) { + return ESP_OK; + } + + ESP_LOGD(MOCK_TAG_CB, "Transfer freed: %p", transfer); + urb_t *urb = __containerof(transfer, urb_t, transfer); + urb_free(urb); + return ESP_OK; +} + +esp_err_t usb_host_client_register_mock_callback(const usb_host_client_config_t *client_config, usb_host_client_handle_t *client_hdl_ret, int call_count) +{ + MOCK_CHECK(client_config != NULL && client_hdl_ret != NULL, ESP_ERR_INVALID_ARG); + + esp_err_t ret; + // Create client object + client_t *client_obj = (client_t*)calloc(1, sizeof(client_t)); + SemaphoreHandle_t event_sem = xSemaphoreCreateBinary(); + if (client_obj == NULL || event_sem == NULL) { + ret = ESP_ERR_NO_MEM; + goto alloc_err; + } + + client_obj->constant.event_sem = event_sem; + + ESP_LOGD(MOCK_TAG_CB, "USB Host client registered"); + + // Write back client handle + *client_hdl_ret = (usb_host_client_handle_t)client_obj; + ret = ESP_OK; + return ret; + +alloc_err: + if (event_sem) { + vSemaphoreDelete(event_sem); + } + free(client_obj); + return ret; +} + +esp_err_t usb_host_client_handle_events_mock_callback(usb_host_client_handle_t client_hdl, TickType_t timeout_ticks, int call_count) +{ + MOCK_CHECK(client_hdl != NULL, ESP_ERR_INVALID_ARG); + esp_err_t ret = (timeout_ticks == 0) ? ESP_OK : ESP_ERR_TIMEOUT; // We don't want to return ESP_ERR_TIMEOUT if we aren't blocking + client_t *client_obj = (client_t *)client_hdl; + + while (1) { + // Loop until there are no more events + if (xSemaphoreTake(client_obj->constant.event_sem, timeout_ticks) == pdFALSE) { + // Timed out waiting for semaphore or currently no events + break; + } + + ret = ESP_OK; + // Set timeout_ticks to 0 so that we can check for events again without blocking + timeout_ticks = 0; + } + return ret; +} + +esp_err_t usb_host_client_unblock_mock_callback(usb_host_client_handle_t client_hdl, int call_count) +{ + MOCK_CHECK(client_hdl != NULL, ESP_ERR_INVALID_ARG); + client_t *client_obj = (client_t *)client_hdl; + xSemaphoreGive(client_obj->constant.event_sem); + return ESP_OK; +} + +esp_err_t usb_host_client_deregister_mock_callback(usb_host_client_handle_t client_hdl, int call_count) +{ + MOCK_CHECK(client_hdl != NULL, ESP_ERR_INVALID_ARG); + client_t *client_obj = (client_t *)client_hdl; + + vSemaphoreDelete(client_obj->constant.event_sem); + free(client_obj); + return ESP_OK; +} + +esp_err_t usb_host_device_open_mock_callback(usb_host_client_handle_t client_hdl, uint8_t dev_address, usb_device_handle_t *dev_hdl_ret, int call_count) +{ + MOCK_CHECK(dev_address < MAX_DEV_COUNT && client_hdl != NULL && dev_hdl_ret != NULL, ESP_ERR_INVALID_ARG); + // Find a device in dev_list by dev_address + for (int index = 0; index < MAX_DEV_COUNT; index++) { + if(device_list[index].address == dev_address) { + + // We should check, if the device has not been opened by the same client + // But we are keeping this mock implementation simple + + // Device found in dev_list + *dev_hdl_ret = (usb_device_handle_t)(&device_list[index]); + device_list[index].opened++; + + return ESP_OK; + } + } + + // Device not found + ESP_LOGW(MOCK_TAG_CB, "Device not found: dev_address = %d", dev_address); + return ESP_ERR_NOT_FOUND; +} + +esp_err_t usb_host_device_close_mock_callback(usb_host_client_handle_t client_hdl, usb_device_handle_t dev_hdl, int call_count) +{ + MOCK_CHECK(dev_hdl != NULL && client_hdl != NULL, ESP_ERR_INVALID_ARG); + device_list_t* current_device = (device_list_t *) dev_hdl; + + if (current_device->opened == 0) { + // Device was never opened + ESP_LOGW(MOCK_TAG_CB, "Device was never opened: dev_address = %d", current_device->address); + return ESP_ERR_INVALID_STATE; + } + + // We should also check, if the device has been opened by this client + // But we are keeping this mock implementation simple + + // Closing the device + current_device->opened--; + ESP_LOGD(MOCK_TAG_CB, "Device closed: dev_address = %d, dev_hdl = %p", current_device->address, current_device); + return ESP_OK; +} + +esp_err_t usb_host_device_addr_list_fill_mock_callback(int list_len, uint8_t *dev_addr_list, int *num_dev_ret, int call_count) +{ + MOCK_CHECK(dev_addr_list != NULL && num_dev_ret != NULL, ESP_ERR_INVALID_ARG); + + int found_devices_count = 0; + for (int index = 0; index < MAX_DEV_COUNT; index++) { + if(IS_VALID_ADDRESS(device_list[index].address) && (found_devices_count < list_len)) { + dev_addr_list[found_devices_count++] = device_list[index].address; + } + } + + // Just print found devices + ESP_LOGD(MOCK_TAG_CB, "%d USB Devices found", found_devices_count); + for (int i = 0; i < found_devices_count; i++) { + ESP_LOGD(MOCK_TAG_CB, "dev_addr_list[%d] = %d", i, dev_addr_list[i]); + } + + *num_dev_ret = found_devices_count; + return ESP_OK; +} + +esp_err_t usb_host_get_device_descriptor_mock_callback(usb_device_handle_t dev_hdl, const usb_device_desc_t **device_desc, int call_count) +{ + MOCK_CHECK(dev_hdl != NULL && device_desc != NULL, ESP_ERR_INVALID_ARG); + ESP_LOGD(MOCK_TAG_CB, "Get device descriptor"); + + const device_list_t* current_device = (const device_list_t *) dev_hdl; + *device_desc = current_device->dev_desc; + return ESP_OK; +} + +esp_err_t usb_host_get_active_config_descriptor_mock_callback(usb_device_handle_t dev_hdl, const usb_config_desc_t **config_desc, int call_count) +{ + MOCK_CHECK(dev_hdl != NULL && config_desc != NULL, ESP_ERR_INVALID_ARG); + ESP_LOGD(MOCK_TAG_CB, "Get active config descriptor"); + + const device_list_t* current_device = (const device_list_t *) dev_hdl; + *config_desc = current_device->config_desc; + return ESP_OK; +} + +esp_err_t usb_host_transfer_submit_success_mock_callback(usb_transfer_t *transfer, int call_count) +{ + MOCK_CHECK(transfer != NULL, ESP_ERR_INVALID_ARG); + // Check that transfer and target endpoint are valid + MOCK_CHECK(transfer->device_handle != NULL, ESP_ERR_INVALID_ARG); // Target device must be set + MOCK_CHECK((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) != 0, ESP_ERR_INVALID_ARG); + transfer->callback(transfer); + transfer->status = USB_TRANSFER_STATUS_COMPLETED; + transfer->actual_num_bytes = transfer->num_bytes; + ESP_LOGD(MOCK_TAG_CB, "Mocked transfer submitted, buff len: %d, buff: %s", transfer->num_bytes, transfer->data_buffer); + return ESP_OK; +} + +esp_err_t usb_host_transfer_submit_invalid_response_mock_callback(usb_transfer_t *transfer, int call_count) +{ + MOCK_CHECK(transfer != NULL, ESP_ERR_INVALID_ARG); + // Check that transfer and target endpoint are valid + MOCK_CHECK(transfer->device_handle != NULL, ESP_ERR_INVALID_ARG); // Target device must be set + MOCK_CHECK((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) != 0, ESP_ERR_INVALID_ARG); + transfer->callback(transfer); + transfer->status = USB_TRANSFER_STATUS_ERROR; + transfer->actual_num_bytes = 0; + ESP_LOGD(MOCK_TAG_CB, "Mocked transfer submitted, buff len: %d, buff: %s", transfer->num_bytes, transfer->data_buffer); + ESP_LOGW(MOCK_TAG_CB, "Mocked transfer error"); + return ESP_OK; +} + +esp_err_t usb_host_transfer_submit_timeout_mock_callback(usb_transfer_t *transfer, int call_count) +{ + MOCK_CHECK(transfer != NULL, ESP_ERR_INVALID_ARG); + // Check that transfer and target endpoint are valid + MOCK_CHECK(transfer->device_handle != NULL, ESP_ERR_INVALID_ARG); // Target device must be set + MOCK_CHECK((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) != 0, ESP_ERR_INVALID_ARG); + return ESP_OK; +}