/* * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "freertos/ringbuf.h" #include "freertos/event_groups.h" #include "usb/usb_host.h" #include "private_include/diskio_usb.h" #include "private_include/msc_common.h" #include "include/msc_host.h" #include "private_include/msc_scsi_bot.h" #include "usb/usb_types_ch9.h" #include "usb/usb_helpers.h" #include "cdc/cdc_acm_host/include/usb/usb_types_cdc.h" #include "config/hardware_define.h" #include "os/os.h" #define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN #define SYS_LOG_DOMAIN "USBH" #include "sys_log.h" static portMUX_TYPE msc_lock = portMUX_INITIALIZER_UNLOCKED; #define MSC_ENTER_CRITICAL() portENTER_CRITICAL(&msc_lock) #define MSC_EXIT_CRITICAL() portEXIT_CRITICAL(&msc_lock) #define MSC_GOTO_ON_FALSE_CRITICAL(exp, err) \ do { \ if (!(exp)) { \ MSC_EXIT_CRITICAL(); \ ret = err; \ goto fail; \ } \ } while (0) #define MSC_RETURN_ON_FALSE_CRITICAL(exp, err) \ do { \ if (!(exp)) { \ MSC_EXIT_CRITICAL(); \ return err; \ } \ } while (0) #define WAIT_FOR_READY_TIMEOUT_MS 3000 #define TAG "USB_MSC" #define SCSI_COMMAND_SET 0x06 #define BULK_ONLY_TRANSFER 0x50 #define MSC_NO_SENSE 0x00 #define MSC_NOT_READY 0x02 #define MSC_UNIT_ATTENTION 0x06 typedef struct { usb_host_client_handle_t client_handle; msc_host_event_cb_t user_cb; void *user_arg; SemaphoreHandle_t all_events_handled; volatile bool end_client_event_handling; } msc_driver_t; static msc_driver_t *s_msc_driver; STAILQ_HEAD(devices, msc_host_device) devices_tailq; typedef struct { uint8_t type; // usbport_status_e uint32_t vendor_id; uint32_t product_id; } usb_device_id_t; typedef enum { USB_DEVICE_TYPE_NONE = 0, // 没有外设 USB_DEVICE_TYPE_CDC = 1, // 外设处于cdc状态 USB_DEVICE_TYPE_DFU = 2, // 外设处于dfu状态 USB_DEVICE_TYPE_MSC = 3, // 外设处于dfu状态 } usb_device_type_e; /////// cdc #define CDC_CHECK(a, str, ret) \ if (!(a)) { \ SYS_LOG_ERR("%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret); \ } #define CDC_CHECK_GOTO(a, str, lable) \ if (!(a)) { \ SYS_LOG_ERR("%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ goto lable; \ } #define CDC_DATA_TASK_NAME "cdc-data" #define CDC_DATA_TASK_PRIORITY (5) #define CDC_DATA_TASK_STACK_SIZE 3072 #define CDC_DATA_TASK_CORE 0 // CDC devices often implement Interface Association Descriptor (IAD). Parse IAD only when // bDeviceClass = 0xEF (Miscellaneous Device Class), bDeviceSubClass = 0x02 (Common Class), bDeviceProtocol = 0x01 (Interface Association Descriptor) // @see USB Interface Association Descriptor: Device Class Code and Use Model rev 1.0, Table 1-1 #define USB_SUBCLASS_COMMON 0x02 #define USB_DEVICE_PROTOCOL_IAD 0x01 // CDC-ACM spinlock static portMUX_TYPE cdc_acm_lock = portMUX_INITIALIZER_UNLOCKED; #define CDC_ACM_ENTER_CRITICAL() portENTER_CRITICAL(&cdc_acm_lock) #define CDC_ACM_EXIT_CRITICAL() portEXIT_CRITICAL(&cdc_acm_lock) // CDC-ACM events #define CDC_ACM_TEARDOWN BIT0 #define CDC_ACM_TEARDOWN_COMPLETE BIT1 // CDC-ACM check macros #define CDC_ACM_CHECK(cond, ret_val) ({ \ if (!(cond)) { \ return (ret_val); \ } \ }) #define CDC_ACM_CHECK_FROM_CRIT(cond, ret_val) ({ \ if (!(cond)) { \ CDC_ACM_EXIT_CRITICAL(); \ return ret_val; \ } \ }) // CDC-ACM driver object typedef struct { usb_host_client_handle_t cdc_acm_client_hdl; /*!< USB Host handle reused for all CDC-ACM devices in the system */ SemaphoreHandle_t open_close_mutex; EventGroupHandle_t event_group; SLIST_HEAD(list_dev, cdc_dev_s) cdc_devices_list; /*!< List of open pseudo devices */ } cdc_acm_obj_t; // static cdc_acm_obj_t *p_cdc_acm_obj = NULL; /** * @brief USB CDC PSTN Call Descriptor * * @see Table 3, USB CDC-PSTN specification rev. 1.2 */ typedef struct { uint8_t bFunctionLength; const uint8_t bDescriptorType; const cdc_desc_subtype_t bDescriptorSubtype; union { struct { uint8_t call_management : 1; // Device handles call management itself uint8_t call_over_data_if : 1; // Device sends/receives call management information over Data Class interface uint8_t reserved : 6; }; uint8_t val; } bmCapabilities; uint8_t bDataInterface; // Interface number of Data Class interface optionally used for call management } __attribute__((packed)) cdc_acm_call_desc_t; /** * @brief USB CDC PSTN Abstract Control Model Descriptor * * @see Table 4, USB CDC-PSTN specification rev. 1.2 */ typedef struct { uint8_t bFunctionLength; const uint8_t bDescriptorType; const cdc_desc_subtype_t bDescriptorSubtype; union { struct { uint8_t feature : 1; // Device supports Set/Clear/Get_Comm_Feature requests uint8_t serial : 1; // Device supports Set/Get_Line_Coding, Set_Control_Line_State and Serial_State request and notifications uint8_t send_break : 1; // Device supports Send_Break request uint8_t network : 1; // Device supports Network_Connection notification uint8_t reserved : 4; }; uint8_t val; } bmCapabilities; } __attribute__((packed)) cdc_acm_acm_desc_t; typedef struct cdc_dev_s cdc_dev_t; struct cdc_dev_s { usb_device_handle_t dev_hdl; // USB device handle void *cb_arg; // Common argument for user's callbacks (data IN and Notification) struct { usb_transfer_t *out_xfer; // OUT data transfer usb_transfer_t *in_xfer; // IN data transfer cdc_acm_data_callback_t in_cb; // User's callback for async (non-blocking) data IN const usb_intf_desc_t *intf_desc; // Pointer to data interface descriptor SemaphoreHandle_t out_mux; // OUT mutex } data; struct { usb_transfer_t *xfer; // IN notification transfer const usb_intf_desc_t *intf_desc; // Pointer to notification interface descriptor, can be NULL if there is no notification channel in the device cdc_acm_host_dev_callback_t cb; // User's callback for device events } notif; // Structure with Notif pipe data usb_transfer_t *ctrl_transfer; // CTRL (endpoint 0) transfer SemaphoreHandle_t ctrl_mux; // CTRL mutex cdc_acm_uart_state_t serial_state; // Serial State cdc_comm_protocol_t comm_protocol; cdc_data_protocol_t data_protocol; int num_cdc_intf_desc; // Number of CDC Interface descriptors in following array const usb_standard_desc_t **cdc_intf_desc; // CDC Interface descriptors SLIST_ENTRY(cdc_dev_s) list_entry; }; cdc_dev_t *g_cdc_dev = NULL; /** * @brief Notification received callback * * Notification (interrupt) IN transfer is submitted at the end of this function to ensure periodic poll of IN endpoint. * * @param[in] transfer Transfer that triggered the callback */ static void notif_xfer_cb(usb_transfer_t *transfer); /** * @brief Data received callback * * Data (bulk) IN transfer is submitted at the end of this function to ensure continuous poll of IN endpoint. * * @param[in] transfer Transfer that triggered the callback */ static void in_xfer_cb(usb_transfer_t *transfer); /** * @brief Data send callback * * Reused for bulk OUT and CTRL transfers * * @param[in] transfer Transfer that triggered the callback */ static void out_xfer_cb(usb_transfer_t *transfer); /** * @brief USB Host Client event callback * * Handling of USB device connection/disconnection to/from root HUB. * * @param[in] event_msg Event message type * @param[in] arg Caller's argument (not used in this driver) */ // static void usb_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg); static EventGroupHandle_t s_usb_event_group = NULL; static RingbufHandle_t s_in_ringbuf_handle = NULL; static RingbufHandle_t s_out_ringbuf_handle = NULL; static SemaphoreHandle_t s_usb_read_mux = NULL; static SemaphoreHandle_t s_usb_write_mux = NULL; // static TaskHandle_t s_usb_processing_task_hdl = NULL; // static portMUX_TYPE s_in_ringbuf_mux = portMUX_INITIALIZER_UNLOCKED; static portMUX_TYPE s_out_ringbuf_mux = portMUX_INITIALIZER_UNLOCKED; volatile static int s_in_buffered_data_len = 0; volatile static int s_out_buffered_data_len = 0; #define USB_TASK_KILL_BIT BIT1 #define CDC_DATA_TASK_KILL_BIT BIT4 #define CDC_DEVICE_READY_BIT BIT19 #define TIMEOUT_USB_RINGBUF_MS 200 // Timeout for Ring Buffer push /////// end cdc ///// stm32 dfu update /** * @brief Initializer for a request to get a device's device descriptor */ #define USB_SETUP_PACKET_INIT_GET_FUNCTION_DESC(setup_pkt_ptr) ({ \ (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ (setup_pkt_ptr)->bRequest = USB_B_REQUEST_GET_DESCRIPTOR; \ (setup_pkt_ptr)->wValue = 0x2100; \ (setup_pkt_ptr)->wIndex = 0; \ (setup_pkt_ptr)->wLength = 255; \ }) #ifndef USB_SETUP_PACKET_INIT_GET_STATUS #define USB_SETUP_PACKET_INIT_GET_STATUS(setup_pkt_ptr) ({ \ (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ (setup_pkt_ptr)->bRequest = STM32_DFU_REQUEST_GETSTATUS; \ (setup_pkt_ptr)->wValue = 0; \ (setup_pkt_ptr)->wIndex = 0; \ (setup_pkt_ptr)->wLength = 6; \ }) #endif #define USB_SETUP_PACKET_INIT_CLEAR_STATUS(setup_pkt_ptr) ({ \ (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ (setup_pkt_ptr)->bRequest = STM32_DFU_REQUEST_CLRSTATUS; \ (setup_pkt_ptr)->wValue = 0; \ (setup_pkt_ptr)->wIndex = 0; \ (setup_pkt_ptr)->wLength = 0; \ }) #define USB_SETUP_PACKET_INIT_LOAD_ADDRESS(setup_pkt_ptr, pkt_data) ({ \ (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; \ (setup_pkt_ptr)->bRequest = STM32_DFU_REQUEST_DNLOAD; \ (setup_pkt_ptr)->wValue = 0; \ (setup_pkt_ptr)->wIndex = 0; \ (setup_pkt_ptr)->wLength = 5; \ memcpy(((uint8_t *)(setup_pkt_ptr)) + sizeof(usb_setup_packet_t), pkt_data, 5); \ }) typedef union { struct { uint8_t bLength; uint8_t bDescriptorType; uint8_t bmAttributes; uint16_t wDetachTimeOut; uint16_t wTransferSize; uint8_t bcdDFUVersion; } __attribute__((packed)); uint8_t val[USB_SETUP_PACKET_SIZE]; } usb_function_desc_packet_t; #define ENUM_CTRL_TRANSFER_MAX_LEN 256 #define STM32_DFU_REQUEST_DETACH 0x00 #define STM32_DFU_REQUEST_DNLOAD 0x01 #define STM32_DFU_REQUEST_UPLOAD 0x02 #define STM32_DFU_REQUEST_GETSTATUS 0x03 #define STM32_DFU_REQUEST_CLRSTATUS 0x04 #define STM32_DFU_REQUEST_GETSTATE 0x05 #define STM32_DFU_REQUEST_ABORT 0x06 #define STM32_DFU_STATE_APP_IDLE 0 #define STM32_DFU_STATE_APP_DETACH 1 #define STM32_DFU_STATE_DFU_IDLE 2 #define STM32_DFU_STATE_DNLOAD_SYNC 3 #define STM32_DFU_STATE_DNBUSY 4 #define STM32_DFU_STATE_DNLOAD_IDLE 5 #define STM32_DFU_STATE_MAINFES_SYNC 6 #define STM32_DFU_STATE_MAINFEST 7 #define STM32_DFU_STATE_MAINFEST_WAIT_RESET 8 #define STM32_DFU_STATE_UPLOAD_IDLE 9 #define STM32_DFU_STATE_ERROR 10 ///// end stm32 dfu update // 兼容USB设备 static const usb_device_id_t g_compatiable_usb_device_list[] = { {.type = USB_DEVICE_TYPE_CDC, .vendor_id = 0x1209, .product_id = 0x5741}, {.type = USB_DEVICE_TYPE_CDC, .vendor_id = 0x483, .product_id = 0x5740}, {.type = USB_DEVICE_TYPE_DFU, .vendor_id = 0x483, .product_id = 0xDF11}}; static uint8_t g_current_device_type = USB_DEVICE_TYPE_NONE; static const usb_standard_desc_t *next_interface_desc(const usb_standard_desc_t *desc, size_t len, size_t *offset) { return usb_parse_next_descriptor_of_type(desc, len, USB_W_VALUE_DT_INTERFACE, (int *)offset); } static const usb_standard_desc_t *next_endpoint_desc(const usb_standard_desc_t *desc, size_t len, size_t *offset) { return usb_parse_next_descriptor_of_type(desc, len, USB_B_DESCRIPTOR_TYPE_ENDPOINT, (int *)offset); } static const usb_intf_desc_t *find_msc_interface(const usb_config_desc_t *config_desc, size_t *offset) { size_t total_length = config_desc->wTotalLength; const usb_standard_desc_t *next_desc = (const usb_standard_desc_t *)config_desc; next_desc = next_interface_desc(next_desc, total_length, offset); while (next_desc) { const usb_intf_desc_t *ifc_desc = (const usb_intf_desc_t *)next_desc; if (ifc_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE && ifc_desc->bInterfaceSubClass == SCSI_COMMAND_SET && ifc_desc->bInterfaceProtocol == BULK_ONLY_TRANSFER) { return ifc_desc; } next_desc = next_interface_desc(next_desc, total_length, offset); }; return NULL; } /** * @brief Extracts configuration from configuration descriptor. * * @note Passes interface and endpoint descriptors to obtain: * - interface number, IN endpoint, OUT endpoint, max. packet size * * @param[in] cfg_desc Configuration descriptor * @param[out] cfg Obtained configuration * @return esp_err_t */ static esp_err_t extract_config_from_descriptor(const usb_config_desc_t *cfg_desc, msc_config_t *cfg) { size_t offset = 0; size_t total_len = cfg_desc->wTotalLength; const usb_intf_desc_t *ifc_desc = find_msc_interface(cfg_desc, &offset); assert(ifc_desc); const usb_standard_desc_t *next_desc = (const usb_standard_desc_t *)ifc_desc; const usb_ep_desc_t *ep_desc = NULL; cfg->iface_num = ifc_desc->bInterfaceNumber; next_desc = next_endpoint_desc(next_desc, total_len, &offset); MSC_RETURN_ON_FALSE(next_desc, ESP_ERR_NOT_SUPPORTED); ep_desc = (const usb_ep_desc_t *)next_desc; if (ep_desc->bEndpointAddress & 0x80) { cfg->bulk_in_ep = ep_desc->bEndpointAddress; cfg->bulk_in_mps = ep_desc->wMaxPacketSize; } else { cfg->bulk_out_ep = ep_desc->bEndpointAddress; } next_desc = next_endpoint_desc(next_desc, total_len, &offset); MSC_RETURN_ON_FALSE(next_desc, ESP_ERR_NOT_SUPPORTED); ep_desc = (const usb_ep_desc_t *)next_desc; if (ep_desc->bEndpointAddress & 0x80) { cfg->bulk_in_ep = ep_desc->bEndpointAddress; cfg->bulk_in_mps = ep_desc->wMaxPacketSize; } else { cfg->bulk_out_ep = ep_desc->bEndpointAddress; } return ESP_OK; } static esp_err_t msc_deinit_device(msc_device_t *dev, bool install_failed) { MSC_ENTER_CRITICAL(); MSC_RETURN_ON_FALSE_CRITICAL(dev, ESP_ERR_INVALID_STATE); STAILQ_REMOVE(&devices_tailq, dev, msc_host_device, tailq_entry); MSC_EXIT_CRITICAL(); if (dev->transfer_done) { vSemaphoreDelete(dev->transfer_done); } if (install_failed) { // Error code is unchecked, as it's unknown at what point installation failed. usb_host_interface_release(s_msc_driver->client_handle, dev->handle, dev->config.iface_num); usb_host_device_close(s_msc_driver->client_handle, dev->handle); usb_host_transfer_free(dev->xfer); } else { MSC_RETURN_ON_ERROR(usb_host_interface_release(s_msc_driver->client_handle, dev->handle, dev->config.iface_num)); MSC_RETURN_ON_ERROR(usb_host_device_close(s_msc_driver->client_handle, dev->handle)); MSC_RETURN_ON_ERROR(usb_host_transfer_free(dev->xfer)); } free(dev); return ESP_OK; } // Some MSC devices requires to change its internal state from non-ready to ready static esp_err_t msc_wait_for_ready_state(msc_device_t *dev, size_t timeout_ms) { esp_err_t err; scsi_sense_data_t sense; uint32_t trials = MAX(1, timeout_ms / 100); do { err = scsi_cmd_unit_ready(dev); if (err != ESP_OK) { MSC_RETURN_ON_ERROR(scsi_cmd_sense(dev, &sense)); if (sense.key != MSC_NOT_READY && sense.key != MSC_UNIT_ATTENTION && sense.key != MSC_NO_SENSE) { return ESP_ERR_MSC_INTERNAL; } } vTaskDelay(pdMS_TO_TICKS(100)); } while (trials-- && err); return err; } static bool is_mass_storage_device(uint8_t dev_addr) { size_t dummy = 0; bool is_msc_device = false; usb_device_handle_t device; const usb_config_desc_t *config_desc; if (usb_host_device_open(s_msc_driver->client_handle, dev_addr, &device) == ESP_OK) { if (usb_host_get_active_config_descriptor(device, &config_desc) == ESP_OK) { if (find_msc_interface(config_desc, &dummy)) { is_msc_device = true; } else { SYS_LOG_DBG("Connected USB device is not MSC"); } } usb_host_device_close(s_msc_driver->client_handle, device); } return is_msc_device; } static void event_handler_task(void *arg) { while (1) { usb_host_client_handle_events(s_msc_driver->client_handle, pdMS_TO_TICKS(50)); if (s_msc_driver->end_client_event_handling) { break; } } usb_host_client_unblock(s_msc_driver->client_handle); ESP_ERROR_CHECK(usb_host_client_deregister(s_msc_driver->client_handle)); xSemaphoreGive(s_msc_driver->all_events_handled); vTaskDelete(NULL); } static msc_device_t *find_msc_device(usb_device_handle_t device_handle) { msc_host_device_handle_t device; STAILQ_FOREACH(device, &devices_tailq, tailq_entry) { if (device_handle == device->handle) { return device; } } return NULL; } static void client_event_cb(const usb_host_client_event_msg_t *event, void *arg) { SYS_LOG_INF("CLIENT EVENT DID RECEIVED"); if (event->event == USB_HOST_CLIENT_EVENT_NEW_DEV) { if (is_mass_storage_device(event->new_dev.address)) { const msc_host_event_t msc_event = { .event = MSC_DEVICE_CONNECTED, .device.address = event->new_dev.address, }; g_current_device_type = USB_DEVICE_TYPE_MSC; s_msc_driver->user_cb(&msc_event, s_msc_driver->user_arg); } else { // 检查vendor id和product id,判断是cdc还是dfu设备 usb_device_handle_t device; const usb_device_desc_t *device_desc; uint8_t event_type = 0; uint16_t vendor_id = 0; uint16_t product_id = 0; if (usb_host_device_open(s_msc_driver->client_handle, event->new_dev.address, &device) == ESP_OK) { if (usb_host_get_device_descriptor(device, &device_desc) == ESP_OK) { const int compatiable_dev_count = sizeof(g_compatiable_usb_device_list) / sizeof(usb_device_id_t); for (int j = 0; j < compatiable_dev_count; j++) { const usb_device_id_t *comp_dev_info = &(g_compatiable_usb_device_list[j]); SYS_LOG_INF("loop comp dev: 0x%08x, 0x%08x, %d", comp_dev_info->vendor_id, comp_dev_info->product_id, comp_dev_info->type); if (device_desc->idVendor == comp_dev_info->vendor_id && device_desc->idProduct == comp_dev_info->product_id) { if (comp_dev_info->type == USB_DEVICE_TYPE_CDC) { event_type = CDC_DEVICE_CONNECTED; g_current_device_type = USB_DEVICE_TYPE_CDC; } else if (comp_dev_info->type == USB_DEVICE_TYPE_DFU) { event_type = DFU_DEVICE_CONNECTED; g_current_device_type = USB_DEVICE_TYPE_DFU; } vendor_id = device_desc->idVendor; product_id = device_desc->idProduct; SYS_LOG_INF("get device event:%d", event_type); break; } } } usb_host_device_close(s_msc_driver->client_handle, device); } if (event_type != 0) { const msc_host_event_t msc_event = { .event = event_type, .device.address = event->new_dev.address, .vendor_id = vendor_id, .product_id = product_id, }; s_msc_driver->user_cb(&msc_event, s_msc_driver->user_arg); } } } else if (event->event == USB_HOST_CLIENT_EVENT_DEV_GONE) { msc_device_t *usb_device = find_msc_device(event->dev_gone.dev_hdl); int event = 0; SYS_LOG_INF("DEV GONE:%d", g_current_device_type); switch (g_current_device_type) { case USB_DEVICE_TYPE_CDC: event = CDC_DEVICE_DISCONNECTED; // 退出任务 xEventGroupSetBits(s_usb_event_group, CDC_DATA_TASK_KILL_BIT); SYS_LOG_WRN("Waitting for CDC task delete"); while (xEventGroupGetBits(s_usb_event_group) & CDC_DATA_TASK_KILL_BIT) { vTaskDelay(10 / portTICK_PERIOD_MS); } break; case USB_DEVICE_TYPE_DFU: event = DFU_DEVICE_DISCONNECTED; break; case USB_DEVICE_TYPE_MSC: event = MSC_DEVICE_DISCONNECTED; break; } if (event != 0) { const msc_host_event_t msc_event = { .event = event, .device.handle = usb_device, }; s_msc_driver->user_cb(&msc_event, s_msc_driver->user_arg); } } } esp_err_t msc_host_install(const msc_host_driver_config_t *config) { esp_err_t ret; MSC_RETURN_ON_INVALID_ARG(config); MSC_RETURN_ON_INVALID_ARG(config->callback); if (config->create_backround_task) { MSC_RETURN_ON_FALSE(config->stack_size != 0, ESP_ERR_INVALID_ARG); MSC_RETURN_ON_FALSE(config->task_priority != 0, ESP_ERR_INVALID_ARG); } MSC_RETURN_ON_FALSE(!s_msc_driver, ESP_ERR_INVALID_STATE); msc_driver_t *driver = calloc(1, sizeof(msc_driver_t)); MSC_RETURN_ON_FALSE(driver, ESP_ERR_NO_MEM); driver->user_cb = config->callback; driver->user_arg = config->callback_arg; usb_host_client_config_t client_config = { .async.client_event_callback = client_event_cb, .async.callback_arg = NULL, .max_num_event_msg = 10, }; driver->end_client_event_handling = false; driver->all_events_handled = xSemaphoreCreateBinary(); MSC_GOTO_ON_FALSE(driver->all_events_handled, ESP_ERR_NO_MEM); MSC_GOTO_ON_ERROR(usb_host_client_register(&client_config, &driver->client_handle)); MSC_ENTER_CRITICAL(); MSC_GOTO_ON_FALSE_CRITICAL(!s_msc_driver, ESP_ERR_INVALID_STATE); s_msc_driver = driver; STAILQ_INIT(&devices_tailq); MSC_EXIT_CRITICAL(); if (config->create_backround_task) { BaseType_t task_created = xTaskCreatePinnedToCore( event_handler_task, "USB MSC", config->stack_size, NULL, SBTASK_PRIORITY_USB_MSC, NULL, SBTASK_CORE_INDEX_USB_MSC); MSC_GOTO_ON_FALSE(task_created, ESP_ERR_NO_MEM); } return ESP_OK; fail: s_msc_driver = NULL; usb_host_client_deregister(driver->client_handle); if (driver->all_events_handled) { vSemaphoreDelete(driver->all_events_handled); } free(driver); return ret; } esp_err_t msc_host_uninstall(void) { // Make sure msc driver is installed, // not being uninstalled from other task // and no msc device is registered MSC_ENTER_CRITICAL(); MSC_RETURN_ON_FALSE_CRITICAL(s_msc_driver != NULL, ESP_ERR_INVALID_STATE); MSC_RETURN_ON_FALSE_CRITICAL(!s_msc_driver->end_client_event_handling, ESP_ERR_INVALID_STATE); MSC_RETURN_ON_FALSE_CRITICAL(STAILQ_EMPTY(&devices_tailq), ESP_ERR_INVALID_STATE); s_msc_driver->end_client_event_handling = true; MSC_EXIT_CRITICAL(); xSemaphoreTake(s_msc_driver->all_events_handled, portMAX_DELAY); vSemaphoreDelete(s_msc_driver->all_events_handled); free(s_msc_driver); s_msc_driver = NULL; return ESP_OK; } esp_err_t msc_host_install_device(uint8_t device_address, msc_host_device_handle_t *msc_device_handle) { esp_err_t ret; uint32_t block_size, block_count; const usb_config_desc_t *config_desc; msc_device_t *msc_device; // uint8_t lun; size_t transfer_size = 512; // Normally the smallest block size MSC_GOTO_ON_FALSE(msc_device = calloc(1, sizeof(msc_device_t)), ESP_ERR_NO_MEM); MSC_ENTER_CRITICAL(); MSC_GOTO_ON_FALSE_CRITICAL(s_msc_driver, ESP_ERR_INVALID_STATE); MSC_GOTO_ON_FALSE_CRITICAL(s_msc_driver->client_handle, ESP_ERR_INVALID_STATE); STAILQ_INSERT_TAIL(&devices_tailq, msc_device, tailq_entry); MSC_EXIT_CRITICAL(); MSC_GOTO_ON_FALSE(msc_device->transfer_done = xSemaphoreCreateBinary(), ESP_ERR_NO_MEM); MSC_GOTO_ON_ERROR(usb_host_device_open(s_msc_driver->client_handle, device_address, &msc_device->handle)); MSC_GOTO_ON_ERROR(usb_host_get_active_config_descriptor(msc_device->handle, &config_desc)); MSC_GOTO_ON_ERROR(extract_config_from_descriptor(config_desc, &msc_device->config)); MSC_GOTO_ON_ERROR(usb_host_transfer_alloc(transfer_size, 0, &msc_device->xfer)); MSC_GOTO_ON_ERROR(usb_host_interface_claim(s_msc_driver->client_handle, msc_device->handle, msc_device->config.iface_num, 0)); MSC_GOTO_ON_ERROR(msc_mass_reset(msc_device)); MSC_GOTO_ON_ERROR(scsi_cmd_inquiry(msc_device)); MSC_GOTO_ON_ERROR(msc_wait_for_ready_state(msc_device, WAIT_FOR_READY_TIMEOUT_MS)); MSC_GOTO_ON_ERROR(scsi_cmd_read_capacity(msc_device, &block_size, &block_count)); // Configuration descriptor size of simple MSC device is 32 bytes. if (config_desc->wTotalLength != 32) { SYS_LOG_ERR("COMPOSITE DEVICES UNSUPPORTED"); } msc_device->disk.block_size = block_size; msc_device->disk.block_count = block_count; if (block_size > transfer_size) { usb_transfer_t *larger_xfer; MSC_GOTO_ON_ERROR(usb_host_transfer_alloc(block_size, 0, &larger_xfer)); usb_host_transfer_free(msc_device->xfer); msc_device->xfer = larger_xfer; } *msc_device_handle = msc_device; return ESP_OK; fail: msc_deinit_device(msc_device, true); return ret; } esp_err_t msc_host_uninstall_device(msc_host_device_handle_t device) { MSC_RETURN_ON_INVALID_ARG(device); return msc_deinit_device((msc_device_t *)device, false); } esp_err_t msc_host_read_sector(msc_host_device_handle_t device, size_t sector, void *data, size_t size) { MSC_RETURN_ON_INVALID_ARG(device); msc_device_t *dev = (msc_device_t *)device; return scsi_cmd_read10(dev, data, sector, 1, dev->disk.block_size); } esp_err_t msc_host_write_sector(msc_host_device_handle_t device, size_t sector, const void *data, size_t size) { MSC_RETURN_ON_INVALID_ARG(device); msc_device_t *dev = (msc_device_t *)device; return scsi_cmd_write10(dev, data, sector, 1, dev->disk.block_size); } esp_err_t msc_host_handle_events(uint32_t timeout_ms) { MSC_RETURN_ON_FALSE(s_msc_driver != NULL, ESP_ERR_INVALID_STATE); return usb_host_client_handle_events(s_msc_driver->client_handle, timeout_ms); } static esp_err_t msc_read_string_desc(msc_device_t *dev, uint8_t index, wchar_t *str) { if (index == 0) { // String descriptor not available str[0] = 0; return ESP_OK; } usb_transfer_t *xfer = dev->xfer; USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)xfer->data_buffer, index, 0x409, 64); MSC_RETURN_ON_ERROR(msc_control_transfer(dev, xfer, USB_SETUP_PACKET_SIZE + 64)); usb_standard_desc_t *desc = (usb_standard_desc_t *)(xfer->data_buffer + USB_SETUP_PACKET_SIZE); wchar_t *data = (wchar_t *)(xfer->data_buffer + USB_SETUP_PACKET_SIZE + 2); size_t len = MIN((desc->bLength - USB_STANDARD_DESC_SIZE) / 2, MSC_STR_DESC_SIZE - 1); wcsncpy(str, data, len); str[len] = 0; return ESP_OK; } esp_err_t msc_host_get_device_info(msc_host_device_handle_t device, msc_host_device_info_t *info) { MSC_RETURN_ON_INVALID_ARG(device); MSC_RETURN_ON_INVALID_ARG(info); msc_device_t *dev = (msc_device_t *)device; const usb_device_desc_t *desc; MSC_RETURN_ON_ERROR(usb_host_get_device_descriptor(dev->handle, &desc)); info->idProduct = desc->idProduct; info->idVendor = desc->idVendor; info->sector_size = dev->disk.block_size; info->sector_count = dev->disk.block_count; MSC_RETURN_ON_ERROR(msc_read_string_desc(dev, desc->iManufacturer, info->iManufacturer)); MSC_RETURN_ON_ERROR(msc_read_string_desc(dev, desc->iProduct, info->iProduct)); MSC_RETURN_ON_ERROR(msc_read_string_desc(dev, desc->iSerialNumber, info->iSerialNumber)); return ESP_OK; } esp_err_t msc_host_print_descriptors(msc_host_device_handle_t device) { msc_device_t *dev = (msc_device_t *)device; const usb_device_desc_t *device_desc; const usb_config_desc_t *config_desc; MSC_RETURN_ON_ERROR(usb_host_get_device_descriptor(dev->handle, &device_desc)); MSC_RETURN_ON_ERROR(usb_host_get_active_config_descriptor(dev->handle, &config_desc)); if (CONFIG_SYS_LOG_LEVEL >= SYS_LOG_LEVEL_INF) { usb_print_device_descriptor(device_desc); usb_print_config_descriptor(config_desc, NULL); } return ESP_OK; } static void transfer_callback(usb_transfer_t *transfer) { msc_device_t *device = (msc_device_t *)transfer->context; if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) { ESP_LOGE("Transfer failed", "Status %d", transfer->status); } device->transfer_status = transfer->status; xSemaphoreGive(device->transfer_done); } static esp_err_t wait_for_transfer_done(usb_transfer_t *xfer) { msc_device_t *device = (msc_device_t *)xfer->context; BaseType_t received = xSemaphoreTake(device->transfer_done, pdMS_TO_TICKS(xfer->timeout_ms)); if (received != pdTRUE) { usb_host_endpoint_halt(xfer->device_handle, xfer->bEndpointAddress); usb_host_endpoint_flush(xfer->device_handle, xfer->bEndpointAddress); xSemaphoreTake(device->transfer_done, portMAX_DELAY); return ESP_ERR_TIMEOUT; } return (device->transfer_status == USB_TRANSFER_STATUS_COMPLETED) ? ESP_OK : ESP_FAIL; } static inline bool is_in_endpoint(uint8_t endpoint) { return endpoint & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK ? true : false; } esp_err_t msc_bulk_transfer(msc_device_t *device, uint8_t *data, size_t size, msc_endpoint_t ep) { usb_transfer_t *xfer = device->xfer; MSC_RETURN_ON_FALSE(size <= xfer->data_buffer_size, ESP_ERR_INVALID_SIZE); uint8_t endpoint = (ep == MSC_EP_IN) ? device->config.bulk_in_ep : device->config.bulk_out_ep; if (is_in_endpoint(endpoint)) { xfer->num_bytes = usb_round_up_to_mps(size, device->config.bulk_in_mps); } else { memcpy(xfer->data_buffer, data, size); xfer->num_bytes = size; } xfer->device_handle = device->handle; xfer->bEndpointAddress = endpoint; xfer->callback = transfer_callback; xfer->timeout_ms = 1000; xfer->context = device; MSC_RETURN_ON_ERROR(usb_host_transfer_submit(xfer)); MSC_RETURN_ON_ERROR(wait_for_transfer_done(xfer)); if (is_in_endpoint(endpoint)) { memcpy(data, xfer->data_buffer, size); } return ESP_OK; } esp_err_t msc_control_transfer(msc_device_t *device, usb_transfer_t *xfer, size_t len) { xfer->device_handle = device->handle; xfer->bEndpointAddress = 0; xfer->callback = transfer_callback; xfer->timeout_ms = 1000; xfer->num_bytes = len; xfer->context = device; MSC_RETURN_ON_ERROR(usb_host_transfer_submit_control(s_msc_driver->client_handle, xfer)); return wait_for_transfer_done(xfer); } ////// CDC接口实现 static esp_err_t usb_out_ringbuf_push(const uint8_t *buf, size_t write_bytes, TickType_t xTicksToWait) { int res = xRingbufferSend(s_out_ringbuf_handle, buf, write_bytes, xTicksToWait); if (res != pdTRUE) { SYS_LOG_WRN("The out buffer is too small, the data has been lost %u", write_bytes); return ESP_FAIL; } portENTER_CRITICAL(&s_out_ringbuf_mux); s_out_buffered_data_len += write_bytes; #ifdef CONFIG_CDC_USE_TRACE_FACILITY s_ringbuf_out_max = s_out_buffered_data_len > s_ringbuf_out_max ? s_out_buffered_data_len : s_ringbuf_out_max; #endif portEXIT_CRITICAL(&s_out_ringbuf_mux); return ESP_OK; } static esp_err_t usb_out_ringbuf_pop(uint8_t *buf, size_t req_bytes, size_t *read_bytes, TickType_t ticks_to_wait) { uint8_t *buf_rcv = xRingbufferReceiveUpTo(s_out_ringbuf_handle, read_bytes, ticks_to_wait, req_bytes); if (buf_rcv) { memcpy(buf, buf_rcv, *read_bytes); vRingbufferReturnItem(s_out_ringbuf_handle, (void *)(buf_rcv)); portENTER_CRITICAL(&s_out_ringbuf_mux); s_out_buffered_data_len -= *read_bytes; portEXIT_CRITICAL(&s_out_ringbuf_mux); return ESP_OK; } else { return ESP_ERR_NO_MEM; } } static bool _cdc_driver_is_init(void) { if (s_usb_event_group == NULL) { return false; } return true; } static bool _if_device_ready() { if (!s_usb_event_group) return false; return xEventGroupGetBits(s_usb_event_group) & CDC_DEVICE_READY_BIT; } void cdc_submit_transfer_in(cdc_acm_dev_hdl_t cdc_hdl) { assert(cdc_hdl); cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer"); usb_host_transfer_submit(cdc_dev->data.in_xfer); } int cdc_write_bytes(const uint8_t *buf, size_t length) { // SYS_LOG_INF("write data to cdc devi11111"); CDC_CHECK(buf != NULL, "invalid args", -1); int tx_data_size = 0; if (_cdc_driver_is_init() == false) { SYS_LOG_INF("CDC Driver not installed"); return -1; } if (_if_device_ready() == false) { SYS_LOG_INF("Device not connected or not ready"); return -1; } // SYS_LOG_INF("write data to cdc devi12222"); xSemaphoreTake(s_usb_write_mux, portMAX_DELAY); // SYS_LOG_INF("write data to cdc devi3333"); esp_err_t ret = usb_out_ringbuf_push(buf, length, pdMS_TO_TICKS(TIMEOUT_USB_RINGBUF_MS)); if (ret != ESP_OK) { xSemaphoreGive(s_usb_write_mux); SYS_LOG_DBG("Write ringbuffer failed"); return -1; } tx_data_size = length; xSemaphoreGive(s_usb_write_mux); return tx_data_size; } static esp_err_t cdc_reset_transfer_endpoint(usb_device_handle_t dev_hdl, usb_transfer_t *transfer); static void _cdc_data_task(void *arg) { cdc_dev_t *cdc_dev = (cdc_dev_t *)arg; const uint32_t timeout_ms = 100; ulTaskNotifyTake(pdTRUE, portMAX_DELAY); xEventGroupSetBits(s_usb_event_group, CDC_DEVICE_READY_BIT); SYS_LOG_INF("CDC TASK RUNNING"); while (!(xEventGroupGetBits(s_usb_event_group) & CDC_DATA_TASK_KILL_BIT)) { size_t num_bytes_to_send = 0; // SYS_LOG_INF("buffer addr:%08x, buffer size:%d", (uint32_t)cdc_dev->data.out_xfer->data_buffer, cdc_dev->data.out_xfer->data_buffer_size); esp_err_t ret = usb_out_ringbuf_pop(cdc_dev->data.out_xfer->data_buffer, cdc_dev->data.out_xfer->data_buffer_size, &num_bytes_to_send, 0); if (ret != ESP_OK || num_bytes_to_send == 0) { vTaskDelay(pdMS_TO_TICKS(20)); continue; } // Take OUT mutex and fill the OUT transfer BaseType_t taken = xSemaphoreTake(cdc_dev->data.out_mux, pdMS_TO_TICKS(timeout_ms)); if (taken != pdTRUE) { vTaskDelay(pdMS_TO_TICKS(20)); continue; } ESP_LOGD("CDC_ACM", "Submitting BULK OUT transfer"); // memcpy(cdc_dev->data.out_xfer->data_buffer, data, data_len); cdc_dev->data.out_xfer->num_bytes = num_bytes_to_send; cdc_dev->data.out_xfer->timeout_ms = 100; ESP_GOTO_ON_ERROR(usb_host_transfer_submit(cdc_dev->data.out_xfer), unblock, TAG, ); // Wait for OUT transfer completion taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->data.out_xfer->context, pdMS_TO_TICKS(timeout_ms)); if (!taken) { // Reset the endpoint cdc_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->data.out_xfer); ret = ESP_ERR_TIMEOUT; goto unblock; } ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Bulk OUT transfer error"); ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->actual_num_bytes == num_bytes_to_send, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Incorrect number of bytes transferred"); ret = ESP_OK; unblock: xSemaphoreGive(cdc_dev->data.out_mux); } xEventGroupClearBits(s_usb_event_group, CDC_DATA_TASK_KILL_BIT); SYS_LOG_INF("CDC task deleted"); vTaskDelete(NULL); // esp_err_t ret; // CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); // cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; // CDC_ACM_CHECK(data && (data_len > 0), ESP_ERR_INVALID_ARG); // CDC_ACM_CHECK(cdc_dev->data.out_xfer, ESP_ERR_NOT_SUPPORTED); // Device was opened as read-only. // CDC_ACM_CHECK(data_len <= cdc_dev->data.out_xfer->data_buffer_size, ESP_ERR_INVALID_SIZE); // return ret; } /** * @brief Free USB transfers used by this device * * @note There can be no transfers in flight, at the moment of calling this function. * @param[in] cdc_dev Pointer to CDC device */ static void cdc_transfers_free(cdc_dev_t *cdc_dev) { assert(cdc_dev); usb_host_transfer_free(cdc_dev->notif.xfer); usb_host_transfer_free(cdc_dev->data.in_xfer); if (cdc_dev->data.out_xfer != NULL) { if (cdc_dev->data.out_xfer->context != NULL) { vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->data.out_xfer->context); } if (cdc_dev->data.out_mux != NULL) { vSemaphoreDelete(cdc_dev->data.out_mux); } usb_host_transfer_free(cdc_dev->data.out_xfer); } if (cdc_dev->ctrl_transfer != NULL) { if (cdc_dev->ctrl_transfer->context != NULL) { vSemaphoreDelete((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context); } if (cdc_dev->ctrl_mux != NULL) { vSemaphoreDelete(cdc_dev->ctrl_mux); } usb_host_transfer_free(cdc_dev->ctrl_transfer); } } /** * @brief Allocate CDC transfers * * @param[in] cdc_dev Pointer to CDC device * @param[in] notif_ep_desc Pointer to notification EP descriptor * @param[in] in_ep_desc- Pointer to data IN EP descriptor * @param[in] out_ep_desc Pointer to data OUT EP descriptor * @param[in] out_buf_len Length of data OUT buffer * @return esp_err_t */ static esp_err_t cdc_transfers_allocate(cdc_dev_t *cdc_dev, const usb_ep_desc_t *notif_ep_desc, const usb_ep_desc_t *in_ep_desc, const usb_ep_desc_t *out_ep_desc, size_t out_buf_len) { esp_err_t ret; // 1. Setup notification and control transfers if they are supported if (notif_ep_desc) { ESP_GOTO_ON_ERROR( usb_host_transfer_alloc(USB_EP_DESC_GET_MPS(notif_ep_desc), 0, &cdc_dev->notif.xfer), err, TAG, ); cdc_dev->notif.xfer->device_handle = cdc_dev->dev_hdl; cdc_dev->notif.xfer->bEndpointAddress = notif_ep_desc->bEndpointAddress; cdc_dev->notif.xfer->callback = notif_xfer_cb; cdc_dev->notif.xfer->context = cdc_dev; cdc_dev->notif.xfer->num_bytes = USB_EP_DESC_GET_MPS(notif_ep_desc); usb_device_info_t dev_info; ESP_ERROR_CHECK(usb_host_device_info(cdc_dev->dev_hdl, &dev_info)); ESP_GOTO_ON_ERROR( usb_host_transfer_alloc(dev_info.bMaxPacketSize0, 0, &cdc_dev->ctrl_transfer), err, TAG, ); cdc_dev->ctrl_transfer->timeout_ms = 1000; cdc_dev->ctrl_transfer->bEndpointAddress = 0; cdc_dev->ctrl_transfer->device_handle = cdc_dev->dev_hdl; cdc_dev->ctrl_transfer->context = cdc_dev; cdc_dev->ctrl_transfer->callback = out_xfer_cb; cdc_dev->ctrl_transfer->context = xSemaphoreCreateBinary(); ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->context, ESP_ERR_NO_MEM, err, TAG, ); cdc_dev->ctrl_mux = xSemaphoreCreateMutex(); ESP_GOTO_ON_FALSE(cdc_dev->ctrl_mux, ESP_ERR_NO_MEM, err, TAG, ); } // 2. Setup IN data transfer ESP_GOTO_ON_ERROR( usb_host_transfer_alloc(1024 * 20, 0, &cdc_dev->data.in_xfer), err, TAG, ); assert(cdc_dev->data.in_xfer); cdc_dev->data.in_xfer->callback = in_xfer_cb; cdc_dev->data.in_xfer->num_bytes = 1024 * 20; cdc_dev->data.in_xfer->bEndpointAddress = in_ep_desc->bEndpointAddress; cdc_dev->data.in_xfer->device_handle = cdc_dev->dev_hdl; cdc_dev->data.in_xfer->context = cdc_dev; // 3. Setup OUT bulk transfer (if it is required (out_buf_len > 0)) if (out_buf_len != 0) { ESP_GOTO_ON_ERROR( usb_host_transfer_alloc(out_buf_len, 0, &cdc_dev->data.out_xfer), err, TAG, ); assert(cdc_dev->data.out_xfer); cdc_dev->data.out_xfer->device_handle = cdc_dev->dev_hdl; cdc_dev->data.out_xfer->context = xSemaphoreCreateBinary(); ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->context, ESP_ERR_NO_MEM, err, TAG, ); cdc_dev->data.out_mux = xSemaphoreCreateMutex(); ESP_GOTO_ON_FALSE(cdc_dev->data.out_mux, ESP_ERR_NO_MEM, err, TAG, ); cdc_dev->data.out_xfer->bEndpointAddress = out_ep_desc->bEndpointAddress; cdc_dev->data.out_xfer->callback = out_xfer_cb; } return ESP_OK; err: cdc_transfers_free(cdc_dev); return ret; } /** * @brief Find CDC interface descriptor and its endpoint descriptors * * @note This function is called in open procedure of CDC compliant devices only. * @param[in] cdc_dev Pointer to CDC device * @param[in] intf_idx Index of CDC interface that should be used for this device * @param[out] notif_ep Pointer to notification EP descriptor * @param[out] in_ep Pointer to data IN EP descriptor * @param[out] out_ep Pointer to data OUT EP descriptor * @return esp_err_t */ static esp_err_t cdc_find_intf_and_ep_desc(cdc_dev_t *cdc_dev, uint8_t intf_idx, const usb_ep_desc_t **notif_ep, const usb_ep_desc_t **in_ep, const usb_ep_desc_t **out_ep) { bool interface_found = false; const usb_config_desc_t *config_desc; const usb_device_desc_t *device_desc; int data_intf_idx, notif_intf_idx; int desc_offset = 0; // Get required descriptors ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc)); ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc)); if ((device_desc->bDeviceClass == USB_CLASS_MISC) && (device_desc->bDeviceSubClass == USB_SUBCLASS_COMMON) && (device_desc->bDeviceProtocol == USB_DEVICE_PROTOCOL_IAD)) { // This is a composite device, that uses Interface Association Descriptor SYS_LOG_INF("found 111"); const usb_standard_desc_t *this_desc = (const usb_standard_desc_t *)config_desc; do { this_desc = usb_parse_next_descriptor_of_type( this_desc, config_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, &desc_offset); SYS_LOG_INF("BBBBBFFFF 111"); if (this_desc == NULL) break; // Reached end of configuration descriptor const usb_iad_desc_t *iad_desc = (const usb_iad_desc_t *)this_desc; SYS_LOG_INF("iad_desc->bFirstInterface:%d", iad_desc->bFirstInterface); if (iad_desc->bFirstInterface == intf_idx) { // IAD with correct interface number was found: Check Class/Subclass codes, save Interface indexes assert(iad_desc->bInterfaceCount == 2); assert(iad_desc->bFunctionClass == USB_CLASS_COMM); assert(iad_desc->bFunctionSubClass == CDC_SUBCLASS_ACM); notif_intf_idx = iad_desc->bFirstInterface; data_intf_idx = iad_desc->bFirstInterface + 1; interface_found = true; } } while (!interface_found); } else if ((device_desc->bDeviceClass == USB_CLASS_COMM) && (intf_idx == 0)) { SYS_LOG_INF("device_desc->bDeviceClass:%d, device_desc->bDeviceSubClass:%d, device_desc->bDeviceProtocol:%d", device_desc->bDeviceClass, device_desc->bDeviceSubClass, device_desc->bDeviceProtocol); // This is a Communication Device Class notif_intf_idx = 0; data_intf_idx = 1; interface_found = true; SYS_LOG_INF("found 222"); } // Save found interfaces descriptors: if (interface_found) { // Notification IF and EP cdc_dev->notif.intf_desc = usb_parse_interface_descriptor(config_desc, notif_intf_idx, 0, &desc_offset); assert(cdc_dev->notif.intf_desc); // CDC specific descriptors should be right after CDC-Communication interface descriptor // Note: That's why we use usb_parse_next_descriptor instead of usb_parse_next_descriptor_of_type. // The latter could return CDC specific descriptors that don't belong to this interface const usb_standard_desc_t *cdc_desc = (usb_standard_desc_t *)cdc_dev->notif.intf_desc; do { cdc_desc = usb_parse_next_descriptor(cdc_desc, config_desc->wTotalLength, &desc_offset); if ((cdc_desc == NULL) || (cdc_desc->bDescriptorType != ((USB_CLASS_COMM << 4) | USB_W_VALUE_DT_INTERFACE))) break; // We found all CDC specific descriptors cdc_dev->num_cdc_intf_desc++; cdc_dev->cdc_intf_desc = realloc(cdc_dev->cdc_intf_desc, cdc_dev->num_cdc_intf_desc * (sizeof(usb_standard_desc_t *))); assert(cdc_dev->cdc_intf_desc); cdc_dev->cdc_intf_desc[cdc_dev->num_cdc_intf_desc - 1] = cdc_desc; } while (1); *notif_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->notif.intf_desc, 0, config_desc->wTotalLength, &desc_offset); assert(notif_ep); // Data IF and EP cdc_dev->data.intf_desc = usb_parse_interface_descriptor(config_desc, data_intf_idx, 0, &desc_offset); assert(cdc_dev->data.intf_desc); int temp_offset = desc_offset; for (int i = 0; i < 2; i++) { const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset); assert(this_ep); if (USB_EP_DESC_GET_EP_DIR(this_ep)) { *in_ep = this_ep; } else { *out_ep = this_ep; } desc_offset = temp_offset; } return ESP_OK; } return ESP_ERR_NOT_FOUND; } /** * @brief Start CDC device * * After this call, USB host peripheral will continuously poll IN endpoints. * * @param cdc_dev * @param[in] event_cb Device event callback * @param[in] in_cb Data received callback * @param[in] user_arg Optional user's argument, that will be passed to the callbacks * @return esp_err_t */ static esp_err_t cdc_start(cdc_dev_t *cdc_dev, cdc_acm_host_dev_callback_t event_cb, cdc_acm_data_callback_t in_cb, void *user_arg) { esp_err_t ret = ESP_OK; assert(cdc_dev); CDC_ACM_ENTER_CRITICAL(); cdc_dev->notif.cb = event_cb; cdc_dev->data.in_cb = in_cb; cdc_dev->cb_arg = user_arg; CDC_ACM_EXIT_CRITICAL(); // Claim data interface and start polling its IN endpoint SYS_LOG_INF("interface claim for cdc bulk in"); ESP_GOTO_ON_ERROR(usb_host_interface_claim(s_msc_driver->client_handle, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber, 0), err, TAG, ); ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer"); ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->data.in_xfer)); // If notification are supported, claim its interface and start polling its IN endpoint // if (cdc_dev->notif.intf_desc != NULL) { // if (cdc_dev->notif.intf_desc != cdc_dev->data.intf_desc) { // esp_err_t result = usb_host_interface_claim(s_msc_driver->client_handle, cdc_dev->dev_hdl, cdc_dev->notif.intf_desc->bInterfaceNumber, 0); // SYS_LOG_INF("CLAIM INTERFACE RESULT:%d", result); // ESP_GOTO_ON_ERROR(result, err, TAG, ); // } // ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer"); // ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->notif.xfer)); // } // Everything OK, add the device into list and return // CDC_ACM_ENTER_CRITICAL(); // SLIST_INSERT_HEAD(&p_cdc_acm_obj->cdc_devices_list, cdc_dev, list_entry); // CDC_ACM_EXIT_CRITICAL(); return ret; err: usb_host_interface_release(s_msc_driver->client_handle, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber); if (cdc_dev->notif.intf_desc != NULL) { usb_host_interface_release(s_msc_driver->client_handle, cdc_dev->dev_hdl, cdc_dev->notif.intf_desc->bInterfaceNumber); } return ret; } /** * @brief Open USB device with requested VID/PID * * This function has two regular return paths: * 1. USB device with matching VID/PID is already opened by this driver: allocate new CDC device on top of the already opened USB device. * 2. USB device with matching VID/PID is NOT opened by this driver yet: poll USB connected devices until it is found. * * @note This function will block for timeout_ms, if the device is not enumerated at the moment of calling this function. * @param[in] vid Vendor ID * @param[in] pid Product ID * @param[in] timeout_ms Connection timeout [ms] * @param[out] dev CDC-ACM device * @return esp_err_t */ static esp_err_t cdc_find_and_open_usb_device(uint16_t vid, uint16_t pid, int timeout_ms, cdc_dev_t **dev) { // assert(p_cdc_acm_obj); assert(dev); *dev = calloc(1, sizeof(cdc_dev_t)); if (*dev == NULL) { return ESP_ERR_NO_MEM; } // // First, check list of already opened CDC devices // SYS_LOG_DBG("Checking list of opened USB devices"); // cdc_dev_t *cdc_dev; // SLIST_FOREACH(cdc_dev, &p_cdc_acm_obj->cdc_devices_list, list_entry) // { // const usb_device_desc_t *device_desc; // ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc)); // if (device_desc->idVendor == vid && device_desc->idProduct == pid) { // // Return path 1: // (*dev)->dev_hdl = cdc_dev->dev_hdl; // return ESP_OK; // } // } // Second, poll connected devices until new device is connected or timeout TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); TimeOut_t connection_timeout; vTaskSetTimeOutState(&connection_timeout); while (true) { SYS_LOG_DBG("Checking list of connected USB devices"); uint8_t dev_addr_list[10]; int num_of_devices; ESP_ERROR_CHECK(usb_host_device_addr_list_fill(sizeof(dev_addr_list), dev_addr_list, &num_of_devices)); // Go through device address list and find the one we are looking for for (int i = 0; i < num_of_devices; i++) { usb_device_handle_t current_device; // Open USB device if (usb_host_device_open(s_msc_driver->client_handle, dev_addr_list[i], ¤t_device) != ESP_OK) { continue; // In case we failed to open this device, continue with next one in the list } assert(current_device); const usb_device_desc_t *device_desc; ESP_ERROR_CHECK(usb_host_get_device_descriptor(current_device, &device_desc)); if (device_desc->idVendor == vid && device_desc->idProduct == pid) { // Return path 2: (*dev)->dev_hdl = current_device; return ESP_OK; } usb_host_device_close(s_msc_driver->client_handle, current_device); } if (xTaskCheckForTimeOut(&connection_timeout, &timeout_ticks) != pdFALSE) { break; // Timeout elapsed and the device is not connected } vTaskDelay(pdMS_TO_TICKS(50)); } // Timeout was reached, clean-up free(*dev); *dev = NULL; return ESP_ERR_NOT_FOUND; } /** * @brief Helper function that releases resources claimed by CDC device * * Close underlying USB device, free device driver memory * * @note All interfaces claimed by this device must be release before calling this function * @param cdc_dev CDC device handle to be removed */ static void cdc_device_remove(cdc_dev_t *cdc_dev) { assert(cdc_dev); cdc_transfers_free(cdc_dev); free(cdc_dev->cdc_intf_desc); // We don't check the error code of usb_host_device_close, as the close might fail, if someone else is still using the device (not all interfaces are released) usb_host_device_close(s_msc_driver->client_handle, cdc_dev->dev_hdl); // Gracefully continue on error free(cdc_dev); } esp_err_t cdc_host_open(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret) { esp_err_t ret = ESP_FAIL; // CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); CDC_ACM_CHECK(dev_config, ESP_ERR_INVALID_ARG); CDC_ACM_CHECK(cdc_hdl_ret, ESP_ERR_INVALID_ARG); // xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); // Find underlying USB device cdc_dev_t *cdc_dev; ESP_GOTO_ON_ERROR( cdc_find_and_open_usb_device(vid, pid, dev_config->connection_timeout_ms, &cdc_dev), exit, TAG, "USB device with VID: 0x%04X, PID: 0x%04X not found", vid, pid); // Find and save relevant interface and endpoint descriptors const usb_ep_desc_t *notif_ep = NULL; const usb_ep_desc_t *in_ep = NULL; const usb_ep_desc_t *out_ep = NULL; ESP_GOTO_ON_ERROR( cdc_find_intf_and_ep_desc(cdc_dev, interface_idx, ¬if_ep, &in_ep, &out_ep), err, TAG, "Could not find required interface"); // Check whether found Interfaces are really CDC-ACM assert(cdc_dev->notif.intf_desc->bInterfaceClass == USB_CLASS_COMM); assert(cdc_dev->notif.intf_desc->bInterfaceSubClass == CDC_SUBCLASS_ACM); assert(cdc_dev->notif.intf_desc->bNumEndpoints == 1); assert(cdc_dev->data.intf_desc->bInterfaceClass == USB_CLASS_CDC_DATA); assert(cdc_dev->data.intf_desc->bNumEndpoints == 2); // Save Communication and Data protocols cdc_dev->comm_protocol = (cdc_comm_protocol_t)cdc_dev->notif.intf_desc->bInterfaceProtocol; cdc_dev->data_protocol = (cdc_data_protocol_t)cdc_dev->data.intf_desc->bInterfaceProtocol; // Allocate USB transfers, claim CDC interfaces and return CDC-ACM handle ESP_GOTO_ON_ERROR(cdc_transfers_allocate(cdc_dev, notif_ep, in_ep, out_ep, dev_config->out_buffer_size), err, TAG, ); ESP_GOTO_ON_ERROR(cdc_start(cdc_dev, dev_config->event_cb, dev_config->data_cb, dev_config->user_arg), err, TAG, ); *cdc_hdl_ret = (cdc_acm_dev_hdl_t)cdc_dev; // xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); // cdc_task_args.bulk_in_ep_desc = in_ep; // cdc_task_args.bulk_out_ep_desc = out_ep; SYS_LOG_INF("s_usb_event_group created"); s_usb_event_group = xEventGroupCreate(); CDC_CHECK(s_usb_event_group != NULL, "Create event group failed", ESP_FAIL); if (s_out_ringbuf_handle == NULL) { // s_in_ringbuf_handle = // xRingbufferCreate(4096, RINGBUF_TYPE_BYTEBUF); // CDC_CHECK_GOTO(s_in_ringbuf_handle != NULL, "Create in ringbuffer failed", delete_resource_); s_out_ringbuf_handle = xRingbufferCreate(1024, RINGBUF_TYPE_BYTEBUF); CDC_CHECK_GOTO(s_out_ringbuf_handle != NULL, "Create out ringbuffer failed", delete_resource_); s_usb_read_mux = xSemaphoreCreateMutex(); CDC_CHECK_GOTO(s_usb_read_mux != NULL, "Create read mutex failed", delete_resource_); s_usb_write_mux = xSemaphoreCreateMutex(); CDC_CHECK_GOTO(s_usb_write_mux != NULL, "Create write mutex failed", delete_resource_); } TaskHandle_t cdc_data_task_hdl = NULL; // _cdc_data_task_args_t cdc_task_args = { // .dev_addr = 0, // // .bulk_in_ep_desc = *(cdc_config->bulk_in_ep), // // .bulk_out_ep_desc = *(cdc_config->bulk_out_ep), // .event_group_hdl = s_usb_event_group, // .rx_callback = NULL, // .rx_callback_arg = NULL, // }; xTaskCreatePinnedToCore( _cdc_data_task, CDC_DATA_TASK_NAME, CDC_DATA_TASK_STACK_SIZE, (void *)(cdc_dev), SBTASK_PRIORITY_USB_CDC, &cdc_data_task_hdl, CDC_DATA_TASK_CORE); vTaskDelay(50 / portTICK_PERIOD_MS); xTaskNotifyGive(cdc_data_task_hdl); g_cdc_dev = cdc_dev; SYS_LOG_INF("g_cdc_dev:%08x", (uint32_t)g_cdc_dev); return ESP_OK; err: cdc_device_remove(cdc_dev); exit: // xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); *cdc_hdl_ret = NULL; return ret; delete_resource_: if (s_usb_write_mux) vSemaphoreDelete(s_usb_write_mux); if (s_usb_read_mux) vSemaphoreDelete(s_usb_read_mux); if (s_out_ringbuf_handle) vRingbufferDelete(s_out_ringbuf_handle); if (s_in_ringbuf_handle) vRingbufferDelete(s_in_ringbuf_handle); if (s_usb_event_group) vEventGroupDelete(s_usb_event_group); s_usb_event_group = NULL; s_in_ringbuf_handle = NULL; s_out_ringbuf_handle = NULL; s_usb_read_mux = NULL; s_usb_write_mux = NULL; *cdc_hdl_ret = NULL; return ret; } /** * @brief Check finished transfer status * * Return to on transfer completed OK. * Cancel the transfer and issue user's callback in case of an error. * * @param[in] transfer Transfer to be checked * @return true Transfer completed * @return false Transfer NOT completed */ static bool cdc_is_transfer_completed(usb_transfer_t *transfer) { cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; bool completed = false; switch (transfer->status) { case USB_TRANSFER_STATUS_COMPLETED: completed = true; break; case USB_TRANSFER_STATUS_NO_DEVICE: // User is notified about device disconnection from usb_event_cb case USB_TRANSFER_STATUS_CANCELED: break; case USB_TRANSFER_STATUS_ERROR: case USB_TRANSFER_STATUS_TIMED_OUT: case USB_TRANSFER_STATUS_STALL: case USB_TRANSFER_STATUS_OVERFLOW: case USB_TRANSFER_STATUS_SKIPPED: default: // Transfer was not completed or cancelled by user. Inform user about this if (cdc_dev->notif.cb) { const cdc_acm_host_dev_event_data_t error_event = { .type = CDC_ACM_HOST_ERROR, .data.error = (int)transfer->status}; cdc_dev->notif.cb((cdc_acm_dev_hdl_t)cdc_dev, &error_event, cdc_dev->cb_arg); } } return completed; } static void in_xfer_cb(usb_transfer_t *transfer) { ESP_LOGD("CDC_ACM", "in xfer cb"); cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; if (cdc_is_transfer_completed(transfer)) { if (cdc_dev->data.in_cb) { cdc_dev->data.in_cb(transfer->data_buffer, transfer->actual_num_bytes, cdc_dev->cb_arg); } /* 通过 cdc_submit_transfer_in() 使能传输 */ // ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer"); // usb_host_transfer_submit(cdc_dev->data.in_xfer); } else { SYS_LOG_INF("IN COMPLETE"); } } static void notif_xfer_cb(usb_transfer_t *transfer) { ESP_LOGD("CDC_ACM", "notif xfer cb"); cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; if (cdc_is_transfer_completed(transfer)) { cdc_notification_t *notif = (cdc_notification_t *)transfer->data_buffer; switch (notif->bNotificationCode) { case CDC_NOTIF_NETWORK_CONNECTION: { if (cdc_dev->notif.cb) { const cdc_acm_host_dev_event_data_t net_conn_event = { .type = CDC_ACM_HOST_NETWORK_CONNECTION, .data.network_connected = (bool)notif->wValue}; cdc_dev->notif.cb((cdc_acm_dev_hdl_t)cdc_dev, &net_conn_event, cdc_dev->cb_arg); } break; } case CDC_NOTIF_SERIAL_STATE: { cdc_dev->serial_state.val = *((uint16_t *)notif->Data); if (cdc_dev->notif.cb) { const cdc_acm_host_dev_event_data_t serial_state_event = { .type = CDC_ACM_HOST_SERIAL_STATE, .data.serial_state = cdc_dev->serial_state}; cdc_dev->notif.cb((cdc_acm_dev_hdl_t)cdc_dev, &serial_state_event, cdc_dev->cb_arg); } break; } case CDC_NOTIF_RESPONSE_AVAILABLE: // Encapsulated commands not implemented - fallthrough default: ESP_LOGW("CDC_ACM", "Unsupported notification type 0x%02X", notif->bNotificationCode); ESP_LOG_BUFFER_HEX("CDC_ACM", transfer->data_buffer, transfer->actual_num_bytes); break; } // Start polling for new data again ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer"); usb_host_transfer_submit(cdc_dev->notif.xfer); } } static void out_xfer_cb(usb_transfer_t *transfer) { ESP_LOGD("CDC_ACM", "out/ctrl xfer cb"); assert(transfer->context); xSemaphoreGive((SemaphoreHandle_t)transfer->context); } void cdc_host_desc_print(cdc_acm_dev_hdl_t cdc_hdl) { assert(cdc_hdl); cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; ESP_RETURN_ON_FALSE(cdc_dev->num_cdc_intf_desc > 0, , TAG, "No CDC-ACM specific descriptors found"); for (int i = 0; i < cdc_dev->num_cdc_intf_desc; i++) { switch (((cdc_header_desc_t *)cdc_dev->cdc_intf_desc[i])->bDescriptorSubtype) { case CDC_DESC_SUBTYPE_HEADER: { cdc_header_desc_t *desc = (cdc_header_desc_t *)cdc_dev->cdc_intf_desc[i]; SYS_PRINT("CDC Header Descriptor:\n"); SYS_PRINT("\tbcdCDC: %d.%d0\n", ((desc->bcdCDC >> 8) & 0xF), ((desc->bcdCDC >> 4) & 0xF)); break; } case CDC_DESC_SUBTYPE_CALL: { cdc_acm_call_desc_t *desc = (cdc_acm_call_desc_t *)cdc_dev->cdc_intf_desc[i]; SYS_PRINT("CDC Call Descriptor:\n"); SYS_PRINT("\tbmCapabilities: 0x%02X\n", desc->bmCapabilities.val); SYS_PRINT("\tbDataInterface: %d\n", desc->bDataInterface); break; } case CDC_DESC_SUBTYPE_ACM: { cdc_acm_acm_desc_t *desc = (cdc_acm_acm_desc_t *)cdc_dev->cdc_intf_desc[i]; SYS_PRINT("CDC ACM Descriptor:\n"); SYS_PRINT("\tbmCapabilities: 0x%02X\n", desc->bmCapabilities.val); break; } case CDC_DESC_SUBTYPE_UNION: { cdc_union_desc_t *desc = (cdc_union_desc_t *)cdc_dev->cdc_intf_desc[i]; SYS_PRINT("CDC Union Descriptor:\n"); SYS_PRINT("\tbControlInterface: %d\n", desc->bControlInterface); SYS_PRINT("\tbSubordinateInterface[0]: %d\n", desc->bSubordinateInterface[0]); break; } default: SYS_LOG_WRN("Unsupported CDC specific descriptor"); break; } } } /** * @brief Cancel transfer and reset endpoint * * This function will cancel ongoing transfer a reset its endpoint to ready state. * * @param[in] dev_hdl USB device handle * @param[in] transfer Transfer to be cancelled * @return esp_err_t */ static esp_err_t cdc_reset_transfer_endpoint(usb_device_handle_t dev_hdl, usb_transfer_t *transfer) { assert(dev_hdl); assert(transfer); ESP_RETURN_ON_ERROR(usb_host_endpoint_halt(dev_hdl, transfer->bEndpointAddress), TAG, ); ESP_RETURN_ON_ERROR(usb_host_endpoint_flush(dev_hdl, transfer->bEndpointAddress), TAG, ); usb_host_endpoint_clear(dev_hdl, transfer->bEndpointAddress); return ESP_OK; } esp_err_t cdc_host_close(cdc_acm_dev_hdl_t cdc_hdl) { // CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); // xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; // Cancel polling of BULK IN and INTERRUPT IN endpoints cdc_dev->notif.cb = NULL; cdc_dev->data.in_cb = NULL; ESP_ERROR_CHECK(cdc_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->data.in_xfer)); if (cdc_dev->notif.intf_desc != NULL) { // ESP_ERROR_CHECK(cdc_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->notif.xfer)); } // Release all interfaces ESP_ERROR_CHECK(usb_host_interface_release(s_msc_driver->client_handle, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber)); if ((cdc_dev->notif.intf_desc != NULL) && (cdc_dev->notif.intf_desc != cdc_dev->data.intf_desc)) { // ESP_ERROR_CHECK(usb_host_interface_release(s_msc_driver->client_handle, cdc_dev->dev_hdl, cdc_dev->notif.intf_desc->bInterfaceNumber)); } // CDC_ACM_ENTER_CRITICAL(); // SLIST_REMOVE(&p_cdc_acm_obj->cdc_devices_list, cdc_dev, cdc_dev_s, list_entry); // CDC_ACM_EXIT_CRITICAL(); cdc_device_remove(cdc_dev); // xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); return ESP_OK; } ///////// dfu 设备处理 /** * @brief Allocate CDC transfers * * @param[in] cdc_dev Pointer to CDC device * @param[in] notif_ep_desc Pointer to notification EP descriptor * @param[in] in_ep_desc- Pointer to data IN EP descriptor * @param[in] out_ep_desc Pointer to data OUT EP descriptor * @param[in] out_buf_len Length of data OUT buffer * @return esp_err_t */ static esp_err_t dfu_transfers_allocate(cdc_dev_t *cdc_dev) { esp_err_t ret; // 1. Setup notification and control transfers if they are supported SYS_LOG_INF("PRE DFU dfu_transfers_allocate"); // if (notif_ep_desc) { SYS_LOG_INF("IN DFU dfu_transfers_allocate"); ESP_GOTO_ON_ERROR( usb_host_transfer_alloc(512, 0, &cdc_dev->notif.xfer), err, TAG, ); cdc_dev->notif.xfer->device_handle = cdc_dev->dev_hdl; cdc_dev->notif.xfer->bEndpointAddress = 0; cdc_dev->notif.xfer->callback = notif_xfer_cb; cdc_dev->notif.xfer->context = cdc_dev; cdc_dev->notif.xfer->num_bytes = 512; usb_device_info_t dev_info; ESP_ERROR_CHECK(usb_host_device_info(cdc_dev->dev_hdl, &dev_info)); ESP_GOTO_ON_ERROR( usb_host_transfer_alloc(4096, 0, &cdc_dev->ctrl_transfer), err, TAG, ); cdc_dev->ctrl_transfer->timeout_ms = 1000; cdc_dev->ctrl_transfer->bEndpointAddress = 0; cdc_dev->ctrl_transfer->device_handle = cdc_dev->dev_hdl; cdc_dev->ctrl_transfer->context = cdc_dev; cdc_dev->ctrl_transfer->callback = out_xfer_cb; cdc_dev->ctrl_transfer->context = xSemaphoreCreateBinary(); ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->context, ESP_ERR_NO_MEM, err, TAG, ); cdc_dev->ctrl_mux = xSemaphoreCreateMutex(); ESP_GOTO_ON_FALSE(cdc_dev->ctrl_mux, ESP_ERR_NO_MEM, err, TAG, ); // } // // 2. Setup IN data transfer // ESP_GOTO_ON_ERROR( // usb_host_transfer_alloc(512, 0, &cdc_dev->data.in_xfer), // err, // TAG, ); // assert(cdc_dev->data.in_xfer); // cdc_dev->data.in_xfer->callback = in_xfer_cb; // cdc_dev->data.in_xfer->num_bytes = 512; // cdc_dev->data.in_xfer->bEndpointAddress = in_ep_desc->bEndpointAddress; // cdc_dev->data.in_xfer->device_handle = cdc_dev->dev_hdl; // cdc_dev->data.in_xfer->context = cdc_dev; // // 3. Setup OUT bulk transfer (if it is required (out_buf_len > 0)) // if (out_buf_len != 0) { // ESP_GOTO_ON_ERROR( // usb_host_transfer_alloc(out_buf_len, 0, &cdc_dev->data.out_xfer), // err, // TAG, ); // assert(cdc_dev->data.out_xfer); // cdc_dev->data.out_xfer->device_handle = cdc_dev->dev_hdl; // cdc_dev->data.out_xfer->context = xSemaphoreCreateBinary(); // ESP_GOTO_ON_FALSE(cdc_dev->data.out_xfer->context, ESP_ERR_NO_MEM, err, TAG, ); // cdc_dev->data.out_mux = xSemaphoreCreateMutex(); // ESP_GOTO_ON_FALSE(cdc_dev->data.out_mux, ESP_ERR_NO_MEM, err, TAG, ); // cdc_dev->data.out_xfer->bEndpointAddress = out_ep_desc->bEndpointAddress; // cdc_dev->data.out_xfer->callback = out_xfer_cb; // } return ESP_OK; err: cdc_transfers_free(cdc_dev); return ret; } /** * @brief Find CDC interface descriptor and its endpoint descriptors * * @note This function is called in open procedure of CDC compliant devices only. * @param[in] cdc_dev Pointer to CDC device * @param[in] intf_idx Index of CDC interface that should be used for this device * @param[out] notif_ep Pointer to notification EP descriptor * @param[out] in_ep Pointer to data IN EP descriptor * @param[out] out_ep Pointer to data OUT EP descriptor * @return esp_err_t */ // static esp_err_t dfu_find_intf_and_ep_desc(cdc_dev_t *cdc_dev, uint8_t intf_idx, const usb_ep_desc_t **notif_ep) // { // bool interface_found = false; // const usb_config_desc_t *config_desc; // const usb_device_desc_t *device_desc; // int data_intf_idx, notif_intf_idx; // int desc_offset = 0; // // Get required descriptors // ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc)); // ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc)); // SYS_LOG_INF("get inpoint step0, device_desc->bDeviceClass:%d, device_desc->bDeviceSubClass:%d, device_desc->bDeviceProtocol:%d", device_desc->bDeviceClass, device_desc->bDeviceSubClass, device_desc->bDeviceProtocol); // // if ((device_desc->bDeviceClass == USB_CLASS_MISC) && (device_desc->bDeviceSubClass == USB_SUBCLASS_COMMON) && (device_desc->bDeviceProtocol == USB_DEVICE_PROTOCOL_IAD)) { // // This is a composite device, that uses Interface Association Descriptor // SYS_LOG_INF("found 111"); // // const usb_standard_desc_t *this_desc = (const usb_standard_desc_t *)config_desc; // // do { // // SYS_LOG_INF("config_desc:%08x", (uint32_t)config_desc); // // this_desc = usb_parse_next_descriptor_of_type( // // this_desc, config_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE, &desc_offset); // // SYS_LOG_INF("fjdlkfladjfkldasjkfl:%d, this_desc:%08x", config_desc->wTotalLength, (uint32_t)this_desc); // // if (this_desc == NULL) // // break; // Reached end of configuration descriptor // // const usb_iad_desc_t *iad_desc = (const usb_iad_desc_t *)this_desc; // // SYS_LOG_INF("iad_desc->bFirstInterfa:%d", iad_desc->bFirstInterface); // // if (iad_desc->bFirstInterface == intf_idx) { // // // IAD with correct interface number was found: Check Class/Subclass codes, save Interface indexes // // SYS_LOG_INF() // // assert(iad_desc->bInterfaceCount == 2); // // assert(iad_desc->bFunctionClass == USB_CLASS_COMM); // // assert(iad_desc->bFunctionSubClass == CDC_SUBCLASS_ACM); // // notif_intf_idx = iad_desc->bFirstInterface; // // data_intf_idx = iad_desc->bFirstInterface + 1; // // interface_found = true; // // } // // } while (!interface_found); // // // } else if ((device_desc->bDeviceClass == USB_CLASS_COMM) && (intf_idx == 0)) { // // // // This is a Communication Device Class // // // notif_intf_idx = 0; // // // data_intf_idx = 1; // // // interface_found = true; // // // SYS_LOG_INF("found 222"); // // // } // // Save found interfaces descriptors: // interface_found = true; // if (interface_found) { // // Notification IF and EP // cdc_dev->notif.intf_desc = usb_parse_interface_descriptor(config_desc, 0, 0, &desc_offset); // assert(cdc_dev->notif.intf_desc); // // CDC specific descriptors should be right after CDC-Communication interface descriptor // // Note: That's why we use usb_parse_next_descriptor instead of usb_parse_next_descriptor_of_type. // // The latter could return CDC specific descriptors that don't belong to this interface // const usb_standard_desc_t *cdc_desc = (usb_standard_desc_t *)cdc_dev->notif.intf_desc; // do { // cdc_desc = usb_parse_next_descriptor(cdc_desc, config_desc->wTotalLength, &desc_offset); // if ((cdc_desc == NULL) || (cdc_desc->bDescriptorType != ((USB_CLASS_COMM << 4) | USB_W_VALUE_DT_INTERFACE))) // break; // We found all CDC specific descriptors // cdc_dev->num_cdc_intf_desc++; // cdc_dev->cdc_intf_desc = // realloc(cdc_dev->cdc_intf_desc, cdc_dev->num_cdc_intf_desc * (sizeof(usb_standard_desc_t *))); // assert(cdc_dev->cdc_intf_desc); // cdc_dev->cdc_intf_desc[cdc_dev->num_cdc_intf_desc - 1] = cdc_desc; // } while (1); // SYS_LOG_INF("BEFORE NOTIFY EP SETUP DONE"); // *notif_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->notif.intf_desc, 0, config_desc->wTotalLength, &desc_offset); // assert(notif_ep); // // // Data IF and EP // // cdc_dev->data.intf_desc = usb_parse_interface_descriptor(config_desc, data_intf_idx, 0, &desc_offset); // // assert(cdc_dev->data.intf_desc); // // int temp_offset = desc_offset; // // for (int i = 0; i < 2; i++) { // // const usb_ep_desc_t *this_ep = usb_parse_endpoint_descriptor_by_index(cdc_dev->data.intf_desc, i, config_desc->wTotalLength, &desc_offset); // // assert(this_ep); // // if (USB_EP_DESC_GET_EP_DIR(this_ep)) { // // *in_ep = this_ep; // // } else { // // *out_ep = this_ep; // // } // // desc_offset = temp_offset; // // } // return ESP_OK; // } // return ESP_ERR_NOT_FOUND; // } esp_err_t dfu_host_open(uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret) { esp_err_t ret; // CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); CDC_ACM_CHECK(dev_config, ESP_ERR_INVALID_ARG); CDC_ACM_CHECK(cdc_hdl_ret, ESP_ERR_INVALID_ARG); // xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); // Find underlying USB device cdc_dev_t *cdc_dev; ESP_GOTO_ON_ERROR( cdc_find_and_open_usb_device(vid, pid, dev_config->connection_timeout_ms, &cdc_dev), exit, TAG, "USB device with VID: 0x%04X, PID: 0x%04X not found", vid, pid); // Find and save relevant interface and endpoint descriptors // const usb_ep_desc_t *notif_ep = NULL; // const usb_ep_desc_t *in_ep = NULL; // const usb_ep_desc_t *out_ep = NULL; // ESP_GOTO_ON_ERROR( // dfu_find_intf_and_ep_desc(cdc_dev, interface_idx, ¬if_ep), // err, // TAG, // "Could not find required interface"); // // Check whether found Interfaces are really CDC-ACM // assert(cdc_dev->notif.intf_desc->bInterfaceClass == USB_CLASS_COMM); // assert(cdc_dev->notif.intf_desc->bInterfaceSubClass == CDC_SUBCLASS_ACM); // assert(cdc_dev->notif.intf_desc->bNumEndpoints == 1); // assert(cdc_dev->data.intf_desc->bInterfaceClass == USB_CLASS_CDC_DATA); // assert(cdc_dev->data.intf_desc->bNumEndpoints == 2); // // Save Communication and Data protocols // cdc_dev->comm_protocol = (cdc_comm_protocol_t)cdc_dev->notif.intf_desc->bInterfaceProtocol; // cdc_dev->data_protocol = (cdc_data_protocol_t)cdc_dev->data.intf_desc->bInterfaceProtocol; // // Allocate USB transfers, claim CDC interfaces and return CDC-ACM handle ESP_GOTO_ON_ERROR(dfu_transfers_allocate(cdc_dev), err, TAG, ); // ESP_GOTO_ON_ERROR(cdc_start(cdc_dev, dev_config->event_cb, dev_config->data_cb, dev_config->user_arg), err, TAG, ); *cdc_hdl_ret = (cdc_acm_dev_hdl_t)cdc_dev; g_cdc_dev = cdc_dev; SYS_LOG_INF("g_cdc_dev:%08x", (uint32_t)g_cdc_dev); // xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); return ESP_OK; err: cdc_device_remove(cdc_dev); exit: // xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); *cdc_hdl_ret = NULL; return ret; } esp_err_t dfu_host_close(cdc_acm_dev_hdl_t cdc_hdl) { // CDC_ACM_CHECK(p_cdc_acm_obj, ESP_ERR_INVALID_STATE); CDC_ACM_CHECK(cdc_hdl, ESP_ERR_INVALID_ARG); // xSemaphoreTake(p_cdc_acm_obj->open_close_mutex, portMAX_DELAY); cdc_dev_t *cdc_dev = (cdc_dev_t *)cdc_hdl; // // Cancel polling of BULK IN and INTERRUPT IN endpoints // cdc_dev->notif.cb = NULL; // cdc_dev->data.in_cb = NULL; // ESP_ERROR_CHECK(cdc_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->data.in_xfer)); // if (cdc_dev->notif.intf_desc != NULL) { // ESP_ERROR_CHECK(cdc_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->notif.xfer)); // } // // Release all interfaces // ESP_ERROR_CHECK(usb_host_interface_release(s_msc_driver->client_handle, cdc_dev->dev_hdl, cdc_dev->data.intf_desc->bInterfaceNumber)); // if ((cdc_dev->notif.intf_desc != NULL) && (cdc_dev->notif.intf_desc != cdc_dev->data.intf_desc)) { // ESP_ERROR_CHECK(usb_host_interface_release(s_msc_driver->client_handle, cdc_dev->dev_hdl, cdc_dev->notif.intf_desc->bInterfaceNumber)); // } // // CDC_ACM_ENTER_CRITICAL(); // // SLIST_REMOVE(&p_cdc_acm_obj->cdc_devices_list, cdc_dev, cdc_dev_s, list_entry); // // CDC_ACM_EXIT_CRITICAL(); cdc_device_remove(cdc_dev); // xSemaphoreGive(p_cdc_acm_obj->open_close_mutex); return ESP_OK; } ///// stm32固件升级 static esp_err_t _usb_control_transfer_ex(cdc_dev_t *cdc_dev, uint8_t direction, uint8_t request, uint16_t value, uint16_t interface, uint16_t length, uint8_t *packet_data, uint8_t *out_buffer, uint16_t out_buffer_size, uint16_t *actual_result_size, uint16_t timeout) { esp_err_t ret = ESP_FAIL; CDC_ACM_CHECK(cdc_dev->ctrl_transfer, ESP_ERR_NOT_SUPPORTED); CDC_ACM_CHECK(cdc_dev->ctrl_transfer->data_buffer_size >= length, ESP_ERR_INVALID_SIZE); // Take Mutex and fill the CTRL request BaseType_t taken = xSemaphoreTake(cdc_dev->ctrl_mux, pdMS_TO_TICKS(1000)); if (!taken) { return ESP_ERR_TIMEOUT; } usb_setup_packet_t *req = (usb_setup_packet_t *)(cdc_dev->ctrl_transfer->data_buffer); uint8_t *start_of_data = (uint8_t *)req + sizeof(usb_setup_packet_t); req->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; req->bRequest = request; req->wValue = value; req->wIndex = interface; req->wLength = length; if (direction == USB_BM_REQUEST_TYPE_DIR_IN) { req->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; } else { memcpy(start_of_data, packet_data, length); } cdc_dev->ctrl_transfer->num_bytes = length + sizeof(usb_setup_packet_t); ESP_GOTO_ON_ERROR( usb_host_transfer_submit_control(s_msc_driver->client_handle, cdc_dev->ctrl_transfer), unblock, TAG, "CTRL transfer failed"); taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context, pdMS_TO_TICKS(timeout)); // This is a fixed timeout. Every CDC device should be able to respond to CTRL transfer in 1 second if (!taken) { // Transfer was not finished, error in USB LIB. Reset the endpoint cdc_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->ctrl_transfer); ret = ESP_ERR_TIMEOUT; goto unblock; } ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Control transfer error"); ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->actual_num_bytes == cdc_dev->ctrl_transfer->num_bytes, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Incorrect number of bytes transferred"); if (direction == USB_BM_REQUEST_TYPE_DIR_IN) { uint16_t result_data_length = cdc_dev->ctrl_transfer->actual_num_bytes - sizeof(usb_setup_packet_t); if (result_data_length > out_buffer_size) { result_data_length = out_buffer_size; } memcpy(out_buffer, start_of_data, result_data_length); if (actual_result_size) { *actual_result_size = result_data_length; } } ret = ESP_OK; unblock: xSemaphoreGive(cdc_dev->ctrl_mux); return ret; } static esp_err_t _usb_control_transfer(cdc_dev_t *cdc_dev, uint8_t direction, uint8_t request, uint16_t value, uint16_t interface, uint16_t length, uint8_t *packet_data, uint8_t *out_buffer, uint16_t out_buffer_size, uint16_t *actual_result_size) { esp_err_t ret = _usb_control_transfer_ex(cdc_dev, direction, request, value, interface, length, packet_data, out_buffer, out_buffer_size, actual_result_size, 5000); return ret; } static esp_err_t _usb_get_status_ex(cdc_dev_t *cdc_dev, uint8_t *out_result_data /*[6]*/, uint16_t timeout) { return _usb_control_transfer_ex(cdc_dev, USB_BM_REQUEST_TYPE_DIR_IN, STM32_DFU_REQUEST_GETSTATUS, 0, 0, 6, 0, out_result_data, 6, NULL, timeout); } esp_err_t usbh_stm32_get_status_ex(uint8_t *out_result_data /*[6]*/, uint16_t timeout) { if (g_cdc_dev != NULL) { return _usb_get_status_ex(g_cdc_dev, out_result_data, timeout); } return ESP_FAIL; } esp_err_t usbh_stm32_get_status(uint8_t *out_result_data /*[6]*/) { // return _usbh_ return usbh_stm32_get_status_ex(out_result_data, 500); } static esp_err_t _usb_get_string(cdc_dev_t *cdc_dev, uint8_t index, char *out_string, uint16_t max_length, uint16_t *actual_length) { esp_err_t ret; SYS_LOG_INF("GET STRING 00000"); CDC_ACM_CHECK(cdc_dev->ctrl_transfer, ESP_ERR_NOT_SUPPORTED); // CDC_ACM_CHECK(cdc_dev->ctrl_transfer->data_buffer_size >= data_len, ESP_ERR_INVALID_SIZE); // Take Mutex and fill the CTRL request SYS_LOG_INF("GET STRING 1111"); BaseType_t taken = xSemaphoreTake(cdc_dev->ctrl_mux, pdMS_TO_TICKS(1000)); if (!taken) { return ESP_ERR_TIMEOUT; } SYS_LOG_INF("GET STRING 2222"); // usb_setup_packet_t *req = (usb_setup_packet_t *)(cdc_dev->ctrl_transfer->data_buffer); // uint8_t *start_of_data = (uint8_t *)cdc_dev->ctrl_transfer->data_buffer; USB_SETUP_PACKET_INIT_GET_STR_DESC( (usb_setup_packet_t *)cdc_dev->ctrl_transfer->data_buffer, index, 0x409, ENUM_CTRL_TRANSFER_MAX_LEN); cdc_dev->ctrl_transfer->num_bytes = sizeof(usb_setup_packet_t) + ENUM_CTRL_TRANSFER_MAX_LEN; // req->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; // req->bRequest = request; // req->wValue = value; // req->wIndex = cdc_dev->notif.intf_desc->bInterfaceNumber; // req->wLength = data_len; cdc_dev->ctrl_transfer->num_bytes = ENUM_CTRL_TRANSFER_MAX_LEN + sizeof(usb_setup_packet_t); SYS_LOG_INF("cdc_dev->ctrl_transfer->num_bytes:%d, cdc_dev->ctrl_transfer->data_buffer_size:%d", cdc_dev->ctrl_transfer->num_bytes, cdc_dev->ctrl_transfer->data_buffer_size); ESP_GOTO_ON_ERROR( usb_host_transfer_submit_control(s_msc_driver->client_handle, cdc_dev->ctrl_transfer), unblock, TAG, "CTRL transfer failed"); SYS_LOG_INF("GET STRING 33333"); taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context, pdMS_TO_TICKS(1000)); // This is a fixed timeout. Every CDC device should be able to respond to CTRL transfer in 1 second if (!taken) { // Transfer was not finished, error in USB LIB. Reset the endpoint cdc_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->ctrl_transfer); ret = ESP_ERR_TIMEOUT; SYS_LOG_INF("GET STRING 5555"); goto unblock; } SYS_LOG_INF("GET STRING 6666"); ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Control transfer error"); SYS_LOG_INF("GET STRING 7777"); // ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->actual_num_bytes == cdc_dev->ctrl_transfer->num_bytes, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Incorrect number of bytes transferred"); SYS_LOG_INF("GET STRING 88888:%d", cdc_dev->ctrl_transfer->actual_num_bytes); uint16_t actual_string_length = 0; for (int i = 10; i < cdc_dev->ctrl_transfer->actual_num_bytes; i += 2) { if (actual_string_length > max_length) { break; } out_string[actual_string_length] = cdc_dev->ctrl_transfer->data_buffer[i]; actual_string_length += 1; } SYS_LOG_INF("GET STRING 9999:%s", out_string); ret = ESP_OK; unblock: xSemaphoreGive(cdc_dev->ctrl_mux); return ret; } static void _usb_get_interface_descriptors(cdc_dev_t *cdc_dev, int interface_num, char *descriptors, uint8_t max_descriptors_count, uint8_t *actual_descriptors_count) { const usb_config_desc_t *config_desc; int i = 0; const usb_standard_desc_t *this_desc = NULL; SYS_LOG_INF("pre get active config desc done:%d", (uint32_t)cdc_dev->dev_hdl); ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(cdc_dev->dev_hdl, &config_desc)); this_desc = (const usb_standard_desc_t *)config_desc; int desc_offset = 0; SYS_LOG_INF("get active config desc done"); do { this_desc = usb_parse_next_descriptor_of_type( this_desc, config_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE, &desc_offset); if (this_desc == NULL) { break; // Reached end of configuration descriptor } const usb_intf_desc_t *intf_desc = (const usb_intf_desc_t *)this_desc; char *result_string = descriptors + i * 255; memset(result_string, 0, 255); _usb_get_string(cdc_dev, intf_desc->iInterface, result_string, 255, NULL); i += 1; if (i >= max_descriptors_count) { break; } } while (true); for (int k = 0; k < i; k++) { SYS_LOG_INF("index:%d, descriptor:%s", k, descriptors + k * 255); } if (actual_descriptors_count) { *actual_descriptors_count = i; } } void usbh_stm32_get_chipinfo(char *descriptors, uint8_t count, uint8_t *actual_desc_count) { SYS_LOG_INF("before usbh_stm32_get_chipinfo:%d", (uint32_t)g_cdc_dev); if (g_cdc_dev != NULL) { _usb_get_interface_descriptors(g_cdc_dev, 0, (char *)descriptors, count, actual_desc_count); } } static esp_err_t _usb_get_function_descriptors(cdc_dev_t *cdc_dev, int interface_num, usb_function_desc_packet_t *out_function_desc) { esp_err_t ret; SYS_LOG_INF("GET function 00000"); CDC_ACM_CHECK(cdc_dev->ctrl_transfer, ESP_ERR_NOT_SUPPORTED); // CDC_ACM_CHECK(cdc_dev->ctrl_transfer->data_buffer_size >= data_len, ESP_ERR_INVALID_SIZE); // Take Mutex and fill the CTRL request SYS_LOG_INF("GET function 1111"); BaseType_t taken = xSemaphoreTake(cdc_dev->ctrl_mux, pdMS_TO_TICKS(1000)); if (!taken) { return ESP_ERR_TIMEOUT; } SYS_LOG_INF("GET function 2222"); // usb_setup_packet_t *req = (usb_setup_packet_t *)(cdc_dev->ctrl_transfer->data_buffer); // uint8_t *start_of_data = (uint8_t *)cdc_dev->ctrl_transfer->data_buffer; USB_SETUP_PACKET_INIT_GET_FUNCTION_DESC((usb_setup_packet_t *)cdc_dev->ctrl_transfer->data_buffer); // cdc_dev->ctrl_transfer->num_bytes = 4096; // sizeof(usb_setup_packet_t) + ENUM_CTRL_TRANSFER_MAX_LEN; // req->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; // req->bRequest = request; // req->wValue = value; // req->wIndex = cdc_dev->notif.intf_desc->bInterfaceNumber; // req->wLength = data_len; cdc_dev->ctrl_transfer->num_bytes = 255 + sizeof(usb_setup_packet_t); SYS_LOG_INF("cdc_dev->ctrl_transfer->num_bytes:%d, cdc_dev->ctrl_transfer->data_buffer_size:%d", cdc_dev->ctrl_transfer->num_bytes, cdc_dev->ctrl_transfer->data_buffer_size); ESP_GOTO_ON_ERROR( usb_host_transfer_submit_control(s_msc_driver->client_handle, cdc_dev->ctrl_transfer), unblock, TAG, "get function transfer failed"); SYS_LOG_INF("GET function 33333"); taken = xSemaphoreTake((SemaphoreHandle_t)cdc_dev->ctrl_transfer->context, pdMS_TO_TICKS(1000)); // This is a fixed timeout. Every CDC device should be able to respond to CTRL transfer in 1 second if (!taken) { // Transfer was not finished, error in USB LIB. Reset the endpoint cdc_reset_transfer_endpoint(cdc_dev->dev_hdl, cdc_dev->ctrl_transfer); ret = ESP_ERR_TIMEOUT; SYS_LOG_INF("GET function 5555"); goto unblock; } SYS_LOG_INF("GET function 6666"); ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Control transfer error"); SYS_LOG_INF("GET function 7777"); // ESP_GOTO_ON_FALSE(cdc_dev->ctrl_transfer->actual_num_bytes == cdc_dev->ctrl_transfer->num_bytes, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Incorrect number of bytes transferred"); SYS_LOG_INF("GET function 88888:%d", cdc_dev->ctrl_transfer->actual_num_bytes); const usb_function_desc_packet_t *function_desc = (const usb_function_desc_packet_t *)(cdc_dev->ctrl_transfer->data_buffer + sizeof(usb_setup_packet_t)); memcpy(out_function_desc, function_desc, sizeof(usb_function_desc_packet_t)); SYS_LOG_INF("GET function 9999"); ret = ESP_OK; unblock: xSemaphoreGive(cdc_dev->ctrl_mux); return ret; } uint16_t usbh_stm32_get_transfer_block_size() { if (g_cdc_dev != NULL) { usb_function_desc_packet_t function_desc_packet; esp_err_t ret = _usb_get_function_descriptors(g_cdc_dev, 0, &function_desc_packet); if (ret != ESP_OK) { return 2048; } else { return function_desc_packet.wTransferSize; } } return 0; } esp_err_t _usbh_stm32_control_transfer(uint8_t direction, uint8_t request, uint16_t value, uint16_t interface, uint16_t length, uint8_t *packet_data, uint8_t *out_buffer, uint16_t out_buffer_size, uint16_t *actual_result_size) { if (g_cdc_dev != NULL) { esp_err_t ret = _usb_control_transfer(g_cdc_dev, direction, request, value, interface, length, packet_data, out_buffer, out_buffer_size, actual_result_size); return ret; } return ESP_FAIL; } esp_err_t _usbh_stm32_control_transfer_ex(uint8_t direction, uint8_t request, uint16_t value, uint16_t interface, uint16_t length, uint8_t *packet_data, uint8_t *out_buffer, uint16_t out_buffer_size, uint16_t *actual_result_size, uint16_t timeout) { if (g_cdc_dev != NULL) { return _usb_control_transfer_ex(g_cdc_dev, direction, request, value, interface, length, packet_data, out_buffer, out_buffer_size, actual_result_size, timeout); } return ESP_FAIL; } static esp_err_t _usb_get_status(cdc_dev_t *cdc_dev, uint8_t *out_result_data /*[6]*/) { return _usb_get_status_ex(cdc_dev, out_result_data, 500); ; } static int _get_tick_by_append_ms(unsigned int ms) { return os_get_sys_ticks() + os_calc_msec_to_ticks(ms); } static esp_err_t _usb_clear_status(cdc_dev_t *cdc_dev) { _usb_control_transfer(cdc_dev, USB_BM_REQUEST_TYPE_DIR_OUT, STM32_DFU_REQUEST_CLRSTATUS, 0, 0, 0, 0, NULL, 0, NULL); uint8_t status[6]; memset(status, 0, 6); esp_err_t ret = _usb_get_status(cdc_dev, status); if (ESP_OK != ret) { SYS_LOG_ERR("failed to get status"); return ret; } if (status[4] != STM32_DFU_STATE_DFU_IDLE) { uint32_t delay_ms = status[1] | (status[2] << 8) | (status[3] << 16); uint32_t wait_timeout_ticks = _get_tick_by_append_ms(delay_ms); while (os_get_sys_ticks() < wait_timeout_ticks) { os_thread_sleep(50); } ret = _usb_clear_status(cdc_dev); } return ret; } static esp_err_t _usb_try_read_ob(cdc_dev_t *cdc_dev, uint16_t ob_data_size) { // uint16_t ob_data_size = g_option_bytes.total_size; uint8_t *ob_data = malloc(ob_data_size); memset(ob_data, 0, ob_data_size); uint16_t actual_ob_data_length = 0; esp_err_t ret = _usb_control_transfer(cdc_dev, USB_BM_REQUEST_TYPE_DIR_IN, STM32_DFU_REQUEST_UPLOAD, 2, 0, ob_data_size, NULL, ob_data, ob_data_size, &actual_ob_data_length); if (ret != ESP_OK) { return ret; } SYS_LOG_INF("ob data(%d,%d):", ob_data_size, actual_ob_data_length); ESP_LOG_BUFFER_HEX(TAG, ob_data, ob_data_size); uint8_t status[6]; memset(status, 0, 6); ret = _usb_get_status_ex(cdc_dev, status, 2000); CDC_CHECK(ESP_OK == ret, "get status failed on load address", ret); ESP_LOG_BUFFER_HEX(TAG, status, 6); if (status[4] == STM32_DFU_STATE_UPLOAD_IDLE && actual_ob_data_length == ob_data_size) { ret = _usb_clear_status(cdc_dev); } else { SYS_LOG_ERR("failed to get status on read ob"); ret = ESP_FAIL; } free(ob_data); return ret; } esp_err_t _usbh_stm32_try_read_ob(uint16_t ob_data_size) { if (g_cdc_dev != NULL) { return _usb_try_read_ob(g_cdc_dev, ob_data_size); } return ESP_FAIL; } static esp_err_t _usb_unprotect(cdc_dev_t *cdc_dev) { SYS_LOG_INF("unprotect"); uint8_t unprotect_command = 0x92; esp_err_t ret = _usb_control_transfer(cdc_dev, USB_BM_REQUEST_TYPE_DIR_OUT, STM32_DFU_REQUEST_DNLOAD, 0, 0, 1, &unprotect_command, NULL, 0, NULL); if (ret != ESP_OK) { SYS_LOG_ERR("failed to send unprotect command"); return ret; } uint8_t status[6]; memset(status, 0, 6); ret = _usb_get_status(cdc_dev, status); if (ret != ESP_OK) { SYS_LOG_ERR("failed to get status"); return ret; } if (status[4] == STM32_DFU_STATE_DNBUSY) { uint32_t delay_ms = 20000 + (status[1] | (status[2] << 8) | (status[3] << 16)); // 等待STM32解除读保护,时间为20秒+delay SYS_LOG_INF("flash erase is running, wait the flash:%d ms", delay_ms); uint32_t wait_timeout_ticks = _get_tick_by_append_ms(delay_ms); while (os_get_sys_ticks() < wait_timeout_ticks) { SYS_LOG_INF("waiting flash erase"); os_thread_sleep(1000); } SYS_LOG_INF("reget status"); ret = _usb_get_status(cdc_dev, status); if (ret != ESP_OK) { SYS_LOG_INF("unprotect mcu done"); return ESP_OK; // unprotect done(flash was erased), tell the user replug the FC with DFU mode } else { SYS_LOG_ERR("failed to unprotect"); return ESP_FAIL; } } return ret; } esp_err_t _usbh_stm32_unprotect() { if (g_cdc_dev != NULL) { return _usb_unprotect(g_cdc_dev); } return ESP_FAIL; } esp_err_t _usbh_stm32_leave_dfu() { if (g_cdc_dev == NULL) { return ESP_FAIL; } esp_err_t ret; SYS_LOG_INF("leave dfu 00000"); CDC_ACM_CHECK(g_cdc_dev->ctrl_transfer, ESP_ERR_NOT_SUPPORTED); // CDC_ACM_CHECK(cdc_dev->ctrl_transfer->data_buffer_size >= data_len, ESP_ERR_INVALID_SIZE); // Take Mutex and fill the CTRL request SYS_LOG_INF("leave dfu 1111"); BaseType_t taken = xSemaphoreTake(g_cdc_dev->ctrl_mux, pdMS_TO_TICKS(1000)); if (!taken) { return ESP_ERR_TIMEOUT; } SYS_LOG_INF("leave dfu 2222"); usb_setup_packet_t *setup_packet = (usb_setup_packet_t *)(g_cdc_dev->ctrl_transfer->data_buffer); // uint8_t *start_of_data = (uint8_t *)g_cdc_dev->ctrl_transfer->data_buffer; setup_packet->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; setup_packet->bRequest = STM32_DFU_REQUEST_GETSTATUS; setup_packet->wValue = 0; setup_packet->wIndex = 0; setup_packet->wLength = 6; g_cdc_dev->ctrl_transfer->num_bytes = sizeof(usb_setup_packet_t) + 6; // req->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; // req->bRequest = request; // req->wValue = value; // req->wIndex = cdc_dev->notif.intf_desc->bInterfaceNumber; // req->wLength = data_len; // g_cdc_dev->ctrl_transfer->num_bytes = ENUM_CTRL_TRANSFER_MAX_LEN + sizeof(usb_setup_packet_t); SYS_LOG_INF("g_cdc_dev->ctrl_transfer->num_bytes:%d, g_cdc_dev->ctrl_transfer->data_buffer_size:%d", g_cdc_dev->ctrl_transfer->num_bytes, g_cdc_dev->ctrl_transfer->data_buffer_size); ESP_GOTO_ON_ERROR( usb_host_transfer_submit_control(s_msc_driver->client_handle, g_cdc_dev->ctrl_transfer), unblock, TAG, "leave dfu failed"); SYS_LOG_INF("leave dfu 33333"); taken = xSemaphoreTake((SemaphoreHandle_t)g_cdc_dev->ctrl_transfer->context, pdMS_TO_TICKS(10000)); // This is a fixed timeout. Every CDC device should be able to respond to CTRL transfer in 1 second if (!taken) { // Transfer was not finished, error in USB LIB. Reset the endpoint cdc_reset_transfer_endpoint(g_cdc_dev->dev_hdl, g_cdc_dev->ctrl_transfer); ret = ESP_ERR_TIMEOUT; SYS_LOG_INF("leave dfu 5555"); goto unblock; } SYS_LOG_INF("leave dfu 6666"); if (g_cdc_dev->ctrl_transfer->status != USB_TRANSFER_STATUS_COMPLETED) { SYS_LOG_WRN("FAILED TO GET STATUS FOR LEAVE DFU"); } else { SYS_LOG_INF("leave dfu 88888:%d", g_cdc_dev->ctrl_transfer->actual_num_bytes); } SYS_LOG_INF("leave dfu 9999"); ret = ESP_OK; unblock: xSemaphoreGive(g_cdc_dev->ctrl_mux); return ret; // // _usb_control_transfer_ex(pipe_handle, // // USB_BM_REQUEST_TYPE_DIR_IN, // // STM32_DFU_REQUEST_GETSTATUS, // // 0, // // 0, // // 6, // // 0, // // out_result_data, // // 6, // // NULL, // // timeout); // uint8_t direction = USB_BM_REQUEST_TYPE_DIR_IN; // uint8_t request = STM32_DFU_REQUEST_GETSTATUS; // uint16_t value = 0; // uint16_t interface = 0; // uint16_t length = 6; // uint8_t *packet_data = NULL; // uint8_t *out_buffer = NULL; // uint16_t out_buffer_size = 0; // uint16_t *actual_result_size = NULL; // uint16_t timeout = 500; // CDC_CHECK(g_cdc_dev != NULL, "g_cdc_dev can't be NULL", ESP_ERR_INVALID_ARG); // // malloc URB for default control // uint16_t packet_data_size = ENUM_CTRL_TRANSFER_MAX_LEN; // if (length > packet_data_size) { // packet_data_size = length; // } // urb_t *urb_ctrl = _usb_urb_alloc(0, sizeof(usb_setup_packet_t) + packet_data_size, NULL); // CDC_CHECK(urb_ctrl != NULL, "alloc urb failed", ESP_ERR_NO_MEM); // usb_setup_packet_t *setup_packet = (usb_setup_packet_t *)urb_ctrl->transfer.data_buffer; // setup_packet->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; // setup_packet->bRequest = request; // setup_packet->wValue = value; // setup_packet->wIndex = interface; // setup_packet->wLength = length; // urb_ctrl->transfer.num_bytes = sizeof(usb_setup_packet_t) + length; // // Enqueue it // esp_err_t ret = hcd_urb_enqueue(g_cdc_dev, urb_ctrl); // CDC_CHECK_GOTO(ESP_OK == ret, "urb enqueue failed", free_urb_); // SYS_LOG_INF("urb request timeout:%d ms, and becuase it used for leave dfu, so dont wait response", timeout); // goto free_urb_; // free_urb_: // _usb_pipe_flush(g_cdc_dev, 1); // _usb_urb_free(urb_ctrl); // return ret; } esp_err_t usbh_stm32_clear_status() { // return _usbh_ if (g_cdc_dev != NULL) { return _usb_clear_status(g_cdc_dev); } return ESP_FAIL; } static esp_err_t _usb_load_address(cdc_dev_t *cdc_dev, uint32_t address) { // CDC_CHECK(pipe_handle != NULL, "pipe_handle can't be NULL", ESP_ERR_INVALID_ARG); // // malloc URB for default control // urb_t *urb_ctrl = _usb_urb_alloc(0, sizeof(usb_setup_packet_t) + ENUM_CTRL_TRANSFER_MAX_LEN, NULL); // CDC_CHECK(urb_ctrl != NULL, "alloc urb failed", ESP_ERR_NO_MEM); uint8_t packet_data[5]; packet_data[0] = 0x21; packet_data[1] = address & 0xff; packet_data[2] = (address >> 8) & 0xff; packet_data[3] = (address >> 16) & 0xff; packet_data[4] = (address >> 24) & 0xff; // USB_SETUP_PACKET_INIT_LOAD_ADDRESS((usb_setup_packet_t *)urb_ctrl->transfer.data_buffer, packet_data); // urb_ctrl->transfer.num_bytes = sizeof(usb_setup_packet_t) + 5; // SYS_LOG_INF("load address urb data, original address:%d", address); // ESP_LOG_BUFFER_HEX(TAG, urb_ctrl->transfer.data_buffer, sizeof(usb_setup_packet_t) + 5); // // Enqueue it // esp_err_t ret = hcd_urb_enqueue(pipe_handle, urb_ctrl); // CDC_CHECK_GOTO(ESP_OK == ret, "urb enqueue failed", free_urb_); // ret = _default_pipe_event_wait_until(pipe_handle, HCD_PIPE_EVENT_URB_DONE, pdMS_TO_TICKS(500)); // CDC_CHECK_GOTO(ESP_OK == ret, "urb event error", flush_urb_); // urb_t *urb_done = hcd_urb_dequeue(pipe_handle); // CDC_CHECK_GOTO(urb_done == urb_ctrl, "urb status: not same", free_urb_); // CDC_CHECK_GOTO(USB_TRANSFER_STATUS_COMPLETED == urb_done->transfer.status, // "urb status: not complete", // free_urb_); // CDC_CHECK_GOTO(urb_ctrl->transfer.actual_num_bytes >= sizeof(usb_setup_packet_t), // "clear status descriptor too short", // free_urb_); esp_err_t ret = _usbh_stm32_control_transfer(USB_BM_REQUEST_TYPE_DIR_OUT, STM32_DFU_REQUEST_DNLOAD, 0, 0, 5, packet_data, NULL, 0, NULL); CDC_CHECK(ESP_OK == ret, "failed to load address", ret); uint8_t status[6]; memset(status, 0, 6); ret = _usb_get_status(cdc_dev, status); CDC_CHECK(ESP_OK == ret, "get status failed on load address", ret); if (status[4] == STM32_DFU_STATE_DNBUSY) { uint32_t delay_ms = status[1] | (status[2] << 8) | (status[3] << 16); uint32_t wait_timeout_ticks = _get_tick_by_append_ms(delay_ms); while (os_get_sys_ticks() < wait_timeout_ticks) { os_thread_sleep(20); } ret = _usb_get_status(cdc_dev, status); CDC_CHECK(ESP_OK == ret, "get status failed on load address", ret); if (status[4] != STM32_DFU_STATE_DNLOAD_IDLE) { SYS_LOG_ERR("unexpeceted dfu status:%d", status[4]); return ESP_FAIL; } } else { SYS_LOG_INF("result of get status"); return ESP_FAIL; } return ret; } esp_err_t _usbh_stm32_load_address(uint32_t address) { if (g_cdc_dev != NULL) { return _usb_load_address(g_cdc_dev, address); } return ESP_FAIL; }