| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- /*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- #include <stdio.h>
- #include <string.h>
- #include "freertos/FreeRTOS.h"
- #include "freertos/semphr.h"
- #include "test_utils.h"
- #include "soc/gpio_pins.h"
- #include "soc/gpio_sig_map.h"
- #include "esp_intr_alloc.h"
- #include "esp_err.h"
- #include "esp_attr.h"
- #include "esp_rom_gpio.h"
- #include "soc/usb_wrap_struct.h"
- #include "hcd.h"
- #include "usb_private.h"
- #include "usb/usb_types_ch9.h"
- #include "test_hcd_common.h"
- #define PORT_NUM 1
- #define EVENT_QUEUE_LEN 5
- #define ENUM_ADDR 1 //Device address to use for tests that enumerate the device
- #define ENUM_CONFIG 1 //Device configuration number to use for tests that enumerate the device
- typedef struct {
- hcd_port_handle_t port_hdl;
- hcd_port_event_t port_event;
- } port_event_msg_t;
- typedef struct {
- hcd_pipe_handle_t pipe_hdl;
- hcd_pipe_event_t pipe_event;
- } pipe_event_msg_t;
- // ---------------------------------------------------- Private --------------------------------------------------------
- /**
- * @brief HCD port callback. Registered when initializing an HCD port
- *
- * @param port_hdl Port handle
- * @param port_event Port event that triggered the callback
- * @param user_arg User argument
- * @param in_isr Whether callback was called in an ISR context
- * @return true ISR should yield after this callback returns
- * @return false No yield required (non-ISR context calls should always return false)
- */
- static bool port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port_event, void *user_arg, bool in_isr)
- {
- //We store the port's queue handle in the port's context variable
- void *port_ctx = hcd_port_get_context(port_hdl);
- QueueHandle_t port_evt_queue = (QueueHandle_t)port_ctx;
- TEST_ASSERT(in_isr); //Current HCD implementation should never call a port callback in a task context
- port_event_msg_t msg = {
- .port_hdl = port_hdl,
- .port_event = port_event,
- };
- BaseType_t xTaskWoken = pdFALSE;
- xQueueSendFromISR(port_evt_queue, &msg, &xTaskWoken);
- return (xTaskWoken == pdTRUE);
- }
- /**
- * @brief HCD pipe callback. Registered when allocating a HCD pipe
- *
- * @param pipe_hdl Pipe handle
- * @param pipe_event Pipe event that triggered the callback
- * @param user_arg User argument
- * @param in_isr Whether the callback was called in an ISR context
- * @return true ISR should yield after this callback returns
- * @return false No yield required (non-ISR context calls should always return false)
- */
- static bool pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
- {
- QueueHandle_t pipe_evt_queue = (QueueHandle_t)user_arg;
- pipe_event_msg_t msg = {
- .pipe_hdl = pipe_hdl,
- .pipe_event = pipe_event,
- };
- if (in_isr) {
- BaseType_t xTaskWoken = pdFALSE;
- xQueueSendFromISR(pipe_evt_queue, &msg, &xTaskWoken);
- return (xTaskWoken == pdTRUE);
- } else {
- xQueueSend(pipe_evt_queue, &msg, portMAX_DELAY);
- return false;
- }
- }
- // ------------------------------------------------- HCD Event Test ----------------------------------------------------
- void test_hcd_expect_port_event(hcd_port_handle_t port_hdl, hcd_port_event_t expected_event)
- {
- //Get the port event queue from the port's context variable
- QueueHandle_t port_evt_queue = (QueueHandle_t)hcd_port_get_context(port_hdl);
- TEST_ASSERT_NOT_EQUAL(NULL, port_evt_queue);
- //Wait for port callback to send an event message
- port_event_msg_t msg;
- xQueueReceive(port_evt_queue, &msg, portMAX_DELAY);
- //Check the contents of that event message
- TEST_ASSERT_EQUAL(port_hdl, msg.port_hdl);
- TEST_ASSERT_EQUAL(expected_event, msg.port_event);
- printf("\t-> Port event\n");
- }
- void test_hcd_expect_pipe_event(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t expected_event)
- {
- //Get the pipe's event queue from the pipe's context variable
- QueueHandle_t pipe_evt_queue = (QueueHandle_t)hcd_pipe_get_context(pipe_hdl);
- TEST_ASSERT_NOT_EQUAL(NULL, pipe_evt_queue);
- //Wait for pipe callback to send an event message
- pipe_event_msg_t msg;
- xQueueReceive(pipe_evt_queue, &msg, portMAX_DELAY);
- //Check the contents of that event message
- TEST_ASSERT_EQUAL(pipe_hdl, msg.pipe_hdl);
- TEST_ASSERT_EQUAL(expected_event, msg.pipe_event);
- }
- int test_hcd_get_num_port_events(hcd_port_handle_t port_hdl)
- {
- //Get the port event queue from the port's context variable
- QueueHandle_t port_evt_queue = (QueueHandle_t)hcd_port_get_context(port_hdl);
- TEST_ASSERT_NOT_EQUAL(NULL, port_evt_queue);
- return EVENT_QUEUE_LEN - uxQueueSpacesAvailable(port_evt_queue);
- }
- int test_hcd_get_num_pipe_events(hcd_pipe_handle_t pipe_hdl)
- {
- //Get the pipe's event queue from the pipe's context variable
- QueueHandle_t pipe_evt_queue = (QueueHandle_t)hcd_pipe_get_context(pipe_hdl);
- TEST_ASSERT_NOT_EQUAL(NULL, pipe_evt_queue);
- return EVENT_QUEUE_LEN - uxQueueSpacesAvailable(pipe_evt_queue);
- }
- // ----------------------------------------------- Driver/Port Related -------------------------------------------------
- void test_hcd_force_conn_state(bool connected, TickType_t delay_ticks)
- {
- vTaskDelay(delay_ticks);
- usb_wrap_dev_t *wrap = &USB_WRAP;
- if (connected) {
- //Swap back to internal PHY that is connected to a device
- wrap->otg_conf.phy_sel = 0;
- } else {
- //Set external PHY input signals to fixed voltage levels mimicking a disconnected state
- esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_EXTPHY_VP_IDX, false);
- esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_EXTPHY_VM_IDX, false);
- esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_EXTPHY_RCV_IDX, false);
- //Swap to the external PHY
- wrap->otg_conf.phy_sel = 1;
- }
- }
- hcd_port_handle_t test_hcd_setup(void)
- {
- //Create a queue for port callback to queue up port events
- QueueHandle_t port_evt_queue = xQueueCreate(EVENT_QUEUE_LEN, sizeof(port_event_msg_t));
- TEST_ASSERT_NOT_EQUAL(NULL, port_evt_queue);
- //Install HCD
- hcd_config_t hcd_config = {
- .intr_flags = ESP_INTR_FLAG_LEVEL1,
- };
- TEST_ASSERT_EQUAL(ESP_OK, hcd_install(&hcd_config));
- //Initialize a port
- hcd_port_config_t port_config = {
- .fifo_bias = HCD_PORT_FIFO_BIAS_BALANCED,
- .callback = port_callback,
- .callback_arg = (void *)port_evt_queue,
- .context = (void *)port_evt_queue,
- };
- hcd_port_handle_t port_hdl;
- TEST_ASSERT_EQUAL(ESP_OK, hcd_port_init(PORT_NUM, &port_config, &port_hdl));
- TEST_ASSERT_NOT_EQUAL(NULL, port_hdl);
- TEST_ASSERT_EQUAL(HCD_PORT_STATE_NOT_POWERED, hcd_port_get_state(port_hdl));
- test_hcd_force_conn_state(false, 0); //Force disconnected state on PHY
- return port_hdl;
- }
- void test_hcd_teardown(hcd_port_handle_t port_hdl)
- {
- //Get the queue handle from the port's context variable
- QueueHandle_t port_evt_queue = (QueueHandle_t)hcd_port_get_context(port_hdl);
- TEST_ASSERT_NOT_EQUAL(NULL, port_evt_queue);
- //Deinitialize a port
- TEST_ASSERT_EQUAL(ESP_OK, hcd_port_deinit(port_hdl));
- //Uninstall the HCD
- TEST_ASSERT_EQUAL(ESP_OK, hcd_uninstall());
- vQueueDelete(port_evt_queue);
- }
- usb_speed_t test_hcd_wait_for_conn(hcd_port_handle_t port_hdl)
- {
- //Power ON the port
- TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_POWER_ON));
- TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISCONNECTED, hcd_port_get_state(port_hdl));
- //Wait for connection event
- printf("Waiting for connection\n");
- test_hcd_force_conn_state(true, pdMS_TO_TICKS(100)); //Allow for connected state on PHY
- test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_CONNECTION);
- TEST_ASSERT_EQUAL(HCD_PORT_EVENT_CONNECTION, hcd_port_handle_event(port_hdl));
- TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISABLED, hcd_port_get_state(port_hdl));
- //Reset newly connected device
- printf("Resetting\n");
- TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_RESET));
- TEST_ASSERT_EQUAL(HCD_PORT_STATE_ENABLED, hcd_port_get_state(port_hdl));
- //Get speed of connected
- usb_speed_t port_speed;
- TEST_ASSERT_EQUAL(ESP_OK, hcd_port_get_speed(port_hdl, &port_speed));
- if (port_speed == USB_SPEED_FULL) {
- printf("Full speed enabled\n");
- } else {
- printf("Low speed enabled\n");
- }
- return port_speed;
- }
- void test_hcd_wait_for_disconn(hcd_port_handle_t port_hdl, bool already_disabled)
- {
- if (!already_disabled) {
- //Disable the device
- printf("Disabling\n");
- TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_DISABLE));
- TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISABLED, hcd_port_get_state(port_hdl));
- }
- //Wait for a safe disconnect
- printf("Waiting for disconnection\n");
- test_hcd_force_conn_state(false, pdMS_TO_TICKS(100)); //Force disconnected state on PHY
- test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_DISCONNECTION);
- TEST_ASSERT_EQUAL(HCD_PORT_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl));
- TEST_ASSERT_EQUAL(HCD_PORT_STATE_RECOVERY, hcd_port_get_state(port_hdl));
- //Power down the port
- TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_POWER_OFF));
- TEST_ASSERT_EQUAL(HCD_PORT_STATE_NOT_POWERED, hcd_port_get_state(port_hdl));
- }
- // ---------------------------------------------- Pipe Setup/Tear-down -------------------------------------------------
- hcd_pipe_handle_t test_hcd_pipe_alloc(hcd_port_handle_t port_hdl, const usb_ep_desc_t *ep_desc, uint8_t dev_addr, usb_speed_t dev_speed)
- {
- //Create a queue for pipe callback to queue up pipe events
- QueueHandle_t pipe_evt_queue = xQueueCreate(EVENT_QUEUE_LEN, sizeof(pipe_event_msg_t));
- TEST_ASSERT_NOT_EQUAL(NULL, pipe_evt_queue);
- printf("Creating pipe\n");
- hcd_pipe_config_t pipe_config = {
- .callback = pipe_callback,
- .callback_arg = (void *)pipe_evt_queue,
- .context = (void *)pipe_evt_queue,
- .ep_desc = ep_desc,
- .dev_addr = dev_addr,
- .dev_speed = dev_speed,
- };
- hcd_pipe_handle_t pipe_hdl;
- TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_alloc(port_hdl, &pipe_config, &pipe_hdl));
- TEST_ASSERT_NOT_EQUAL(NULL, pipe_hdl);
- return pipe_hdl;
- }
- void test_hcd_pipe_free(hcd_pipe_handle_t pipe_hdl)
- {
- //Get the pipe's event queue from its context variable
- QueueHandle_t pipe_evt_queue = (QueueHandle_t)hcd_pipe_get_context(pipe_hdl);
- TEST_ASSERT_NOT_EQUAL(NULL, pipe_evt_queue);
- //Free the pipe and queue
- TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_free(pipe_hdl));
- vQueueDelete(pipe_evt_queue);
- }
- urb_t *test_hcd_alloc_urb(int num_isoc_packets, size_t data_buffer_size)
- {
- //Allocate a URB and data buffer
- urb_t *urb = heap_caps_calloc(1, sizeof(urb_t) + (num_isoc_packets * sizeof(usb_isoc_packet_desc_t)), MALLOC_CAP_DEFAULT);
- uint8_t *data_buffer = heap_caps_malloc(data_buffer_size, MALLOC_CAP_DMA);
- TEST_ASSERT_NOT_EQUAL(NULL, urb);
- TEST_ASSERT_NOT_EQUAL(NULL, data_buffer);
- //Initialize URB and underlying transfer structure. Need to cast to dummy due to const fields
- usb_transfer_dummy_t *transfer_dummy = (usb_transfer_dummy_t *)&urb->transfer;
- transfer_dummy->data_buffer = data_buffer;
- transfer_dummy->num_isoc_packets = num_isoc_packets;
- return urb;
- }
- void test_hcd_free_urb(urb_t *urb)
- {
- //Free data buffer of the transfer
- heap_caps_free(urb->transfer.data_buffer);
- //Free the URB
- heap_caps_free(urb);
- }
- uint8_t test_hcd_enum_device(hcd_pipe_handle_t default_pipe)
- {
- //We need to create a URB for the enumeration control transfers
- urb_t *urb = test_hcd_alloc_urb(0, sizeof(usb_setup_packet_t) + 256);
- usb_setup_packet_t *setup_pkt = (usb_setup_packet_t *)urb->transfer.data_buffer;
- //Get the device descriptor (note that device might only return 8 bytes)
- USB_SETUP_PACKET_INIT_GET_DEVICE_DESC(setup_pkt);
- urb->transfer.num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_device_desc_t);
- TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb));
- test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE);
- TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe));
- TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status);
- //Update the MPS of the default pipe
- usb_device_desc_t *device_desc = (usb_device_desc_t *)(urb->transfer.data_buffer + sizeof(usb_setup_packet_t));
- TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_update_mps(default_pipe, device_desc->bMaxPacketSize0));
- //Send a set address request
- USB_SETUP_PACKET_INIT_SET_ADDR(setup_pkt, ENUM_ADDR); //We only support one device for now so use address 1
- urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
- TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb));
- test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE);
- TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe));
- TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status);
- //Update address of default pipe
- TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_update_dev_addr(default_pipe, ENUM_ADDR));
- //Send a set configuration request
- USB_SETUP_PACKET_INIT_SET_CONFIG(setup_pkt, ENUM_CONFIG);
- urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
- TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb));
- test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE);
- TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe));
- TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status);
- //Free URB
- test_hcd_free_urb(urb);
- return ENUM_ADDR;
- }
|