esp-idf/tools/mocks/usb/mock_add_usb_device.c
peter.marcisovsky 3cef3baeba feat(usb_host): Mock USB device open/close
- CMock callbacks for USB device opening and closing
2024-12-16 16:55:37 +01:00

377 lines
14 KiB
C

/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#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;
}