|
| 1 | +#include <stdio.h> |
| 2 | +#include <string.h> |
| 3 | + |
| 4 | +#include "esp_err.h" |
| 5 | +#include "esp_log.h" |
| 6 | +#include "esp_check.h" |
| 7 | +#include "freertos/FreeRTOS.h" |
| 8 | +#include "freertos/task.h" |
| 9 | +#include "freertos/semphr.h" |
| 10 | + |
| 11 | +#include "usb/cdc_acm_host.h" |
| 12 | +#include "usb/vcp_ch34x.hpp" |
| 13 | +#include "usb/vcp_cp210x.hpp" |
| 14 | +#include "usb/vcp_ftdi.hpp" |
| 15 | +#include "usb/vcp.hpp" |
| 16 | +#include "usb/usb_host.h" |
| 17 | +#include "cdc_wrapper.h" |
| 18 | + |
| 19 | +using namespace esp_usb; |
| 20 | + |
| 21 | +static const char *TAG = "VCP example"; |
| 22 | +static usb_cdc_wrapper_on_data_t s_on_data; |
| 23 | +static volatile bool s_device_connected; |
| 24 | +static SemaphoreHandle_t s_device_disconnected_sem; |
| 25 | +static std::unique_ptr<CdcAcmDevice> s_vcp; |
| 26 | + |
| 27 | +static bool handle_rx(const uint8_t *data, size_t data_len, void *arg) |
| 28 | +{ |
| 29 | + if (s_on_data) { |
| 30 | + s_on_data(data, data_len); |
| 31 | + } |
| 32 | + return true; |
| 33 | +} |
| 34 | + |
| 35 | +static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) |
| 36 | +{ |
| 37 | + switch (event->type) { |
| 38 | + case CDC_ACM_HOST_ERROR: |
| 39 | + ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %d", event->data.error); |
| 40 | + break; |
| 41 | + case CDC_ACM_HOST_DEVICE_DISCONNECTED: |
| 42 | + ESP_LOGI(TAG, "Device suddenly disconnected"); |
| 43 | + xSemaphoreGive(s_device_disconnected_sem); |
| 44 | + s_device_connected = false; |
| 45 | + break; |
| 46 | + case CDC_ACM_HOST_SERIAL_STATE: |
| 47 | + ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); |
| 48 | + break; |
| 49 | + case CDC_ACM_HOST_NETWORK_CONNECTION: |
| 50 | + default: break; |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +static void usb_lib_task(void *arg) |
| 55 | +{ |
| 56 | + while (1) { |
| 57 | + uint32_t event_flags; |
| 58 | + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); |
| 59 | + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { |
| 60 | + ESP_ERROR_CHECK(usb_host_device_free_all()); |
| 61 | + } |
| 62 | + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { |
| 63 | + ESP_LOGI(TAG, "USB: All devices freed"); |
| 64 | + } |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +extern "C" esp_err_t usb_cdc_wrapper_init(usb_cdc_wrapper_on_data_t on_data) |
| 69 | +{ |
| 70 | + s_on_data = on_data; |
| 71 | + s_device_connected = false; |
| 72 | + s_device_disconnected_sem = xSemaphoreCreateBinary(); |
| 73 | + ESP_LOGI(TAG, "Installing USB Host"); |
| 74 | + usb_host_config_t host_config = {}; |
| 75 | + host_config.skip_phy_setup = false; |
| 76 | + host_config.intr_flags = ESP_INTR_FLAG_LEVEL1; |
| 77 | + ESP_RETURN_ON_ERROR(usb_host_install(&host_config), TAG, "usb_host_install failed"); |
| 78 | + |
| 79 | + BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, NULL); |
| 80 | + ESP_RETURN_ON_FALSE(task_created, ESP_ERR_NO_MEM, TAG, "xTaskCreate failed"); |
| 81 | + |
| 82 | + ESP_LOGI(TAG, "Installing CDC-ACM driver"); |
| 83 | + ESP_RETURN_ON_ERROR(cdc_acm_host_install(NULL), TAG, "cdc_acm_host_install failed"); |
| 84 | + |
| 85 | + VCP::register_driver<FT23x>(); |
| 86 | + VCP::register_driver<CP210x>(); |
| 87 | + VCP::register_driver<CH34x>(); |
| 88 | + |
| 89 | + return ESP_OK; |
| 90 | +} |
| 91 | + |
| 92 | +extern "C" esp_err_t usb_cdc_wrapper_wait_for_device_connected(void) |
| 93 | +{ |
| 94 | + s_vcp.reset(); |
| 95 | + const cdc_acm_host_device_config_t dev_config = { |
| 96 | + .connection_timeout_ms = 5000, |
| 97 | + .out_buffer_size = 512, |
| 98 | + .in_buffer_size = 512, |
| 99 | + .event_cb = handle_event, |
| 100 | + .data_cb = handle_rx, |
| 101 | + .user_arg = NULL, |
| 102 | + }; |
| 103 | + ESP_LOGI(TAG, "Opening VCP device..."); |
| 104 | + s_vcp = std::unique_ptr<CdcAcmDevice>(VCP::open(&dev_config)); |
| 105 | + |
| 106 | + if (s_vcp == nullptr) { |
| 107 | + ESP_LOGI(TAG, "Failed to open VCP device"); |
| 108 | + return ESP_FAIL; |
| 109 | + } |
| 110 | + vTaskDelay(10); |
| 111 | + |
| 112 | + ESP_LOGI(TAG, "Setting up line coding"); |
| 113 | + cdc_acm_line_coding_t line_coding = { |
| 114 | + .dwDTERate = 115200, |
| 115 | + .bCharFormat = 0, // 0: 1 stopbit, 1: 1.5 stopbits, 2: 2 stopbits |
| 116 | + .bParityType = 0, // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space |
| 117 | + .bDataBits = 8, |
| 118 | + }; |
| 119 | + ESP_RETURN_ON_ERROR(s_vcp->line_coding_set(&line_coding), TAG, "line_coding_set failed"); |
| 120 | + s_device_connected = true; |
| 121 | + return ESP_OK; |
| 122 | +} |
| 123 | + |
| 124 | +esp_err_t usb_cdc_wrapper_wait_for_device_disconnected(void) |
| 125 | +{ |
| 126 | + xSemaphoreTake(s_device_disconnected_sem, portMAX_DELAY); |
| 127 | + return ESP_OK; |
| 128 | +} |
| 129 | + |
| 130 | +extern "C" esp_err_t usb_cdc_wrapper_set_baudrate(unsigned baudrate) |
| 131 | +{ |
| 132 | + if (!s_device_connected) { |
| 133 | + return ESP_ERR_INVALID_STATE; |
| 134 | + } |
| 135 | + ESP_LOGI(TAG, "Setting baud rate to %u", baudrate); |
| 136 | + cdc_acm_line_coding_t line_coding = { |
| 137 | + .dwDTERate = baudrate, |
| 138 | + .bCharFormat = 0, // 0: 1 stopbit, 1: 1.5 stopbits, 2: 2 stopbits |
| 139 | + .bParityType = 0, // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space |
| 140 | + .bDataBits = 8, |
| 141 | + }; |
| 142 | + ESP_RETURN_ON_ERROR(s_vcp->line_coding_set(&line_coding), TAG, "line_coding_set failed"); |
| 143 | + return ESP_OK; |
| 144 | +} |
| 145 | + |
| 146 | +extern "C" esp_err_t usb_cdc_wrapper_send_data(const uint8_t *data, size_t len) |
| 147 | +{ |
| 148 | + if (!s_device_connected) { |
| 149 | + return ESP_ERR_INVALID_STATE; |
| 150 | + } |
| 151 | + ESP_RETURN_ON_ERROR(s_vcp->tx_blocking((uint8_t *) data, len), TAG, "tx_blocking failed"); |
| 152 | + return ESP_OK; |
| 153 | +} |
| 154 | + |
| 155 | +extern "C" esp_err_t usb_cdc_wrapper_set_line_control(bool dtr, bool rts) |
| 156 | +{ |
| 157 | + if (!s_device_connected) { |
| 158 | + return ESP_ERR_INVALID_STATE; |
| 159 | + } |
| 160 | + ESP_RETURN_ON_ERROR(s_vcp->set_control_line_state(dtr, rts), TAG, "set_control_line_state failed"); |
| 161 | + return ESP_OK; |
| 162 | +} |
0 commit comments