|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include "soc/soc_caps.h" |
| 8 | +#if SOC_USB_OTG_SUPPORTED |
| 9 | + |
| 10 | +// |
| 11 | +#include <stdio.h> |
| 12 | +#include <string.h> |
| 13 | +// |
| 14 | +#include "freertos/FreeRTOS.h" |
| 15 | +#include "freertos/task.h" |
| 16 | +#include "freertos/semphr.h" |
| 17 | +// |
| 18 | +#include "esp_system.h" |
| 19 | +#include "esp_log.h" |
| 20 | +#include "esp_err.h" |
| 21 | +// |
| 22 | +#include "unity.h" |
| 23 | +#include "tinyusb.h" |
| 24 | +#include "tusb_cdc_acm.h" |
| 25 | + |
| 26 | +static const char *TAG = "teardown"; |
| 27 | + |
| 28 | +SemaphoreHandle_t wait_mount = NULL; |
| 29 | +SemaphoreHandle_t wait_terminal = NULL; |
| 30 | +SemaphoreHandle_t wait_command = NULL; |
| 31 | + |
| 32 | +static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; |
| 33 | +static uint8_t tx_buf[CONFIG_TINYUSB_CDC_TX_BUFSIZE + 1] = { 0 }; |
| 34 | + |
| 35 | +#define TEARDOWN_CMD_KEY 0xAA |
| 36 | +#define TEARDOWN_RPL_KEY 0x55 |
| 37 | +#define TEARDOWN_CMD_RPL_SIZE ((TUD_OPT_HIGH_SPEED ? 512 : 64)) |
| 38 | +#define TEARDOWN_ATTACH_TIMEOUT_MS 2000 |
| 39 | +#define TEARDOWN_COMMAND_TIMEOUT_MS 3000 |
| 40 | +#define TEARDOWN_AMOUNT 4 |
| 41 | + |
| 42 | +static const tusb_desc_device_t cdc_device_descriptor = { |
| 43 | + .bLength = sizeof(cdc_device_descriptor), |
| 44 | + .bDescriptorType = TUSB_DESC_DEVICE, |
| 45 | + .bcdUSB = 0x0200, |
| 46 | + .bDeviceClass = TUSB_CLASS_MISC, |
| 47 | + .bDeviceSubClass = MISC_SUBCLASS_COMMON, |
| 48 | + .bDeviceProtocol = MISC_PROTOCOL_IAD, |
| 49 | + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, |
| 50 | + .idVendor = USB_ESPRESSIF_VID, |
| 51 | + .idProduct = 0x4002, |
| 52 | + .bcdDevice = 0x0100, |
| 53 | + .iManufacturer = 0x01, |
| 54 | + .iProduct = 0x02, |
| 55 | + .iSerialNumber = 0x03, |
| 56 | + .bNumConfigurations = 0x01 |
| 57 | +}; |
| 58 | + |
| 59 | +static const uint16_t cdc_desc_config_len = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN; |
| 60 | +static const uint8_t cdc_desc_configuration[] = { |
| 61 | + TUD_CONFIG_DESCRIPTOR(1, 2, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), |
| 62 | + TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, (TUD_OPT_HIGH_SPEED ? 512 : 64)), |
| 63 | +}; |
| 64 | + |
| 65 | +#if (TUD_OPT_HIGH_SPEED) |
| 66 | +static const tusb_desc_device_qualifier_t device_qualifier = { |
| 67 | + .bLength = sizeof(tusb_desc_device_qualifier_t), |
| 68 | + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, |
| 69 | + .bcdUSB = 0x0200, |
| 70 | + .bDeviceClass = TUSB_CLASS_MISC, |
| 71 | + .bDeviceSubClass = MISC_SUBCLASS_COMMON, |
| 72 | + .bDeviceProtocol = MISC_PROTOCOL_IAD, |
| 73 | + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, |
| 74 | + .bNumConfigurations = 0x01, |
| 75 | + .bReserved = 0 |
| 76 | +}; |
| 77 | +#endif // TUD_OPT_HIGH_SPEED |
| 78 | + |
| 79 | +static void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event) |
| 80 | +{ |
| 81 | + // Something was received |
| 82 | + xSemaphoreGive(wait_command); |
| 83 | +} |
| 84 | + |
| 85 | +/** |
| 86 | + * @brief CDC device line change callback |
| 87 | + * |
| 88 | + * CDC device signals, that the DTR, RTS states changed |
| 89 | + * |
| 90 | + * @param[in] itf CDC device index |
| 91 | + * @param[in] event CDC event type |
| 92 | + */ |
| 93 | +void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event) |
| 94 | +{ |
| 95 | + int dtr = event->line_state_changed_data.dtr; |
| 96 | + int rts = event->line_state_changed_data.rts; |
| 97 | + ESP_LOGD(TAG, "Line state changed on channel %d: DTR:%d, RTS:%d", itf, dtr, rts); |
| 98 | + |
| 99 | + // Terminal: |
| 100 | + // dtr==1 && rts==1 - connected |
| 101 | + // dtr==0 && rts==0 - disconnected |
| 102 | + xSemaphoreGive(wait_terminal); |
| 103 | +} |
| 104 | + |
| 105 | +// Invoked when device is mounted |
| 106 | +void tud_mount_cb(void) |
| 107 | +{ |
| 108 | + xSemaphoreGive(wait_mount); |
| 109 | +} |
| 110 | + |
| 111 | +/** |
| 112 | + * @brief TinyUSB Teardown specific testcase |
| 113 | + * |
| 114 | + * Scenario: |
| 115 | + * - Installs the tinyUSB driver via esp-tinyusb wrapper with 1xCDC class device |
| 116 | + * - Awaits device configuration be the Host (TEARDOWN_ATTACH_TIMEOUT_MS) |
| 117 | + * - Awaits the terminal connection (TEARDOWN_ATTACH_TIMEOUT_MS) |
| 118 | + * - Expects the command sequence from the Host (TEARDOWN_COMMAND_TIMEOUT_MS) |
| 119 | + * - Replies with the response sequence to the Host |
| 120 | + * - Awaits terminal disconnection (TEARDOWN_ATTACH_TIMEOUT_MS) |
| 121 | + * - Teardowns the tinyUSB driver via esp-tinyusb wrapper |
| 122 | + * - Repeats the steps from the step.1 N times (where N = TEARDOWN_AMOUNT) |
| 123 | + * - Verifies amount of attempts and memory leakage (attempts should be 0) |
| 124 | + * |
| 125 | + * command sequence[] = ep_size * 0xAA |
| 126 | + * response sequence[] = ep_size * 0x55 |
| 127 | + * |
| 128 | + * Hint: Values 0xAA and 0x55 were selected to verify the buffer memory integrity, |
| 129 | + * as the 0xAA and 0x55 are the inversion of each other and the data bits in the same position changes from 1 to 0 in every transaction. |
| 130 | + */ |
| 131 | +TEST_CASE("tinyusb_teardown", "[esp_tinyusb][teardown]") |
| 132 | +{ |
| 133 | + size_t rx_size = 0; |
| 134 | + |
| 135 | + wait_mount = xSemaphoreCreateBinary(); |
| 136 | + TEST_ASSERT_NOT_EQUAL(NULL, wait_mount); |
| 137 | + wait_command = xSemaphoreCreateBinary(); |
| 138 | + TEST_ASSERT_NOT_EQUAL(NULL, wait_command); |
| 139 | + wait_terminal = xSemaphoreCreateBinary(); |
| 140 | + TEST_ASSERT_NOT_EQUAL(NULL, wait_terminal); |
| 141 | + |
| 142 | + // Prep reply |
| 143 | + for (int i = 0; i < TEARDOWN_CMD_RPL_SIZE; i++) { |
| 144 | + tx_buf[i] = TEARDOWN_RPL_KEY; |
| 145 | + } |
| 146 | + |
| 147 | + // TinyUSB driver configuration |
| 148 | + const tinyusb_config_t tusb_cfg = { |
| 149 | + .device_descriptor = &cdc_device_descriptor, |
| 150 | + .string_descriptor = NULL, |
| 151 | + .string_descriptor_count = 0, |
| 152 | + .external_phy = false, |
| 153 | +#if (TUD_OPT_HIGH_SPEED) |
| 154 | + .fs_configuration_descriptor = cdc_desc_configuration, |
| 155 | + .hs_configuration_descriptor = cdc_desc_configuration, |
| 156 | + .qualifier_descriptor = &device_qualifier, |
| 157 | +#else |
| 158 | + .configuration_descriptor = cdc_desc_configuration, |
| 159 | +#endif // TUD_OPT_HIGH_SPEED |
| 160 | + }; |
| 161 | + |
| 162 | + // TinyUSB ACM Driver configuration |
| 163 | + const tinyusb_config_cdcacm_t acm_cfg = { |
| 164 | + .usb_dev = TINYUSB_USBDEV_0, |
| 165 | + .cdc_port = TINYUSB_CDC_ACM_0, |
| 166 | + .rx_unread_buf_sz = 64, |
| 167 | + .callback_rx = &tinyusb_cdc_rx_callback, |
| 168 | + .callback_rx_wanted_char = NULL, |
| 169 | + .callback_line_state_changed = &tinyusb_cdc_line_state_changed_callback, |
| 170 | + .callback_line_coding_changed = NULL |
| 171 | + }; |
| 172 | + |
| 173 | + int attempts = TEARDOWN_AMOUNT; |
| 174 | + while (attempts) { |
| 175 | + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg)); |
| 176 | + // Init CDC 0 |
| 177 | + TEST_ASSERT_FALSE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0)); |
| 178 | + TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_init(&acm_cfg)); |
| 179 | + TEST_ASSERT_TRUE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0)); |
| 180 | + // Wait for the usb event |
| 181 | + ESP_LOGD(TAG, "wait dev mounted..."); |
| 182 | + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_ATTACH_TIMEOUT_MS))); |
| 183 | + ESP_LOGD(TAG, "wait terminal connection..."); |
| 184 | + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_terminal, pdMS_TO_TICKS(TEARDOWN_ATTACH_TIMEOUT_MS))); |
| 185 | + // Wait for the command |
| 186 | + ESP_LOGD(TAG, "wait command..."); |
| 187 | + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_command, pdMS_TO_TICKS(TEARDOWN_COMMAND_TIMEOUT_MS))); |
| 188 | + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_read(TINYUSB_CDC_ACM_0, rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size)); |
| 189 | + for (int i = 0; i < TEARDOWN_CMD_RPL_SIZE; i++) { |
| 190 | + TEST_ASSERT_EQUAL(TEARDOWN_CMD_KEY, rx_buf[i]); |
| 191 | + } |
| 192 | + ESP_LOGD(TAG, "command received"); |
| 193 | + // Reply the response sequence |
| 194 | + ESP_LOGD(TAG, "send response..."); |
| 195 | + TEST_ASSERT_EQUAL(TEARDOWN_CMD_RPL_SIZE, tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, tx_buf, TEARDOWN_CMD_RPL_SIZE)); |
| 196 | + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, pdMS_TO_TICKS(1000))); |
| 197 | + ESP_LOGD(TAG, "wait for terminal disconnection"); |
| 198 | + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_terminal, pdMS_TO_TICKS(TEARDOWN_ATTACH_TIMEOUT_MS))); |
| 199 | + // Teardown |
| 200 | + attempts--; |
| 201 | + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_unregister_callback(TINYUSB_CDC_ACM_0, CDC_EVENT_RX)); |
| 202 | + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_unregister_callback(TINYUSB_CDC_ACM_0, CDC_EVENT_LINE_STATE_CHANGED)); |
| 203 | + TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_deinit(TINYUSB_CDC_ACM_0)); |
| 204 | + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall()); |
| 205 | + } |
| 206 | + // Remove primitives |
| 207 | + vSemaphoreDelete(wait_mount); |
| 208 | + vSemaphoreDelete(wait_command); |
| 209 | + // All attempts should be completed |
| 210 | + TEST_ASSERT_EQUAL(0, attempts); |
| 211 | +} |
| 212 | + |
| 213 | +#endif |
0 commit comments