diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 00000000..8f80c12c --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,5 @@ +description: CherryUSB is a tiny and portable USB Stack (device & host) for embedded system with USB IP +url: https://github.com/cherry-embedded/CherryUSB +repository: https://github.com/cherry-embedded/CherryUSB.git +documentation: https://cherryusb.readthedocs.io/ +issues: https://github.com/cherry-embedded/CherryUSB/issues \ No newline at end of file diff --git a/osal/idf/linker.lf b/osal/idf/linker.lf new file mode 100644 index 00000000..24c8b27a --- /dev/null +++ b/osal/idf/linker.lf @@ -0,0 +1,13 @@ +[sections:usbh_class_info] +entries: + .usbh_class_info+ + +[scheme:usbh_class_info_array] +entries: + usbh_class_info -> flash_rodata + +[mapping:usbh_class_info_table] +archive: * +entries: + * (usbh_class_info_array); + usbh_class_info -> flash_rodata KEEP() SURROUND(usbh_class_info) \ No newline at end of file diff --git a/osal/idf/usb_config.h b/osal/idf/usb_config.h new file mode 100644 index 00000000..e813c920 --- /dev/null +++ b/osal/idf/usb_config.h @@ -0,0 +1,271 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define CHERRYUSB_VERSION 0x010301 +#define CHERRYUSB_VERSION_STR "v1.3.1" + +#include "sdkconfig.h" +#include "esp_rom_sys.h" + +/* ================ USB common Configuration ================ */ + +#define CONFIG_USB_PRINTF(...) esp_rom_printf(__VA_ARGS__) + +#define usb_malloc(size) malloc(size) +#define usb_free(ptr) free(ptr) + +#ifndef CONFIG_USB_DBG_LEVEL +#define CONFIG_USB_DBG_LEVEL USB_DBG_INFO +#endif + +/* Enable print with color */ +#define CONFIG_USB_PRINTF_COLOR_ENABLE + +/* data align size when use dma */ +#ifndef CONFIG_USB_ALIGN_SIZE +#define CONFIG_USB_ALIGN_SIZE 4 +#endif + +/* attribute data into no cache ram */ +#define USB_NOCACHE_RAM_SECTION + +/* ================= USB Device Stack Configuration ================ */ +// NOTE: Below configurations are removed to Kconfig, `idf.py menuconfig` to config them + +/* Ep0 in and out transfer buffer */ +#ifndef CONFIG_USBDEV_REQUEST_BUFFER_LEN +#define CONFIG_USBDEV_REQUEST_BUFFER_LEN 512 +#endif + +/* Setup packet log for debug */ +// #define CONFIG_USBDEV_SETUP_LOG_PRINT + +/* Send ep0 in data from user buffer instead of copying into ep0 reqdata + * Please note that user buffer must be aligned with CONFIG_USB_ALIGN_SIZE +*/ +// #define CONFIG_USBDEV_EP0_INDATA_NO_COPY + +/* Check if the input descriptor is correct */ +// #define CONFIG_USBDEV_DESC_CHECK + +/* Enable test mode */ +// #define CONFIG_USBDEV_TEST_MODE + +#ifndef CONFIG_USBDEV_MSC_MAX_LUN +#define CONFIG_USBDEV_MSC_MAX_LUN 1 +#endif + +#ifndef CONFIG_USBDEV_MSC_MAX_BUFSIZE +#define CONFIG_USBDEV_MSC_MAX_BUFSIZE 512 +#endif + +#ifndef CONFIG_USBDEV_MSC_MANUFACTURER_STRING +#define CONFIG_USBDEV_MSC_MANUFACTURER_STRING "" +#endif + +#ifndef CONFIG_USBDEV_MSC_PRODUCT_STRING +#define CONFIG_USBDEV_MSC_PRODUCT_STRING "" +#endif + +#ifndef CONFIG_USBDEV_MSC_VERSION_STRING +#define CONFIG_USBDEV_MSC_VERSION_STRING "0.01" +#endif + +// #define CONFIG_USBDEV_MSC_THREAD + +#ifndef CONFIG_USBDEV_MSC_PRIO +#define CONFIG_USBDEV_MSC_PRIO 4 +#endif + +#ifndef CONFIG_USBDEV_MSC_STACKSIZE +#define CONFIG_USBDEV_MSC_STACKSIZE 2048 +#endif + +#ifndef CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE +#define CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE 156 +#endif + +/* rndis transfer buffer size, must be a multiple of (1536 + 44)*/ +#ifndef CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE +#define CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE 1580 +#endif + +#ifndef CONFIG_USBDEV_RNDIS_VENDOR_ID +#define CONFIG_USBDEV_RNDIS_VENDOR_ID 0x0000ffff +#endif + +#ifndef CONFIG_USBDEV_RNDIS_VENDOR_DESC +#define CONFIG_USBDEV_RNDIS_VENDOR_DESC "CherryUSB" +#endif + +/****** +#define CONFIG_USBDEV_RNDIS_USING_LWIP +******/ + +/* ================ USB HOST Stack Configuration ================== */ +// NOTE: Below configurations are removed to Kconfig, `idf.py menuconfig` to config them + +/****** +#define CONFIG_USBHOST_MAX_RHPORTS 1 +#define CONFIG_USBHOST_MAX_EXTHUBS 1 +#define CONFIG_USBHOST_MAX_EHPORTS 4 +#define CONFIG_USBHOST_MAX_INTERFACES 8 +#define CONFIG_USBHOST_MAX_INTF_ALTSETTINGS 8 +#define CONFIG_USBHOST_MAX_ENDPOINTS 4 + +#define CONFIG_USBHOST_MAX_CDC_ACM_CLASS 4 +#define CONFIG_USBHOST_MAX_HID_CLASS 4 +#define CONFIG_USBHOST_MAX_MSC_CLASS 2 +#define CONFIG_USBHOST_MAX_AUDIO_CLASS 1 +#define CONFIG_USBHOST_MAX_VIDEO_CLASS 1 + +#define CONFIG_USBHOST_DEV_NAMELEN 16 +******/ + +#ifndef CONFIG_USBHOST_PSC_PRIO +#define CONFIG_USBHOST_PSC_PRIO 0 +#endif +#ifndef CONFIG_USBHOST_PSC_STACKSIZE +#define CONFIG_USBHOST_PSC_STACKSIZE 2048 +#endif + +// #define CONFIG_USBHOST_GET_STRING_DESC + +// #define CONFIG_USBHOST_MSOS_ENABLE +#ifndef CONFIG_USBHOST_MSOS_VENDOR_CODE +#define CONFIG_USBHOST_MSOS_VENDOR_CODE 0x00 +#endif + +/* Ep0 max transfer buffer */ +#ifndef CONFIG_USBHOST_REQUEST_BUFFER_LEN +#define CONFIG_USBHOST_REQUEST_BUFFER_LEN 512 +#endif + +#ifndef CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT +#define CONFIG_USBHOST_CONTROL_TRANSFER_TIMEOUT 500 +#endif + +#ifndef CONFIG_USBHOST_MSC_TIMEOUT +#define CONFIG_USBHOST_MSC_TIMEOUT 5000 +#endif + +/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size, + * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow. + */ +#ifndef CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE +#define CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE (2048) +#endif + +/* Because lwip do not support multi pbuf at a time, so increasing this variable has no performance improvement */ +#ifndef CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE +#define CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE (2048) +#endif + +/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size, + * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow. + */ +#ifndef CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE +#define CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE (2048) +#endif +/* Because lwip do not support multi pbuf at a time, so increasing this variable has no performance improvement */ +#ifndef CONFIG_USBHOST_CDC_NCM_ETH_MAX_TX_SIZE +#define CONFIG_USBHOST_CDC_NCM_ETH_MAX_TX_SIZE (2048) +#endif + +/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size, + * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow. + */ +#ifndef CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE +#define CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE (2048) +#endif +/* Because lwip do not support multi pbuf at a time, so increasing this variable has no performance improvement */ +#ifndef CONFIG_USBHOST_ASIX_ETH_MAX_TX_SIZE +#define CONFIG_USBHOST_ASIX_ETH_MAX_TX_SIZE (2048) +#endif + +/* This parameter affects usb performance, and depends on (TCP_WND)tcp eceive windows size, + * you can change to 2K ~ 16K and must be larger than TCP RX windows size in order to avoid being overflow. + */ +#ifndef CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE +#define CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE (2048) +#endif +/* Because lwip do not support multi pbuf at a time, so increasing this variable has no performance improvement */ +#ifndef CONFIG_USBHOST_RTL8152_ETH_MAX_TX_SIZE +#define CONFIG_USBHOST_RTL8152_ETH_MAX_TX_SIZE (2048) +#endif + +#define CONFIG_USBHOST_BLUETOOTH_HCI_H4 +// #define CONFIG_USBHOST_BLUETOOTH_HCI_LOG + +#ifndef CONFIG_USBHOST_BLUETOOTH_TX_SIZE +#define CONFIG_USBHOST_BLUETOOTH_TX_SIZE 2048 +#endif +#ifndef CONFIG_USBHOST_BLUETOOTH_RX_SIZE +#define CONFIG_USBHOST_BLUETOOTH_RX_SIZE 2048 +#endif + +/* ================ USB Device Port Configuration ================*/ + +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#define ESP_USBD_BASE 0x60080000 + +#define CONFIG_USBDEV_MAX_BUS 1 +// esp32s2/s3 has 7 endpoints in device mode (include ep0) +#define CONFIG_USBDEV_EP_NUM 7 + +/* ---------------- DWC2 Configuration ---------------- */ +//esp32s2/s3 can support up to 5 IN endpoints(include ep0) at the same time +#define CONFIG_USB_DWC2_RXALL_FIFO_SIZE (208 / 4) +#define CONFIG_USB_DWC2_TX0_FIFO_SIZE (64 / 4) +#define CONFIG_USB_DWC2_TX1_FIFO_SIZE (136 / 4) +#define CONFIG_USB_DWC2_TX2_FIFO_SIZE (136 / 4) +#define CONFIG_USB_DWC2_TX3_FIFO_SIZE (128 / 4) +#define CONFIG_USB_DWC2_TX4_FIFO_SIZE (128 / 4) +#define CONFIG_USB_DWC2_TX5_FIFO_SIZE (0 / 4) +#define CONFIG_USB_DWC2_TX6_FIFO_SIZE (0 / 4) +#define CONFIG_USB_DWC2_TX7_FIFO_SIZE (0 / 4) +#define CONFIG_USB_DWC2_TX8_FIFO_SIZE (0 / 4) + +#define CONFIG_USB_DWC2_DMA_ENABLE + +#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32P4 +#define CONFIG_USB_HS +#define ESP_USBD_BASE 0x60080000 +// todo: check c5, p4 in later +#define CONFIG_USBDEV_EP_NUM 7 +#else +#error "Unsupported SoC" +#endif + +/* ================ USB Host Port Configuration ==================*/ + +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#define ESP_USBH_BASE 0x60080000 + +#define CONFIG_USBHOST_MAX_BUS 1 +// esp32s2/s3 has 8 endpoints in host mode (include ep0) +#define CONFIG_USBHOST_PIPE_NUM 8 + +/* ---------------- DWC2 Configuration ---------------- */ +/* largest non-periodic USB packet used / 4 */ +#define CONFIG_USB_DWC2_NPTX_FIFO_SIZE (240 / 4) +/* largest periodic USB packet used / 4 */ +#define CONFIG_USB_DWC2_PTX_FIFO_SIZE (240 / 4) +/* + * (largest USB packet used / 4) + 1 for status information + 1 transfer complete + + * 1 location each for Bulk/Control endpoint for handling NAK/NYET scenario + */ +#define CONFIG_USB_DWC2_RX_FIFO_SIZE ((200 - CONFIG_USB_DWC2_NPTX_FIFO_SIZE - CONFIG_USB_DWC2_PTX_FIFO_SIZE) / 4) + +#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32P4 +// todo: check c5, p4 in later +#define ESP_USBH_BASE 0x60080000 +#define CONFIG_USBHOST_PIPE_NUM 8 +#else +#error "Unsupported SoC" +#endif \ No newline at end of file diff --git a/osal/idf/usb_osal_idf.c b/osal/idf/usb_osal_idf.c new file mode 100644 index 00000000..f85aba2e --- /dev/null +++ b/osal/idf/usb_osal_idf.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2022, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usb_osal.h" +#include "usb_errno.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/timers.h" +#include "freertos/event_groups.h" + +static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; + +usb_osal_thread_t usb_osal_thread_create(const char *name, uint32_t stack_size, uint32_t prio, usb_thread_entry_t entry, void *args) +{ + TaskHandle_t htask = NULL; + stack_size /= sizeof(StackType_t); + xTaskCreate(entry, name, stack_size, args, configMAX_PRIORITIES - 1 - prio, &htask); + return (usb_osal_thread_t)htask; +} + +void usb_osal_thread_delete(usb_osal_thread_t thread) +{ + vTaskDelete(thread); +} + +usb_osal_sem_t usb_osal_sem_create(uint32_t initial_count) +{ + return (usb_osal_sem_t)xSemaphoreCreateCounting(1, initial_count); +} + +void usb_osal_sem_delete(usb_osal_sem_t sem) +{ + vSemaphoreDelete((SemaphoreHandle_t)sem); +} + +int usb_osal_sem_take(usb_osal_sem_t sem, uint32_t timeout) +{ + if (timeout == USB_OSAL_WAITING_FOREVER) { + return (xSemaphoreTake((SemaphoreHandle_t)sem, portMAX_DELAY) == pdPASS) ? 0 : -USB_ERR_TIMEOUT; + } else { + return (xSemaphoreTake((SemaphoreHandle_t)sem, pdMS_TO_TICKS(timeout)) == pdPASS) ? 0 : -USB_ERR_TIMEOUT; + } +} + +int usb_osal_sem_give(usb_osal_sem_t sem) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + int ret; + + if (xPortInIsrContext()) { + ret = xSemaphoreGiveFromISR((SemaphoreHandle_t)sem, &xHigherPriorityTaskWoken); + if (ret == pdPASS) { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } + } else { + ret = xSemaphoreGive((SemaphoreHandle_t)sem); + } + + return (ret == pdPASS) ? 0 : -USB_ERR_TIMEOUT; +} + +void usb_osal_sem_reset(usb_osal_sem_t sem) +{ + xQueueReset((QueueHandle_t)sem); +} + +usb_osal_mutex_t usb_osal_mutex_create(void) +{ + return (usb_osal_mutex_t)xSemaphoreCreateMutex(); +} + +void usb_osal_mutex_delete(usb_osal_mutex_t mutex) +{ + vSemaphoreDelete((SemaphoreHandle_t)mutex); +} + +int usb_osal_mutex_take(usb_osal_mutex_t mutex) +{ + return (xSemaphoreTake((SemaphoreHandle_t)mutex, portMAX_DELAY) == pdPASS) ? 0 : -USB_ERR_TIMEOUT; +} + +int usb_osal_mutex_give(usb_osal_mutex_t mutex) +{ + return (xSemaphoreGive((SemaphoreHandle_t)mutex) == pdPASS) ? 0 : -USB_ERR_TIMEOUT; +} + +usb_osal_mq_t usb_osal_mq_create(uint32_t max_msgs) +{ + return (usb_osal_mq_t)xQueueCreate(max_msgs, sizeof(uintptr_t)); +} + +void usb_osal_mq_delete(usb_osal_mq_t mq) +{ + vQueueDelete((QueueHandle_t)mq); +} + +int usb_osal_mq_send(usb_osal_mq_t mq, uintptr_t addr) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + int ret; + + ret = xQueueSendFromISR((usb_osal_mq_t)mq, &addr, &xHigherPriorityTaskWoken); + if (ret == pdPASS) { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } + + return (ret == pdPASS) ? 0 : -USB_ERR_TIMEOUT; +} + +int usb_osal_mq_recv(usb_osal_mq_t mq, uintptr_t *addr, uint32_t timeout) +{ + if (timeout == USB_OSAL_WAITING_FOREVER) { + return (xQueueReceive((usb_osal_mq_t)mq, addr, portMAX_DELAY) == pdPASS) ? 0 : -USB_ERR_TIMEOUT; + } else { + return (xQueueReceive((usb_osal_mq_t)mq, addr, pdMS_TO_TICKS(timeout)) == pdPASS) ? 0 : -USB_ERR_TIMEOUT; + } +} + +static void __usb_timeout(TimerHandle_t *handle) +{ + struct usb_osal_timer *timer = (struct usb_osal_timer *)pvTimerGetTimerID((TimerHandle_t)handle); + + timer->handler(timer->argument); +} + +struct usb_osal_timer *usb_osal_timer_create(const char *name, uint32_t timeout_ms, usb_timer_handler_t handler, void *argument, bool is_period) +{ + struct usb_osal_timer *timer; + + timer = pvPortMalloc(sizeof(struct usb_osal_timer)); + + if (timer == NULL) { + return NULL; + } + memset(timer, 0, sizeof(struct usb_osal_timer)); + + timer->handler = handler; + timer->argument = argument; + + timer->timer = (void *)xTimerCreate("usb_tim", pdMS_TO_TICKS(timeout_ms), is_period, timer, (TimerCallbackFunction_t)__usb_timeout); + if (timer->timer == NULL) { + return NULL; + } + return timer; +} + +void usb_osal_timer_delete(struct usb_osal_timer *timer) +{ + xTimerStop(timer->timer, 0); + xTimerDelete(timer->timer, 0); + vPortFree(timer); +} + +void usb_osal_timer_start(struct usb_osal_timer *timer) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + int ret; + + if (xPortInIsrContext()) { + ret = xTimerStartFromISR(timer->timer, &xHigherPriorityTaskWoken); + if (ret == pdPASS) { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } + } else { + xTimerStart(timer->timer, 0); + } +} + +void usb_osal_timer_stop(struct usb_osal_timer *timer) +{ + xTimerStop(timer->timer, 0); +} + +size_t usb_osal_enter_critical_section(void) +{ + portENTER_CRITICAL_SAFE(&spinlock); + return 0; +} + +void usb_osal_leave_critical_section(size_t flag) +{ + portEXIT_CRITICAL_SAFE(&spinlock); +} + +void usb_osal_msleep(uint32_t delay) +{ + vTaskDelay(pdMS_TO_TICKS(delay)); +} + +void *usb_osal_malloc(size_t size) +{ + return malloc(size); +} + +void usb_osal_free(void *ptr) +{ + free(ptr); +} \ No newline at end of file diff --git a/osal/idf/usbh_class_info.ld b/osal/idf/usbh_class_info.ld new file mode 100644 index 00000000..117bfd8d --- /dev/null +++ b/osal/idf/usbh_class_info.ld @@ -0,0 +1,2 @@ +__usbh_class_info_start__ = _usbh_class_info_start; +__usbh_class_info_end__ = _usbh_class_info_end; \ No newline at end of file