1 /* 2 * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 #include <stdio.h> 8 #include <string.h> 9 #include "freertos/FreeRTOS.h" 10 #include "freertos/semphr.h" 11 #include "unity.h" 12 #include "test_utils.h" 13 #include "test_usb_mock_classes.h" 14 #include "test_usb_common.h" 15 #include "test_hcd_common.h" 16 17 #define NUM_URBS 3 18 #define NUM_PACKETS_PER_URB 3 19 #define ISOC_PACKET_SIZE MOCK_ISOC_EP_MPS 20 #define URB_DATA_BUFF_SIZE (NUM_PACKETS_PER_URB * ISOC_PACKET_SIZE) 21 #define POST_ENQUEUE_DELAY_US 20 22 23 /* 24 Test HCD ISOC pipe URBs 25 26 Purpose: 27 - Test that an isochronous pipe can be created 28 - URBs can be created and enqueued to the isoc pipe pipe 29 - isoc pipe returns HCD_PIPE_EVENT_URB_DONE for completed URBs 30 - Test utilizes ISOC OUT transfers and do not require ACKs. So the isoc pipe will target a non existing endpoint 31 32 Procedure: 33 - Setup HCD and wait for connection 34 - Allocate default pipe and enumerate the device 35 - Allocate an isochronous pipe and multiple URBs. Each URB should contain multiple packets to test HCD's ability to 36 schedule an URB across multiple intervals. 37 - Enqueue those URBs 38 - Expect HCD_PIPE_EVENT_URB_DONE for each URB. Verify that data is correct using logic analyzer 39 - Deallocate URBs 40 - Teardown 41 */ 42 43 TEST_CASE("Test HCD isochronous pipe URBs", "[hcd][ignore]") 44 { 45 hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port 46 usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection 47 //The MPS of the ISOC OUT pipe is quite large, so we need to bias the FIFO sizing 48 TEST_ASSERT_EQUAL(ESP_OK, hcd_port_set_fifo_bias(port_hdl, HCD_PORT_FIFO_BIAS_PTX)); 49 vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) 50 51 //Enumerate and reset device 52 hcd_pipe_handle_t default_pipe = test_hcd_pipe_alloc(port_hdl, NULL, 0, port_speed); //Create a default pipe (using a NULL EP descriptor) 53 uint8_t dev_addr = test_hcd_enum_device(default_pipe); 54 55 //Create ISOC OUT pipe to non-existent device 56 hcd_pipe_handle_t isoc_out_pipe = test_hcd_pipe_alloc(port_hdl, &mock_isoc_out_ep_desc, dev_addr + 1, port_speed); 57 //Create URBs 58 urb_t *urb_list[NUM_URBS]; 59 //Initialize URBs 60 for (int urb_idx = 0; urb_idx < NUM_URBS; urb_idx++) { 61 urb_list[urb_idx] = test_hcd_alloc_urb(NUM_PACKETS_PER_URB, URB_DATA_BUFF_SIZE); 62 urb_list[urb_idx]->transfer.num_bytes = URB_DATA_BUFF_SIZE; 63 urb_list[urb_idx]->transfer.context = URB_CONTEXT_VAL; 64 for (int pkt_idx = 0; pkt_idx < NUM_PACKETS_PER_URB; pkt_idx++) { 65 urb_list[urb_idx]->transfer.isoc_packet_desc[pkt_idx].num_bytes = ISOC_PACKET_SIZE; 66 //Each packet will consist of the same byte, but each subsequent packet's byte will increment (i.e., packet 0 transmits all 0x0, packet 1 transmits all 0x1) 67 memset(&urb_list[urb_idx]->transfer.data_buffer[pkt_idx * ISOC_PACKET_SIZE], (urb_idx * NUM_URBS) + pkt_idx, ISOC_PACKET_SIZE); 68 } 69 } 70 //Enqueue URBs 71 for (int i = 0; i < NUM_URBS; i++) { 72 TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(isoc_out_pipe, urb_list[i])); 73 } 74 //Wait for each done event from each URB 75 for (int i = 0; i < NUM_URBS; i++) { 76 test_hcd_expect_pipe_event(isoc_out_pipe, HCD_PIPE_EVENT_URB_DONE); 77 } 78 //Dequeue URBs 79 for (int urb_idx = 0; urb_idx < NUM_URBS; urb_idx++) { 80 urb_t *urb = hcd_urb_dequeue(isoc_out_pipe); 81 TEST_ASSERT_EQUAL(urb_list[urb_idx], urb); 82 TEST_ASSERT_EQUAL(URB_CONTEXT_VAL, urb->transfer.context); 83 //Overall URB status and overall number of bytes 84 TEST_ASSERT_EQUAL(URB_DATA_BUFF_SIZE, urb->transfer.actual_num_bytes); 85 TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status); 86 for (int pkt_idx = 0; pkt_idx < NUM_PACKETS_PER_URB; pkt_idx++) { 87 TEST_ASSERT_EQUAL(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.isoc_packet_desc[pkt_idx].status); 88 } 89 } 90 //Free URB list and pipe 91 for (int i = 0; i < NUM_URBS; i++) { 92 test_hcd_free_urb(urb_list[i]); 93 } 94 test_hcd_pipe_free(isoc_out_pipe); 95 test_hcd_pipe_free(default_pipe); 96 //Cleanup 97 test_hcd_wait_for_disconn(port_hdl, false); 98 test_hcd_teardown(port_hdl); 99 } 100 101 /* 102 Test a port sudden disconnect with an active ISOC pipe 103 104 Purpose: Test that when sudden disconnection happens on an HCD port, the ISOC pipe will 105 - Remain active after the HCD_PORT_EVENT_SUDDEN_DISCONN port event 106 - ISOC pipe can be halted 107 - ISOC pipe can be flushed (and transfers status are updated accordingly) 108 109 Procedure: 110 - Setup HCD and wait for connection 111 - Allocate default pipe and enumerate the device 112 - Allocate an isochronous pipe and multiple URBs. Each URB should contain multiple packets to test HCD's ability to 113 schedule an URB across multiple intervals. 114 - Enqueue those URBs 115 - Trigger a disconnect after a short delay 116 - Check that HCD_PORT_EVENT_SUDDEN_DISCONN event is generated. Handle that port event. 117 - Check that both pipes remain in the HCD_PIPE_STATE_ACTIVE after the port error. 118 - Check that both pipes pipe can be halted. 119 - Check that the default pipe can be flushed. A HCD_PIPE_EVENT_URB_DONE event should be generated for the ISOC pipe 120 because it had enqueued URBs. 121 - Check that all URBs can be dequeued and their status is updated 122 - Free both pipes 123 - Teardown 124 */ 125 TEST_CASE("Test HCD isochronous pipe sudden disconnect", "[hcd][ignore]") 126 { 127 hcd_port_handle_t port_hdl = test_hcd_setup(); //Setup the HCD and port 128 usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); //Trigger a connection 129 //The MPS of the ISOC OUT pipe is quite large, so we need to bias the FIFO sizing 130 TEST_ASSERT_EQUAL(ESP_OK, hcd_port_set_fifo_bias(port_hdl, HCD_PORT_FIFO_BIAS_PTX)); 131 vTaskDelay(pdMS_TO_TICKS(100)); //Short delay send of SOF (for FS) or EOPs (for LS) 132 133 //Enumerate and reset device 134 hcd_pipe_handle_t default_pipe = test_hcd_pipe_alloc(port_hdl, NULL, 0, port_speed); //Create a default pipe (using a NULL EP descriptor) 135 uint8_t dev_addr = test_hcd_enum_device(default_pipe); 136 137 //Create ISOC OUT pipe to non-existent device 138 hcd_pipe_handle_t isoc_out_pipe = test_hcd_pipe_alloc(port_hdl, &mock_isoc_out_ep_desc, dev_addr + 1, port_speed); 139 //Create URBs 140 urb_t *urb_list[NUM_URBS]; 141 //Initialize URBs 142 for (int urb_idx = 0; urb_idx < NUM_URBS; urb_idx++) { 143 urb_list[urb_idx] = test_hcd_alloc_urb(NUM_PACKETS_PER_URB, URB_DATA_BUFF_SIZE); 144 urb_list[urb_idx]->transfer.num_bytes = URB_DATA_BUFF_SIZE; 145 urb_list[urb_idx]->transfer.context = URB_CONTEXT_VAL; 146 for (int pkt_idx = 0; pkt_idx < NUM_PACKETS_PER_URB; pkt_idx++) { 147 urb_list[urb_idx]->transfer.isoc_packet_desc[pkt_idx].num_bytes = ISOC_PACKET_SIZE; 148 //Each packet will consist of the same byte, but each subsequent packet's byte will increment (i.e., packet 0 transmits all 0x0, packet 1 transmits all 0x1) 149 memset(&urb_list[urb_idx]->transfer.data_buffer[pkt_idx * ISOC_PACKET_SIZE], (urb_idx * NUM_URBS) + pkt_idx, ISOC_PACKET_SIZE); 150 } 151 } 152 //Enqueue URBs 153 for (int i = 0; i < NUM_URBS; i++) { 154 TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(isoc_out_pipe, urb_list[i])); 155 } 156 //Add a short delay to let the transfers run for a bit 157 esp_rom_delay_us(POST_ENQUEUE_DELAY_US); 158 test_usb_set_phy_state(false, 0); 159 //Disconnect event should have occurred. Handle the port event 160 test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_DISCONNECTION); 161 TEST_ASSERT_EQUAL(HCD_PORT_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl)); 162 TEST_ASSERT_EQUAL(HCD_PORT_STATE_RECOVERY, hcd_port_get_state(port_hdl)); 163 printf("Sudden disconnect\n"); 164 165 //Both pipes should still be active 166 TEST_ASSERT_EQUAL(HCD_PIPE_STATE_ACTIVE, hcd_pipe_get_state(default_pipe)); 167 TEST_ASSERT_EQUAL(HCD_PIPE_STATE_ACTIVE, hcd_pipe_get_state(isoc_out_pipe)); 168 //Halt both pipes 169 TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_command(default_pipe, HCD_PIPE_CMD_HALT)); 170 TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_command(isoc_out_pipe, HCD_PIPE_CMD_HALT)); 171 TEST_ASSERT_EQUAL(HCD_PIPE_STATE_HALTED, hcd_pipe_get_state(default_pipe)); 172 TEST_ASSERT_EQUAL(HCD_PIPE_STATE_HALTED, hcd_pipe_get_state(isoc_out_pipe)); 173 //Flush both pipes. ISOC pipe should return completed URBs 174 TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_command(default_pipe, HCD_PIPE_CMD_FLUSH)); 175 TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_command(isoc_out_pipe, HCD_PIPE_CMD_FLUSH)); 176 177 //Dequeue ISOC URBs 178 for (int urb_idx = 0; urb_idx < NUM_URBS; urb_idx++) { 179 urb_t *urb = hcd_urb_dequeue(isoc_out_pipe); 180 TEST_ASSERT_EQUAL(urb_list[urb_idx], urb); 181 TEST_ASSERT_EQUAL(URB_CONTEXT_VAL, urb->transfer.context); 182 //The URB has either completed entirely or is marked as no_device 183 TEST_ASSERT(urb->transfer.status == USB_TRANSFER_STATUS_COMPLETED || urb->transfer.status == USB_TRANSFER_STATUS_NO_DEVICE); 184 } 185 186 //Free URB list and pipe 187 for (int i = 0; i < NUM_URBS; i++) { 188 test_hcd_free_urb(urb_list[i]); 189 } 190 test_hcd_pipe_free(isoc_out_pipe); 191 test_hcd_pipe_free(default_pipe); 192 //Cleanup 193 test_hcd_teardown(port_hdl); 194 } 195