diff --git a/.github/workflows/build_arm.yml b/.github/workflows/build_arm.yml
index 47dea1925..4127db5bf 100644
--- a/.github/workflows/build_arm.yml
+++ b/.github/workflows/build_arm.yml
@@ -37,7 +37,7 @@ jobs:
# Alphabetical order
- 'broadcom_32bit'
- 'kinetis_k32l2'
- - 'lpc11 lpc13 lpc15 lpc17'
+ - 'lpc11 lpc13 lpc15'
- 'lpc51'
- 'mm32 msp432e4'
- 'samd11 same5x saml2x'
diff --git a/.github/workflows/cmake_arm.yml b/.github/workflows/cmake_arm.yml
index 13120f96f..8b87fef21 100644
--- a/.github/workflows/cmake_arm.yml
+++ b/.github/workflows/cmake_arm.yml
@@ -37,7 +37,7 @@ jobs:
# Alphabetical order
- 'imxrt'
- 'kinetis_kl'
- - 'lpc18 lpc40 lpc43'
+ - 'lpc17 lpc18 lpc40 lpc43'
- 'lpc54 lpc55'
- 'mcx'
- 'nrf'
diff --git a/.idea/cmake.xml b/.idea/cmake.xml
index d3917c1f7..432e44172 100644
--- a/.idea/cmake.xml
+++ b/.idea/cmake.xml
@@ -80,6 +80,7 @@
+
\ No newline at end of file
diff --git a/examples/device/video_capture/src/images.h b/examples/device/video_capture/src/images.h
index 0398428b3..784e7754c 100644
--- a/examples/device/video_capture/src/images.h
+++ b/examples/device/video_capture/src/images.h
@@ -1,4 +1,4 @@
-#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPG)
+#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
static const unsigned char frame_buffer[128 * (96 + 1) * 2] = {
/* 0 */
0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80, 0xeb, 0x80,
diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c
index c653761c1..711d4710a 100644
--- a/examples/device/video_capture/src/main.c
+++ b/examples/device/video_capture/src/main.c
@@ -115,7 +115,7 @@ static unsigned interval_ms = 1000 / FRAME_RATE;
#ifdef CFG_EXAMPLE_VIDEO_READONLY
#include "images.h"
-# if !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPG)
+# if !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
static struct {
uint32_t size;
uint8_t const *buffer;
@@ -187,7 +187,7 @@ void video_task(void)
already_sent = 1;
start_ms = board_millis();
#ifdef CFG_EXAMPLE_VIDEO_READONLY
-# if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPG)
+# if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
# else
@@ -205,7 +205,7 @@ void video_task(void)
start_ms += interval_ms;
#ifdef CFG_EXAMPLE_VIDEO_READONLY
-# if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPG)
+# if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
tud_video_n_frame_xfer(0, 0, (void*)(uintptr_t)&frame_buffer[(frame_num % (FRAME_WIDTH / 2)) * 4],
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
# else
diff --git a/examples/device/video_capture/src/usb_descriptors.c b/examples/device/video_capture/src/usb_descriptors.c
index 292d86cd9..49919fc58 100644
--- a/examples/device/video_capture/src/usb_descriptors.c
+++ b/examples/device/video_capture/src/usb_descriptors.c
@@ -37,6 +37,9 @@
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VIDEO, 5) | _PID_MAP(VENDOR, 6) )
+#define USB_VID 0xCafe
+#define USB_BCD 0x0200
+
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
@@ -44,7 +47,7 @@ tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
- .bcdUSB = 0x0200,
+ .bcdUSB = USB_BCD,
// Use Interface Association Descriptor (IAD) for Video
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
@@ -54,7 +57,7 @@ tusb_desc_device_t const desc_device =
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
- .idVendor = 0xCafe,
+ .idVendor = USB_VID,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
@@ -137,6 +140,76 @@ uint8_t const desc_fs_configuration[] =
#endif
};
+#if TUD_OPT_HIGH_SPEED
+// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
+
+uint8_t const desc_hs_configuration[] =
+{
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 500),
+
+ // IAD for Video Control
+#if defined(CFG_EXAMPLE_VIDEO_READONLY) && !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
+# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
+ TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(4, EPNUM_VIDEO_IN,
+ FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
+ 512)
+# else
+ TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(4, EPNUM_VIDEO_IN,
+ FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
+ CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
+# endif
+#else
+# if 1 == CFG_TUD_VIDEO_STREAMING_BULK
+ TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK(4, EPNUM_VIDEO_IN,
+ FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
+ 512)
+# else
+ TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(4, EPNUM_VIDEO_IN,
+ FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE,
+ CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE)
+# endif
+#endif
+};
+
+// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
+tusb_desc_device_qualifier_t const desc_device_qualifier =
+{
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = USB_BCD,
+
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00
+};
+
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
+// device_qualifier descriptor describes information about a high-speed capable device that would
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
+uint8_t const* tud_descriptor_device_qualifier_cb(void)
+{
+ return (uint8_t const*) &desc_device_qualifier;
+}
+
+// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
+{
+ (void) index; // for multiple configurations
+
+ // if link speed is high return fullspeed config, and vice versa
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration;
+}
+
+#endif // highspeed
+
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
@@ -144,7 +217,12 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
+#if TUD_OPT_HIGH_SPEED
+ // Although we are highspeed, host may be fullspeed.
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
+#else
return desc_fs_configuration;
+#endif
}
//--------------------------------------------------------------------+
diff --git a/hw/bsp/lpc17/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/lpc17/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..9b66fb110
--- /dev/null
+++ b/hw/bsp/lpc17/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,149 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+ #include "chip.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU 0
+#define configENABLE_FPU 0
+#define configENABLE_TRUSTZONE 0
+#define configMINIMAL_SECURE_STACK_SIZE (1024)
+
+#define configUSE_PREEMPTION 1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ SystemCoreClock
+#define configTICK_RATE_HZ ( 1000 )
+#define configMAX_PRIORITIES ( 5 )
+#define configMINIMAL_STACK_SIZE ( 128 )
+#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN 16
+#define configUSE_16_BIT_TICKS 0
+#define configIDLE_SHOULD_YIELD 1
+#define configUSE_MUTEXES 1
+#define configUSE_RECURSIVE_MUTEXES 1
+#define configUSE_COUNTING_SEMAPHORES 1
+#define configQUEUE_REGISTRY_SIZE 4
+#define configUSE_QUEUE_SETS 0
+#define configUSE_TIME_SLICING 0
+#define configUSE_NEWLIB_REENTRANT 0
+#define configENABLE_BACKWARD_COMPATIBILITY 1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0
+
+#define configSUPPORT_STATIC_ALLOCATION 1
+#define configSUPPORT_DYNAMIC_ALLOCATION 0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK 0
+#define configUSE_TICK_HOOK 0
+#define configUSE_MALLOC_FAILED_HOOK 0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW 2
+#define configCHECK_HANDLER_INSTALLATION 0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS 0
+#define configRECORD_STACK_HIGH_ADDRESS 1
+#define configUSE_TRACE_FACILITY 1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS 0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES 0
+#define configMAX_CO_ROUTINE_PRIORITIES 2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS 1
+#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH 32
+#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet 0
+#define INCLUDE_uxTaskPriorityGet 0
+#define INCLUDE_vTaskDelete 0
+#define INCLUDE_vTaskSuspend 1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR 0
+#define INCLUDE_vTaskDelayUntil 1
+#define INCLUDE_vTaskDelay 1
+#define INCLUDE_xTaskGetSchedulerState 0
+#define INCLUDE_xTaskGetCurrentTaskHandle 1
+#define INCLUDE_uxTaskGetStackHighWaterMark 0
+#define INCLUDE_xTaskGetIdleTaskHandle 0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName 0
+#define INCLUDE_eTaskGetState 0
+#define INCLUDE_xEventGroupSetBitFromISR 0
+#define INCLUDE_xTimerPendFunctionCall 0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler PendSV_Handler
+#define xPortSysTickHandler SysTick_Handler
+#define vPortSVCHandler SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS 3
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1<OTGClkCtrl = clk_en;
- while ( (LPC_USB->OTGClkSt & clk_en) != clk_en );
-
-#if CFG_TUH_ENABLED
- // set portfunc to host !!!
- LPC_USB->StCtrl = 0x3; // should be 1
-#endif
-}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
- Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
-}
-
-uint32_t board_button_read(void)
-{
- return BUTTON_STATE_ACTIVE == Chip_GPIO_GetPinState(LPC_GPIO, BUTTON_PORT, BUTTON_PIN);
-}
-
-int board_uart_read(uint8_t* buf, int len)
-{
-// return UART_ReceiveByte(BOARD_UART_PORT);
- (void) buf; (void) len;
- return 0;
-}
-
-int board_uart_write(void const * buf, int len)
-{
-// UART_Send(BOARD_UART_PORT, &c, 1, BLOCKING);
- (void) buf; (void) len;
- return 0;
-}
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-void SysTick_Handler (void)
-{
- system_ticks++;
-}
-
-uint32_t board_millis(void)
-{
- return system_ticks;
-}
-#endif
diff --git a/hw/bsp/lpc17/boards/mbed1768/board.cmake b/hw/bsp/lpc17/boards/mbed1768/board.cmake
new file mode 100644
index 000000000..688f34292
--- /dev/null
+++ b/hw/bsp/lpc17/boards/mbed1768/board.cmake
@@ -0,0 +1,11 @@
+set(MCU_VARIANT LPC1768)
+
+set(JLINK_DEVICE LPC1768)
+set(PYOCD_TARGET LPC1768)
+set(NXPLINK_DEVICE LPC1768:LPC1768)
+
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/lpc1768.ld)
+
+function(update_board TARGET)
+ # nothing to do
+endfunction()
diff --git a/hw/bsp/lpc17/boards/mbed1768/board.h b/hw/bsp/lpc17/boards/mbed1768/board.h
new file mode 100644
index 000000000..2b3ddc905
--- /dev/null
+++ b/hw/bsp/lpc17/boards/mbed1768/board.h
@@ -0,0 +1,71 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#define LED_PORT 1
+#define LED_PIN 18
+#define LED_STATE_ON 1
+
+// JOYSTICK_DOWN if using LPCXpresso Base Board
+#define BUTTON_PORT 0
+#define BUTTON_PIN 15
+#define BUTTON_STATE_ACTIVE 0
+
+#define BOARD_UART_PORT LPC_UART3
+
+/* System oscillator rate and RTC oscillator rate */
+const uint32_t OscRateIn = 10000000;
+const uint32_t RTCOscRateIn = 32768;
+
+// Pin muxing configuration
+static const PINMUX_GRP_T pinmuxing[] = {
+ {LED_PORT, LED_PIN, IOCON_MODE_INACT | IOCON_FUNC0},
+ {BUTTON_PORT, BUTTON_PIN, IOCON_FUNC0 | IOCON_MODE_PULLUP},
+};
+
+static const PINMUX_GRP_T pin_usb_mux[] = {
+ {0, 29, IOCON_MODE_INACT | IOCON_FUNC1}, // D+
+ {0, 30, IOCON_MODE_INACT | IOCON_FUNC1}, // D-
+ {2, 9, IOCON_MODE_INACT | IOCON_FUNC1}, // Soft Connect
+
+ {1, 19, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PPWR (Host mode)
+ {1, 22, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PWRD
+
+ // VBUS is not connected on this board, so leave the pin at default setting.
+ // Chip_IOCON_PinMux(LPC_IOCON, 1, 30, IOCON_MODE_INACT, IOCON_FUNC2); // USB VBUS
+};
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/hw/bsp/lpc17/boards/mbed1768/mbed1768.c b/hw/bsp/lpc17/family.c
similarity index 61%
rename from hw/bsp/lpc17/boards/mbed1768/mbed1768.c
rename to hw/bsp/lpc17/family.c
index 613dcb570..79281ba41 100644
--- a/hw/bsp/lpc17/boards/mbed1768/mbed1768.c
+++ b/hw/bsp/lpc17/family.c
@@ -26,58 +26,24 @@
#include "chip.h"
#include "bsp/board_api.h"
-
-#define LED_PORT 1
-#define LED_PIN 18
-#define LED_STATE_ON 1
-
-// JOYSTICK_DOWN if using LPCXpresso Base Board
-#define BUTTON_PORT 0
-#define BUTTON_PIN 15
-#define BUTTON_STATE_ACTIVE 0
-
-#define BOARD_UART_PORT LPC_UART3
-
-/* System oscillator rate and RTC oscillator rate */
-const uint32_t OscRateIn = 10000000;
-const uint32_t RTCOscRateIn = 32768;
-
-/* Pin muxing configuration */
-static const PINMUX_GRP_T pinmuxing[] =
-{
- {LED_PORT, LED_PIN, IOCON_MODE_INACT | IOCON_FUNC0},
- {BUTTON_PORT, BUTTON_PIN, IOCON_FUNC0 | IOCON_MODE_PULLUP},
-};
-
-static const PINMUX_GRP_T pin_usb_mux[] =
-{
- {0, 29, IOCON_MODE_INACT | IOCON_FUNC1}, // D+
- {0, 30, IOCON_MODE_INACT | IOCON_FUNC1}, // D-
- {2, 9, IOCON_MODE_INACT | IOCON_FUNC1}, // Connect
-
- {1, 19, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PPWR
- {1, 22, IOCON_MODE_INACT | IOCON_FUNC2}, // USB_PWRD
-
- /* VBUS is not connected on this board, so leave the pin at default setting. */
- /*Chip_IOCON_PinMux(LPC_IOCON, 1, 30, IOCON_MODE_INACT, IOCON_FUNC2);*/ /* USB VBUS */
-};
+#include "board.h"
// Invoked by startup code
-void SystemInit(void)
-{
+void SystemInit(void) {
#ifdef __USE_LPCOPEN
- extern void (* const g_pfnVectors[])(void);
- unsigned int *pSCB_VTOR = (unsigned int *) 0xE000ED08;
- *pSCB_VTOR = (unsigned int) g_pfnVectors;
+ extern void (* const g_pfnVectors[])(void);
+ unsigned int* pSCB_VTOR = (unsigned int*) 0xE000ED08;
+ *pSCB_VTOR = (unsigned int) g_pfnVectors;
#endif
Chip_IOCON_Init(LPC_IOCON);
Chip_IOCON_SetPinMuxing(LPC_IOCON, pinmuxing, sizeof(pinmuxing) / sizeof(PINMUX_GRP_T));
Chip_SetupXtalClocking();
+
+ Chip_SYSCTL_SetFLASHAccess(FLASHTIM_100MHZ_CPU);
}
-void board_init(void)
-{
+void board_init(void) {
SystemCoreClockUpdate();
#if CFG_TUSB_OS == OPT_OS_NONE
@@ -89,11 +55,7 @@ void board_init(void)
#endif
Chip_GPIO_Init(LPC_GPIO);
-
- // LED
Chip_GPIO_SetPinDIROutput(LPC_GPIO, LED_PORT, LED_PIN);
-
- // Button
Chip_GPIO_SetPinDIRInput(LPC_GPIO, BUTTON_PORT, BUTTON_PIN);
#if 0
@@ -106,34 +68,34 @@ void board_init(void)
.OpenDrain = 0,
.Pinmode = 0
};
- PINSEL_ConfigPin(&PinCfg);
+ PINSEL_ConfigPin(&PinCfg);
- PinCfg.Portnum = 0;
- PinCfg.Pinnum = 1; // RXD is P0.1
- PINSEL_ConfigPin(&PinCfg);
+ PinCfg.Portnum = 0;
+ PinCfg.Pinnum = 1; // RXD is P0.1
+ PINSEL_ConfigPin(&PinCfg);
- UART_CFG_Type UARTConfigStruct;
+ UART_CFG_Type UARTConfigStruct;
UART_ConfigStructInit(&UARTConfigStruct);
- UARTConfigStruct.Baud_rate = CFG_BOARD_UART_BAUDRATE;
+ UARTConfigStruct.Baud_rate = CFG_BOARD_UART_BAUDRATE;
- UART_Init(BOARD_UART_PORT, &UARTConfigStruct);
- UART_TxCmd(BOARD_UART_PORT, ENABLE); // Enable UART Transmit
+ UART_Init(BOARD_UART_PORT, &UARTConfigStruct);
+ UART_TxCmd(BOARD_UART_PORT, ENABLE); // Enable UART Transmit
#endif
- //------------- USB -------------//
+ //------------- USB -------------//
Chip_IOCON_SetPinMuxing(LPC_IOCON, pin_usb_mux, sizeof(pin_usb_mux) / sizeof(PINMUX_GRP_T));
- Chip_USB_Init();
+ Chip_USB_Init();
enum {
USBCLK_DEVCIE = 0x12, // AHB + Device
- USBCLK_HOST = 0x19, // AHB + Host + OTG
+ USBCLK_HOST = 0x19, // AHB + Host + OTG
// 0x1B // Host + Device + OTG + AHB
};
uint32_t const clk_en = CFG_TUD_ENABLED ? USBCLK_DEVCIE : USBCLK_HOST;
LPC_USB->OTGClkCtrl = clk_en;
- while ( (LPC_USB->OTGClkSt & clk_en) != clk_en );
+ while ((LPC_USB->OTGClkSt & clk_en) != clk_en) {}
#if CFG_TUH_ENABLED
// set portfunc to host !!!
@@ -141,57 +103,53 @@ void board_init(void)
#endif
}
-//--------------------------------------------------------------------+
-// USB Interrupt Handler
-//--------------------------------------------------------------------+
-void USB_IRQHandler(void)
-{
- #if CFG_TUD_ENABLED
- tud_int_handler(0);
- #endif
-
- #if CFG_TUH_ENABLED
- tuh_int_handler(0, true);
- #endif
-}
-
//--------------------------------------------------------------------+
// Board porting API
//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
- Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+void board_led_write(bool state) {
+ Chip_GPIO_SetPinState(LPC_GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON));
}
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
return BUTTON_STATE_ACTIVE == Chip_GPIO_GetPinState(LPC_GPIO, BUTTON_PORT, BUTTON_PIN);
}
-int board_uart_read(uint8_t* buf, int len)
-{
+int board_uart_read(uint8_t* buf, int len) {
// return UART_ReceiveByte(BOARD_UART_PORT);
- (void) buf; (void) len;
+ (void) buf;
+ (void) len;
return 0;
}
-int board_uart_write(void const * buf, int len)
-{
+int board_uart_write(void const* buf, int len) {
// UART_Send(BOARD_UART_PORT, &c, 1, BLOCKING);
- (void) buf; (void) len;
+ (void) buf;
+ (void) len;
return 0;
}
#if CFG_TUSB_OS == OPT_OS_NONE
volatile uint32_t system_ticks = 0;
-void SysTick_Handler (void)
-{
+
+void SysTick_Handler(void) {
system_ticks++;
}
-uint32_t board_millis(void)
-{
+uint32_t board_millis(void) {
return system_ticks;
}
+
+//--------------------------------------------------------------------+
+// USB Interrupt Handler
+//--------------------------------------------------------------------+
+void USB_IRQHandler(void) {
+ #if CFG_TUD_ENABLED
+ tud_int_handler(0);
+ #endif
+
+ #if CFG_TUH_ENABLED
+ tuh_int_handler(0, true);
+ #endif
+}
+
#endif
diff --git a/hw/bsp/lpc17/family.cmake b/hw/bsp/lpc17/family.cmake
new file mode 100644
index 000000000..63ac3149c
--- /dev/null
+++ b/hw/bsp/lpc17/family.cmake
@@ -0,0 +1,102 @@
+include_guard()
+
+if (NOT BOARD)
+ message(FATAL_ERROR "BOARD not specified")
+endif ()
+
+set(SDK_DIR ${TOP}/hw/mcu/nxp/lpcopen/lpc175x_6x/lpc_chip_175x_6x)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR cortex-m3 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS LPC175X_6X CACHE INTERNAL "")
+
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+ if (NOT TARGET ${BOARD_TARGET})
+ add_library(${BOARD_TARGET} STATIC
+ ${SDK_DIR}/../gcc/cr_startup_lpc175x_6x.c
+ ${SDK_DIR}/src/chip_17xx_40xx.c
+ ${SDK_DIR}/src/clock_17xx_40xx.c
+ ${SDK_DIR}/src/gpio_17xx_40xx.c
+ ${SDK_DIR}/src/iocon_17xx_40xx.c
+ ${SDK_DIR}/src/sysctl_17xx_40xx.c
+ ${SDK_DIR}/src/sysinit_17xx_40xx.c
+ ${SDK_DIR}/src/uart_17xx_40xx.c
+ )
+ target_compile_options(${BOARD_TARGET} PUBLIC
+ -nostdlib
+ )
+ target_compile_definitions(${BOARD_TARGET} PUBLIC
+ __USE_LPCOPEN
+ CORE_M3
+ RTC_EV_SUPPORT=0
+ )
+ target_include_directories(${BOARD_TARGET} PUBLIC
+ ${SDK_DIR}/inc
+ )
+
+ update_board(${BOARD_TARGET})
+
+ if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ target_link_options(${BOARD_TARGET} PUBLIC
+ "LINKER:--script=${LD_FILE_GNU}"
+ # nanolib
+ --specs=nosys.specs --specs=nano.specs
+ )
+ elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+ target_link_options(${BOARD_TARGET} PUBLIC
+ "LINKER:--config=${LD_FILE_IAR}"
+ )
+ endif ()
+ endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+ family_configure_common(${TARGET} ${RTOS})
+
+ # Board target
+ add_board_target(board_${BOARD})
+
+ #---------- Port Specific ----------
+ # These files are built for each example since it depends on example's tusb_config.h
+ target_sources(${TARGET} PUBLIC
+ # BSP
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+ )
+ target_include_directories(${TARGET} PUBLIC
+ # family, hw, board
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+ )
+
+ # Add TinyUSB target and port source
+ family_add_tinyusb(${TARGET} OPT_MCU_LPC175X_6X ${RTOS})
+ target_sources(${TARGET}-tinyusb PUBLIC
+ ${TOP}/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
+ ${TOP}/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+ ${TOP}/src/portable/ohci/ohci.c
+ )
+ target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+ # Link dependencies
+ target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+ # Flashing
+ family_flash_jlink(${TARGET})
+ #family_flash_nxplink(${TARGET})
+endfunction()
diff --git a/hw/bsp/lpc17/family.mk b/hw/bsp/lpc17/family.mk
index 67d5e14b5..694b6cccf 100644
--- a/hw/bsp/lpc17/family.mk
+++ b/hw/bsp/lpc17/family.mk
@@ -34,4 +34,5 @@ SRC_C += \
$(MCU_DIR)/src/uart_17xx_40xx.c \
INC += \
- $(TOP)/$(MCU_DIR)/inc
+ $(TOP)/$(BOARD_PATH) \
+ $(TOP)/$(MCU_DIR)/inc \
diff --git a/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h b/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h
index 8fa8f4ffa..2f30a09d4 100644
--- a/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h
+++ b/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h
@@ -42,12 +42,12 @@
#define BUTTON_STATE_ACTIVE 1
// UART
-//#define UART_DEV USART1
-//#define UART_CLK_EN __HAL_RCC_USART1_CLK_ENABLE
-//#define UART_GPIO_PORT GPIOA
+#define UART_DEV USART1
+#define UART_CLK_EN __HAL_RCC_USART1_CLK_ENABLE
+#define UART_GPIO_PORT GPIOA
//#define UART_GPIO_AF GPIO_AF1_USART1
-//#define UART_TX_PIN GPIO_PIN_9
-//#define UART_RX_PIN GPIO_PIN_10
+#define UART_TX_PIN GPIO_PIN_9
+#define UART_RX_PIN GPIO_PIN_10
//--------------------------------------------------------------------+
// RCC Clock
diff --git a/hw/bsp/stm32f1/family.c b/hw/bsp/stm32f1/family.c
index 3b1c5796b..0c1b362ab 100644
--- a/hw/bsp/stm32f1/family.c
+++ b/hw/bsp/stm32f1/family.c
@@ -46,6 +46,7 @@ void USBWakeUp_IRQHandler(void) {
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
//--------------------------------------------------------------------+
+UART_HandleTypeDef UartHandle;
void board_init(void) {
board_stm32f1_clock_init();
@@ -82,6 +83,30 @@ void board_init(void) {
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);
+#ifdef UART_DEV
+ // UART
+ UART_CLK_EN();
+
+ GPIO_InitStruct.Pin = UART_TX_PIN | UART_RX_PIN;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_PULLUP;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+ //GPIO_InitStruct.Alternate = UART_GPIO_AF;
+ HAL_GPIO_Init(UART_GPIO_PORT, &GPIO_InitStruct);
+
+ UartHandle = (UART_HandleTypeDef) {
+ .Instance = UART_DEV,
+ .Init.BaudRate = CFG_BOARD_UART_BAUDRATE,
+ .Init.WordLength = UART_WORDLENGTH_8B,
+ .Init.StopBits = UART_STOPBITS_1,
+ .Init.Parity = UART_PARITY_NONE,
+ .Init.HwFlowCtl = UART_HWCONTROL_NONE,
+ .Init.Mode = UART_MODE_TX_RX,
+ .Init.OverSampling = UART_OVERSAMPLING_16
+ };
+ HAL_UART_Init(&UartHandle);
+#endif
+
// USB Pins
// Configure USB DM and DP pins.
GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12);
@@ -127,9 +152,8 @@ int board_uart_read(uint8_t *buf, int len) {
}
int board_uart_write(void const *buf, int len) {
- (void) buf;
- (void) len;
- return 0;
+ HAL_UART_Transmit(&UartHandle, (uint8_t *) (uintptr_t) buf, len, 0xffff);
+ return len;
}
#if CFG_TUSB_OS == OPT_OS_NONE
diff --git a/hw/bsp/stm32f1/family.mk b/hw/bsp/stm32f1/family.mk
index 90a984bfe..8a5625551 100644
--- a/hw/bsp/stm32f1/family.mk
+++ b/hw/bsp/stm32f1/family.mk
@@ -18,6 +18,9 @@ CFLAGS_GCC += \
-flto \
-nostdlib -nostartfiles \
+# mcu driver cause following warnings
+CFLAGS_GCC += -Wno-error=cast-align
+
LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
# ------------------------
@@ -30,7 +33,8 @@ SRC_C += \
$(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_cortex.c \
$(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_rcc.c \
$(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_rcc_ex.c \
- $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_gpio.c
+ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_gpio.c \
+ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_uart.c
INC += \
$(TOP)/$(BOARD_PATH) \
diff --git a/hw/bsp/stm32f1/stm32f1xx_hal_conf.h b/hw/bsp/stm32f1/stm32f1xx_hal_conf.h
index 45ef993da..0fce774e1 100644
--- a/hw/bsp/stm32f1/stm32f1xx_hal_conf.h
+++ b/hw/bsp/stm32f1/stm32f1xx_hal_conf.h
@@ -38,7 +38,7 @@
/* #define HAL_ADC_MODULE_ENABLED */
/* #define HAL_CAN_MODULE_ENABLED */
/* #define HAL_CAN_LEGACY_MODULE_ENABLED */
-#define HAL_CORTEX_MODULE_ENABLED */
+#define HAL_CORTEX_MODULE_ENABLED
/* #define HAL_CRC_MODULE_ENABLED */
/* #define HAL_DAC_MODULE_ENABLED */
#define HAL_DMA_MODULE_ENABLED
diff --git a/src/class/cdc/cdc_debug.h.bak b/src/class/cdc/cdc_debug.h.bak
new file mode 100644
index 000000000..dc9e8a3c8
--- /dev/null
+++ b/src/class/cdc/cdc_debug.h.bak
@@ -0,0 +1,55 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach (tinyusb.org)
+ * Copyright (c) 2023 IngHK Heiko Kuester
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_CDC_DEBUG_H_
+#define _TUSB_CDC_DEBUG_H_
+
+#include "cdc.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// logging of line coding
+#define TU_LOG_LINE_CODING(PRE_TEXT,LINE_CODING) \
+ TU_LOG_DRV(PRE_TEXT "Line Coding %luBd %u%c%s\r\n", \
+ LINE_CODING.bit_rate, \
+ LINE_CODING.data_bits, \
+ LINE_CODING.parity == CDC_LINE_CODING_PARITY_NONE ? 'N' : \
+ LINE_CODING.parity == CDC_LINE_CODING_PARITY_ODD ? 'O' : \
+ LINE_CODING.parity == CDC_LINE_CODING_PARITY_EVEN ? 'E' : \
+ LINE_CODING.parity == CDC_LINE_CODING_PARITY_MARK ? 'M' : \
+ LINE_CODING.parity == CDC_LINE_CODING_PARITY_SPACE ? 'S' : '?', \
+ LINE_CODING.stop_bits == CDC_LINE_CODING_STOP_BITS_1 ? "1" : \
+ LINE_CODING.stop_bits == CDC_LINE_CODING_STOP_BITS_1_5 ? "1.5" : \
+ LINE_CODING.stop_bits == CDC_LINE_CODING_STOP_BITS_2 ? "2" : "?")
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CDC_DEBUG_H_ */
diff --git a/src/class/cdc/cdc_device.c.bak b/src/class/cdc/cdc_device.c.bak
new file mode 100644
index 000000000..1e3bbb2a1
--- /dev/null
+++ b/src/class/cdc/cdc_device.c.bak
@@ -0,0 +1,501 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (CFG_TUD_ENABLED && CFG_TUD_CDC)
+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h"
+
+#include "cdc_device.h"
+#if 0 // TODO activate and test
+#include "cdc_debug.h"
+#endif
+
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUD_CDC_LOG_LEVEL
+ #define CFG_TUD_CDC_LOG_LEVEL CFG_TUD_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_CDC_LOG_LEVEL, __VA_ARGS__)
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+enum
+{
+ BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64)
+};
+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_notif;
+ uint8_t ep_in;
+ uint8_t ep_out;
+
+ // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
+ uint8_t line_state;
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+ char wanted_char;
+ TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding;
+
+ // FIFO
+ tu_fifo_t rx_ff;
+ tu_fifo_t tx_ff;
+
+ uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
+
+ OSAL_MUTEX_DEF(rx_ff_mutex);
+ OSAL_MUTEX_DEF(tx_ff_mutex);
+
+ // Endpoint Transfer buffer
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE];
+
+}cdcd_interface_t;
+
+#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUD_MEM_SECTION tu_static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+
+static bool _prep_out_transaction (cdcd_interface_t* p_cdc)
+{
+ uint8_t const rhport = 0;
+ uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
+
+ // Prepare for incoming data but only allow what we can store in the ring buffer.
+ // TODO Actually we can still carry out the transfer, keeping count of received bytes
+ // and slowly move it to the FIFO when read().
+ // This pre-check reduces endpoint claiming
+ TU_VERIFY(available >= sizeof(p_cdc->epout_buf));
+
+ // claim endpoint
+ TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out));
+
+ // fifo can be changed before endpoint is claimed
+ available = tu_fifo_remaining(&p_cdc->rx_ff);
+
+ if ( available >= sizeof(p_cdc->epout_buf) )
+ {
+ return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
+ }else
+ {
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(rhport, p_cdc->ep_out);
+
+ return false;
+ }
+}
+
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+bool tud_cdc_n_connected(uint8_t itf)
+{
+ // DTR (bit 0) active is considered as connected
+ return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
+}
+
+uint8_t tud_cdc_n_get_line_state (uint8_t itf)
+{
+ return _cdcd_itf[itf].line_state;
+}
+
+void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding)
+{
+ (*coding) = _cdcd_itf[itf].line_coding;
+}
+
+void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted)
+{
+ _cdcd_itf[itf].wanted_char = wanted;
+}
+
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+uint32_t tud_cdc_n_available(uint8_t itf)
+{
+ return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
+}
+
+uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
+{
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
+ _prep_out_transaction(p_cdc);
+ return num_read;
+}
+
+bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr)
+{
+ return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr);
+}
+
+void tud_cdc_n_read_flush (uint8_t itf)
+{
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ tu_fifo_clear(&p_cdc->rx_ff);
+ _prep_out_transaction(p_cdc);
+}
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
+{
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
+
+ // flush if queue more than packet size
+ // may need to suppress -Wunreachable-code since most of the time CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
+ if ( (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE) || ((CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE) && tu_fifo_full(&p_cdc->tx_ff)) )
+ {
+ tud_cdc_n_write_flush(itf);
+ }
+
+ return ret;
+}
+
+uint32_t tud_cdc_n_write_flush (uint8_t itf)
+{
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+
+ // Skip if usb is not ready yet
+ TU_VERIFY( tud_ready(), 0 );
+
+ // No data to send
+ if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0;
+
+ uint8_t const rhport = 0;
+
+ // Claim the endpoint
+ TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 );
+
+ // Pull data from FIFO
+ uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
+
+ if ( count )
+ {
+ TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 );
+ return count;
+ }else
+ {
+ // Release endpoint since we don't make any transfer
+ // Note: data is dropped if terminal is not connected
+ usbd_edpt_release(rhport, p_cdc->ep_in);
+ return 0;
+ }
+}
+
+uint32_t tud_cdc_n_write_available (uint8_t itf)
+{
+ return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
+}
+
+bool tud_cdc_n_write_clear (uint8_t itf)
+{
+ return tu_fifo_clear(&_cdcd_itf[itf].tx_ff);
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void cdcd_init(void)
+{
+ tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
+
+ for(uint8_t i=0; iwanted_char = (char) -1;
+
+ // default line coding is : stop bit = 1, parity = none, data bits = 8
+ p_cdc->line_coding.bit_rate = 115200;
+ p_cdc->line_coding.stop_bits = 0;
+ p_cdc->line_coding.parity = 0;
+ p_cdc->line_coding.data_bits = 8;
+
+ // Config RX fifo
+ tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false);
+
+ // Config TX fifo as overwritable at initialization and will be changed to non-overwritable
+ // if terminal supports DTR bit. Without DTR we do not know if data is actually polled by terminal.
+ // In this way, the most current data is prioritized.
+ tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true);
+
+ tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, osal_mutex_create(&p_cdc->rx_ff_mutex));
+ tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex), NULL);
+ }
+}
+
+void cdcd_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ for(uint8_t i=0; irx_ff);
+ tu_fifo_clear(&p_cdc->tx_ff);
+ tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
+ }
+}
+
+uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ // Only support ACM subclass
+ TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
+
+ // Find available interface
+ cdcd_interface_t * p_cdc = NULL;
+ for(uint8_t cdc_id=0; cdc_iditf_num = itf_desc->bInterfaceNumber;
+
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+ uint8_t const * p_desc = tu_desc_next( itf_desc );
+
+ // Communication Functional Descriptors
+ while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
+ {
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
+ {
+ // notification endpoint
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+
+ TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 );
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ //------------- Data Interface (if any) -------------//
+ if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+ (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
+ {
+ // next to endpoint descriptor
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+
+ // Open endpoint pair
+ TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 );
+
+ drv_len += 2*sizeof(tusb_desc_endpoint_t);
+ }
+
+ // Prepare for incoming data
+ _prep_out_transaction(p_cdc);
+
+ return drv_len;
+}
+
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ // Handle class request only
+ TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+
+ uint8_t itf = 0;
+ cdcd_interface_t* p_cdc = _cdcd_itf;
+
+ // Identify which interface to use
+ for ( ; ; itf++, p_cdc++)
+ {
+ if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
+
+ if ( p_cdc->itf_num == request->wIndex ) break;
+ }
+
+ switch ( request->bRequest )
+ {
+ case CDC_REQUEST_SET_LINE_CODING:
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ #if 0 // TODO activate and test
+ TU_LOG_LINE_CODING(" Set ", p_cdc->line_coding);
+ #else
+ TU_LOG_DRV(" Set Line Coding\r\n");
+ #endif
+ tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
+ }
+ else if ( stage == CONTROL_STAGE_ACK)
+ {
+ if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
+ }
+ break;
+
+ case CDC_REQUEST_GET_LINE_CODING:
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ #if 0 // TODO activate and test
+ TU_LOG_LINE_CODING(" Get ", p_cdc->line_coding);
+ #else
+ TU_LOG_DRV(" Get Line Coding\r\n");
+ #endif
+ tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
+ }
+ break;
+
+ case CDC_REQUEST_SET_CONTROL_LINE_STATE:
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ tud_control_status(rhport, request);
+ }
+ else if (stage == CONTROL_STAGE_ACK)
+ {
+ // CDC PSTN v1.2 section 6.3.12
+ // Bit 0: Indicates if DTE is present or not.
+ // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
+ // Bit 1: Carrier control for half-duplex modems.
+ // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send)
+ bool const dtr = tu_bit_test(request->wValue, 0);
+ bool const rts = tu_bit_test(request->wValue, 1);
+
+ p_cdc->line_state = (uint8_t) request->wValue;
+
+ // Disable fifo overwriting if DTR bit is set
+ tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
+
+ TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
+
+ // Invoke callback
+ if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts);
+ }
+ break;
+ case CDC_REQUEST_SEND_BREAK:
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ tud_control_status(rhport, request);
+ }
+ else if (stage == CONTROL_STAGE_ACK)
+ {
+ TU_LOG_DRV(" Send Break\r\n");
+ if ( tud_cdc_send_break_cb ) tud_cdc_send_break_cb(itf, request->wValue);
+ }
+ break;
+
+ default: return false; // stall unsupported request
+ }
+
+ return true;
+}
+
+bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
+
+ uint8_t itf;
+ cdcd_interface_t* p_cdc;
+
+ // Identify which interface to use
+ for (itf = 0; itf < CFG_TUD_CDC; itf++)
+ {
+ p_cdc = &_cdcd_itf[itf];
+ if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break;
+ }
+ TU_ASSERT(itf < CFG_TUD_CDC);
+
+ // Received new data
+ if ( ep_addr == p_cdc->ep_out )
+ {
+ tu_fifo_write_n(&p_cdc->rx_ff, p_cdc->epout_buf, (uint16_t) xferred_bytes);
+
+ // Check for wanted char and invoke callback if needed
+ if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) )
+ {
+ for ( uint32_t i = 0; i < xferred_bytes; i++ )
+ {
+ if ( (p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff) )
+ {
+ tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
+ }
+ }
+ }
+
+ // invoke receive callback (if there is still data)
+ if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
+
+ // prepare for OUT transaction
+ _prep_out_transaction(p_cdc);
+ }
+
+ // Data sent to host, we continue to fetch from tx fifo to send.
+ // Note: This will cause incorrect baudrate set in line coding.
+ // Though maybe the baudrate is not really important !!!
+ if ( ep_addr == p_cdc->ep_in )
+ {
+ // invoke transmit callback to possibly refill tx fifo
+ if ( tud_cdc_tx_complete_cb ) tud_cdc_tx_complete_cb(itf);
+
+ if ( 0 == tud_cdc_n_write_flush(itf) )
+ {
+ // If there is no data left, a ZLP should be sent if
+ // xferred_bytes is multiple of EP Packet size and not zero
+ if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) )
+ {
+ if ( usbd_edpt_claim(rhport, p_cdc->ep_in) )
+ {
+ usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
+ }
+ }
+ }
+ }
+
+ // nothing to do with notif endpoint for now
+
+ return true;
+}
+
+#endif
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 9b5186e0c..f6a6773e0 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -90,6 +90,7 @@ static cdch_interface_t cdch_data[CFG_TUH_CDC];
//--------------------------------------------------------------------+
//------------- ACM prototypes -------------//
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
static void acm_process_config(tuh_xfer_t* xfer);
static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
@@ -649,8 +650,6 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
// Enumeration
//--------------------------------------------------------------------+
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
-
static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const *desc_ep)
{
for(size_t i=0; i<2; i++)
@@ -685,8 +684,13 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d
{
return acm_open(daddr, itf_desc, max_len);
}
+<<<<<<< HEAD
#if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
else if ( 0xff == itf_desc->bInterfaceClass )
+=======
+ #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X
+ else if ( TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass )
+>>>>>>> remotes/hathach/master
{
uint16_t vid, pid;
TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
diff --git a/src/class/cdc/cdc_host.c.bak b/src/class/cdc/cdc_host.c.bak
new file mode 100644
index 000000000..2c08f2ace
--- /dev/null
+++ b/src/class/cdc/cdc_host.c.bak
@@ -0,0 +1,1600 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2023 IngHK Heiko Kuester
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (CFG_TUH_ENABLED && CFG_TUH_CDC)
+
+#include "host/usbh.h"
+#include "host/usbh_pvt.h"
+
+#include "cdc_host.h"
+#include "cdc_debug.h"
+
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUH_CDC_LOG_LEVEL
+ #define CFG_TUH_CDC_LOG_LEVEL CFG_TUH_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__)
+
+//--------------------------------------------------------------------+
+// Host CDC Interface
+//--------------------------------------------------------------------+
+
+typedef struct {
+ uint8_t daddr;
+ uint8_t bInterfaceNumber;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+
+ uint8_t serial_drid; // Serial Driver ID
+ cdc_acm_capability_t acm_capability;
+ uint8_t ep_notif;
+
+ uint8_t line_state; // RTS, DTR (refer enums CDC_CONTROL_LINE_STATE_RTS, CDC_CONTROL_LINE_STATE_DTR)
+ TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width
+
+ tuh_xfer_cb_t user_control_cb;
+
+ struct {
+ tu_edpt_stream_t tx;
+ tu_edpt_stream_t rx;
+
+ uint8_t tx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
+ CFG_TUH_MEM_ALIGN uint8_t tx_ep_buf[CFG_TUH_CDC_TX_EPSIZE];
+
+ uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
+ CFG_TUH_MEM_ALIGN uint8_t rx_ep_buf[CFG_TUH_CDC_TX_EPSIZE];
+ } stream;
+#if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CH34X
+ uint32_t baudrate_requested;
+#endif
+#if CFG_TUH_CDC_CH34X
+ struct {
+ uint8_t version;
+ } ch34x;
+#endif
+} cdch_interface_t;
+
+CFG_TUH_MEM_SECTION
+static cdch_interface_t cdch_data[CFG_TUH_CDC];
+
+//--------------------------------------------------------------------+
+// Serial Driver
+//--------------------------------------------------------------------+
+
+//------------- ACM prototypes -------------//
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
+static void acm_process_config(tuh_xfer_t* xfer);
+
+static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+//------------- FTDI prototypes -------------//
+#if CFG_TUH_CDC_FTDI
+#include "serial/ftdi_sio.h"
+
+static uint16_t const ftdi_pids[] = { CFG_TUH_CDC_FTDI_PID_LIST };
+enum {
+ FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0])
+};
+
+static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+static void ftdi_process_config(tuh_xfer_t* xfer);
+
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+#endif
+
+//------------- CP210X prototypes -------------//
+#if CFG_TUH_CDC_CP210X
+#include "serial/cp210x.h"
+
+static uint16_t const cp210x_pids[] = { CFG_TUH_CDC_CP210X_PID_LIST };
+enum {
+ CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0])
+};
+
+static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
+static void cp210x_process_config(tuh_xfer_t* xfer);
+
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+#endif
+
+//------------- CH34x prototypes -------------//
+#if CFG_TUH_CDC_CH34X
+#include "serial/ch34x.h"
+
+static uint16_t const ch34x_vids_pids[][2] = { CFG_TUH_CDC_CH34X_VID_PID_LIST };
+enum {
+ CH34X_VID_PID_COUNT = sizeof ( ch34x_vids_pids ) / sizeof ( ch34x_vids_pids[0] )
+};
+
+static bool ch34x_open ( uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len );
+static void ch34x_process_config ( tuh_xfer_t* xfer );
+
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_modem_ctrl ( cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data );
+static bool ch34x_set_baudrate ( cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data );
+#endif
+
+
+enum {
+ SERIAL_DRIVER_ACM = 0,
+
+#if CFG_TUH_CDC_FTDI
+ SERIAL_DRIVER_FTDI,
+#endif
+
+#if CFG_TUH_CDC_CP210X
+ SERIAL_DRIVER_CP210X,
+#endif
+
+#if CFG_TUH_CDC_CH34X
+ SERIAL_DRIVER_CH34X,
+#endif
+};
+
+typedef struct {
+ void (*const process_set_config)(tuh_xfer_t* xfer);
+ bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+} cdch_serial_driver_t;
+
+// Note driver list must be in the same order as SERIAL_DRIVER enum
+static const cdch_serial_driver_t serial_drivers[] = {
+ { .process_set_config = acm_process_config,
+ .set_control_line_state = acm_set_control_line_state,
+ .set_baudrate = acm_set_baudrate,
+ .set_line_coding = acm_set_line_coding
+ },
+
+ #if CFG_TUH_CDC_FTDI
+ { .process_set_config = ftdi_process_config,
+ .set_control_line_state = ftdi_sio_set_modem_ctrl,
+ .set_baudrate = ftdi_sio_set_baudrate,
+ .set_line_coding = ftdi_set_line_coding
+ },
+ #endif
+
+ #if CFG_TUH_CDC_CP210X
+ { .process_set_config = cp210x_process_config,
+ .set_control_line_state = cp210x_set_modem_ctrl,
+ .set_baudrate = cp210x_set_baudrate,
+ .set_line_coding = cp210x_set_line_coding
+ },
+ #endif
+
+ #if CFG_TUH_CDC_CH34X
+ { .process_set_config = ch34x_process_config,
+ .set_control_line_state = ch34x_set_modem_ctrl,
+ .set_baudrate = ch34x_set_baudrate,
+ .set_line_coding = ch34x_set_line_coding
+ },
+ #endif
+};
+
+enum {
+ SERIAL_DRIVER_COUNT = sizeof(serial_drivers) / sizeof(serial_drivers[0])
+};
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+
+static inline cdch_interface_t* get_itf(uint8_t idx)
+{
+ TU_ASSERT(idx < CFG_TUH_CDC, NULL);
+ cdch_interface_t* p_cdc = &cdch_data[idx];
+
+ return (p_cdc->daddr != 0) ? p_cdc : NULL;
+}
+
+static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr)
+{
+ for(uint8_t i=0; idaddr == daddr) &&
+ (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr))
+ {
+ return i;
+ }
+ }
+
+ return TUSB_INDEX_INVALID_8;
+}
+
+
+static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc)
+{
+ for(uint8_t i=0; idaddr = daddr;
+ p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber;
+ p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass;
+ p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol;
+ p_cdc->line_state = 0;
+ return p_cdc;
+ }
+ }
+
+ return NULL;
+}
+
+static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep);
+static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num);
+static void cdch_internal_control_complete(tuh_xfer_t* xfer);
+
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+
+uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num)
+{
+ for(uint8_t i=0; idaddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i;
+ }
+
+ return TUSB_INDEX_INVALID_8;
+}
+
+bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && info);
+
+ info->daddr = p_cdc->daddr;
+
+ // re-construct descriptor
+ tusb_desc_interface_t* desc = &info->desc;
+ desc->bLength = sizeof(tusb_desc_interface_t);
+ desc->bDescriptorType = TUSB_DESC_INTERFACE;
+
+ desc->bInterfaceNumber = p_cdc->bInterfaceNumber;
+ desc->bAlternateSetting = 0;
+ desc->bNumEndpoints = 2u + (p_cdc->ep_notif ? 1u : 0u);
+ desc->bInterfaceClass = TUSB_CLASS_CDC;
+ desc->bInterfaceSubClass = p_cdc->bInterfaceSubClass;
+ desc->bInterfaceProtocol = p_cdc->bInterfaceProtocol;
+ desc->iInterface = 0; // not used yet
+
+ return true;
+}
+
+bool tuh_cdc_mounted(uint8_t idx)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ return p_cdc != NULL;
+}
+
+bool tuh_cdc_get_dtr(uint8_t idx)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+ TU_LOG_CONTROL_LINE_STATE("CDCh Local ", p_cdc->line_state);
+
+ return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false;
+}
+
+bool tuh_cdc_get_rts(uint8_t idx)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+ TU_LOG_CONTROL_LINE_STATE("CDCh Local ", p_cdc->line_state);
+
+ return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false;
+}
+
+bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ *line_coding = p_cdc->line_coding;
+ TU_LOG_LINE_CODING("CDCh Get ", p_cdc->line_coding);
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// Write
+//--------------------------------------------------------------------+
+
+uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize);
+}
+
+uint32_t tuh_cdc_write_flush(uint8_t idx)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_write_xfer(&p_cdc->stream.tx);
+}
+
+bool tuh_cdc_write_clear(uint8_t idx)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_clear(&p_cdc->stream.tx);
+}
+
+uint32_t tuh_cdc_write_available(uint8_t idx)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_write_available(&p_cdc->stream.tx);
+}
+
+//--------------------------------------------------------------------+
+// Read
+//--------------------------------------------------------------------+
+
+uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize);
+}
+
+uint32_t tuh_cdc_read_available(uint8_t idx)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_read_available(&p_cdc->stream.rx);
+}
+
+bool tuh_cdc_peek(uint8_t idx, uint8_t* ch)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_peek(&p_cdc->stream.rx, ch);
+}
+
+bool tuh_cdc_read_clear (uint8_t idx)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ bool ret = tu_edpt_stream_clear(&p_cdc->stream.rx);
+ tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
+ return ret;
+}
+
+//--------------------------------------------------------------------+
+// Control Endpoint API
+//--------------------------------------------------------------------+
+
+// internal control complete to update state such as line state, encoding
+static void cdch_internal_control_complete(tuh_xfer_t* xfer)
+{
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc, );
+
+ if (xfer->result == XFER_RESULT_SUCCESS)
+ {
+ switch (p_cdc->serial_drid) {
+ case SERIAL_DRIVER_ACM:
+ switch (xfer->setup->bRequest) {
+ case CDC_REQUEST_SET_CONTROL_LINE_STATE:
+ p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue);
+ break;
+
+ case CDC_REQUEST_SET_LINE_CODING: {
+ uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength));
+ memcpy(&p_cdc->line_coding, xfer->buffer, len);
+ }
+ break;
+
+ default: break;
+ }
+ break;
+
+ #if CFG_TUH_CDC_FTDI
+ case SERIAL_DRIVER_FTDI:
+ switch (xfer->setup->bRequest) {
+ case FTDI_SIO_MODEM_CTRL:
+ p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
+ break;
+
+ case FTDI_SIO_SET_BAUD_RATE:
+ // convert from divisor to baudrate is not supported
+ p_cdc->line_coding.bit_rate = p_cdc->baudrate_requested;
+ break;
+
+ default: break;
+ }
+ break;
+ #endif
+
+ #if CFG_TUH_CDC_CP210X
+ case SERIAL_DRIVER_CP210X:
+ switch(xfer->setup->bRequest) {
+ case CP210X_SET_MHS:
+ p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff);
+ break;
+
+ case CP210X_SET_BAUDRATE: {
+ uint32_t baudrate;
+ memcpy(&baudrate, xfer->buffer, sizeof(uint32_t));
+ p_cdc->line_coding.bit_rate = tu_le32toh(baudrate);
+ }
+ break;
+ }
+ break;
+ #endif
+
+ default: break;
+ }
+ }
+
+ xfer->complete_cb = p_cdc->user_control_cb;
+ if (xfer->complete_cb) {
+ xfer->complete_cb(xfer);
+ }
+}
+
+bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+ if ( complete_cb ) {
+ TU_VERIFY(driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data));
+ }else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
+ bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result);
+
+ if (user_data) {
+ // user_data is not NULL, return result via user_data
+ *((xfer_result_t*) user_data) = result;
+ }
+
+ TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+
+ p_cdc->line_state = (uint8_t) line_state;
+ }
+ TU_LOG_CONTROL_LINE_STATE("CDCh Set ", p_cdc->line_state);
+
+ return true;
+}
+
+bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+ if ( complete_cb ) {
+ TU_VERIFY(driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data));
+ }else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
+ bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result);
+
+ if (user_data) {
+ // user_data is not NULL, return result via user_data
+ *((xfer_result_t*) user_data) = result;
+ }
+
+ TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+
+ p_cdc->line_coding.bit_rate = baudrate;
+ }
+ TU_LOG_BAUDRATE("CDCh Set ", p_cdc->line_coding.bit_rate);
+
+ return true;
+}
+
+bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+ if ( complete_cb ) {
+ TU_VERIFY(driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data));
+ } else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
+ bool ret = driver->set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+
+ if (user_data) {
+ // user_data is not NULL, return result via user_data
+ *((xfer_result_t*) user_data) = result;
+ }
+
+ TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+
+ p_cdc->line_coding = *line_coding;
+ }
+ TU_LOG_LINE_CODING("CDCh Set ", p_cdc->line_coding);
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// CLASS-USBH API
+//--------------------------------------------------------------------+
+
+void cdch_init(void)
+{
+ tu_memclr(cdch_data, sizeof(cdch_data));
+
+ for(size_t i=0; istream.tx, true, true, false,
+ p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
+ p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE);
+
+ tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false,
+ p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
+ p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE);
+ }
+}
+
+void cdch_close(uint8_t daddr)
+{
+ for(uint8_t idx=0; idxdaddr == daddr)
+ {
+ TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx);
+
+ // Invoke application callback
+ if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx);
+
+ //tu_memclr(p_cdc, sizeof(cdch_interface_t));
+ p_cdc->daddr = 0;
+ p_cdc->bInterfaceNumber = 0;
+ tu_edpt_stream_close(&p_cdc->stream.tx);
+ tu_edpt_stream_close(&p_cdc->stream.rx);
+ }
+ }
+}
+
+bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
+ // TODO handle stall response, retry failed transfer ...
+ TU_ASSERT(event == XFER_RESULT_SUCCESS);
+
+ uint8_t const idx = get_idx_by_ep_addr(daddr, ep_addr);
+ cdch_interface_t * p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc);
+
+ if ( ep_addr == p_cdc->stream.tx.ep_addr ) {
+ // invoke tx complete callback to possibly refill tx fifo
+ if (tuh_cdc_tx_complete_cb) tuh_cdc_tx_complete_cb(idx);
+
+ if ( 0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx) ) {
+ // If there is no data left, a ZLP should be sent if:
+ // - xferred_bytes is multiple of EP Packet size and not zero
+ tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes);
+ }
+ }
+ else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
+ #if CFG_TUH_CDC_FTDI
+ if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
+ // FTDI reserve 2 bytes for status
+ // FTDI status
+// uint8_t status[2] = {
+// p_cdc->stream.rx.ep_buf[0],
+// p_cdc->stream.rx.ep_buf[1]
+// };
+ tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2);
+ }else
+ #endif
+ {
+ tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes);
+ }
+
+ // invoke receive callback
+ if (tuh_cdc_rx_cb) tuh_cdc_rx_cb(idx);
+
+ // prepare for next transfer if needed
+ tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
+ }else if ( ep_addr == p_cdc->ep_notif ) {
+ // TODO handle notification endpoint
+ }else {
+ TU_ASSERT(false);
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// Enumeration
+//--------------------------------------------------------------------+
+
+static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const *desc_ep)
+{
+ for(size_t i=0; i<2; i++)
+ {
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
+ TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
+
+ TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
+
+ if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
+ {
+ tu_edpt_stream_open(&p_cdc->stream.rx, p_cdc->daddr, desc_ep);
+ }else
+ {
+ tu_edpt_stream_open(&p_cdc->stream.tx, p_cdc->daddr, desc_ep);
+ }
+
+ desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(desc_ep);
+ }
+
+ return true;
+}
+
+bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
+{
+ (void) rhport;
+
+ // Only support ACM subclass
+ // Note: Protocol 0xFF can be RNDIS device
+ if ( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass)
+ {
+ return acm_open(daddr, itf_desc, max_len);
+ }
+ #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
+ else if ( TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass )
+ {
+ uint16_t vid, pid;
+ TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
+
+ #if CFG_TUH_CDC_FTDI
+ if (TU_FTDI_VID == vid) {
+ for (size_t i = 0; i < FTDI_PID_COUNT; i++) {
+ if (ftdi_pids[i] == pid) {
+ return ftdi_open(daddr, itf_desc, max_len);
+ }
+ }
+ }
+ #endif
+
+ #if CFG_TUH_CDC_CP210X
+ if (TU_CP210X_VID == vid) {
+ for (size_t i = 0; i < CP210X_PID_COUNT; i++) {
+ if (cp210x_pids[i] == pid) {
+ return cp210x_open(daddr, itf_desc, max_len);
+ }
+ }
+ }
+ #endif
+
+ #if CFG_TUH_CDC_CH34X
+ for (size_t i = 0; i < CH34X_VID_PID_COUNT; i++) {
+ if ( ch34x_vids_pids[i][0] == vid && ch34x_vids_pids[i][1] == pid ) {
+ return ch34x_open(daddr, itf_desc, max_len);
+ }
+ }
+ #endif
+ }
+ #endif // CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
+
+ return false;
+}
+
+static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) {
+ if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx);
+
+ // Prepare for incoming data
+ tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
+
+ // notify usbh that driver enumeration is complete
+ usbh_driver_set_config_complete(p_cdc->daddr, itf_num);
+}
+
+
+bool cdch_set_config(uint8_t daddr, uint8_t itf_num)
+{
+ tusb_control_request_t request;
+ request.wIndex = tu_htole16((uint16_t) itf_num);
+
+ // fake transfer to kick-off process
+ tuh_xfer_t xfer;
+ xfer.daddr = daddr;
+ xfer.result = XFER_RESULT_SUCCESS;
+ xfer.setup = &request;
+ xfer.user_data = 0; // initial state
+
+ uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num);
+ cdch_interface_t * p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+
+ serial_drivers[p_cdc->serial_drid].process_set_config(&xfer);
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// ACM
+//--------------------------------------------------------------------+
+
+enum {
+ CONFIG_ACM_SET_CONTROL_LINE_STATE = 0,
+ CONFIG_ACM_SET_LINE_CODING,
+ CONFIG_ACM_COMPLETE,
+};
+
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
+{
+ uint8_t const * p_desc_end = ((uint8_t const*) itf_desc) + max_len;
+
+ cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
+
+ p_cdc->serial_drid = SERIAL_DRIVER_ACM;
+
+ //------------- Control Interface -------------//
+ uint8_t const * p_desc = tu_desc_next(itf_desc);
+
+ // Communication Functional Descriptors
+ while( (p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) )
+ {
+ if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
+ {
+ // save ACM bmCapabilities
+ p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // Open notification endpoint of control interface if any
+ if (itf_desc->bNumEndpoints == 1)
+ {
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+
+ TU_ASSERT( tuh_edpt_open(daddr, desc_ep) );
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ //------------- Data Interface (if any) -------------//
+ if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+ (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
+ {
+ // next to endpoint descriptor
+ p_desc = tu_desc_next(p_desc);
+
+ // data endpoints expected to be in pairs
+ TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
+ }
+
+ TU_LOG_DRV("[%u] CDCh ACM opened\r\n", daddr);
+
+ return true;
+}
+
+static void acm_process_config(tuh_xfer_t* xfer)
+{
+ uintptr_t const state = xfer->user_data;
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t * p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc, );
+
+ switch(state)
+ {
+ case CONFIG_ACM_SET_CONTROL_LINE_STATE:
+ #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ if (p_cdc->acm_capability.support_line_request)
+ {
+ TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config,
+ CONFIG_ACM_SET_LINE_CODING), );
+ break;
+ }
+ #endif
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_ACM_SET_LINE_CODING:
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ if (p_cdc->acm_capability.support_line_request)
+ {
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE), );
+ break;
+ }
+ #endif
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_ACM_COMPLETE:
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ TU_LOG_LINE_CODING("CDCh ACM Init ", p_cdc->line_coding);
+ #endif
+ #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ TU_LOG_CONTROL_LINE_STATE("CDCh ACM Init", p_cdc->line_state);
+ #endif
+ // itf_num+1 to account for data interface as well
+ set_config_complete(p_cdc, idx, itf_num+1);
+ break;
+
+ default: break;
+ }
+}
+
+static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_VERIFY(p_cdc->acm_capability.support_line_request);
+ TU_LOG_DRV("CDC ACM Set Control Line State\r\n");
+
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
+ .wValue = tu_htole16(line_state),
+ .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber),
+ .wLength = 0
+ };
+
+ p_cdc->user_control_cb = complete_cb;
+
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call
+ .user_data = user_data
+ };
+
+ TU_ASSERT(tuh_control_xfer(&xfer));
+ return true;
+}
+
+static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("CDC ACM Set Line Conding\r\n");
+ TU_VERIFY(p_cdc->acm_capability.support_line_request);
+
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = CDC_REQUEST_SET_LINE_CODING,
+ .wValue = 0,
+ .wIndex = tu_htole16(p_cdc->bInterfaceNumber),
+ .wLength = tu_htole16(sizeof(cdc_line_coding_t))
+ };
+
+ // use usbh enum buf to hold line coding since user line_coding variable does not live long enough
+ uint8_t* enum_buf = usbh_get_enum_buf();
+ memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t));
+
+ p_cdc->user_control_cb = complete_cb;
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = enum_buf,
+ .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call
+ .user_data = user_data
+ };
+
+ TU_ASSERT(tuh_control_xfer(&xfer));
+ return true;
+}
+
+static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_VERIFY(p_cdc->acm_capability.support_line_request);
+ cdc_line_coding_t line_coding = p_cdc->line_coding;
+ line_coding.bit_rate = baudrate;
+ return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
+}
+
+//--------------------------------------------------------------------+
+// FTDI
+//--------------------------------------------------------------------+
+#if CFG_TUH_CDC_FTDI
+
+enum {
+ CONFIG_FTDI_RESET = 0,
+ CONFIG_FTDI_MODEM_CTRL,
+ CONFIG_FTDI_SET_BAUDRATE,
+ CONFIG_FTDI_SET_DATA,
+ CONFIG_FTDI_COMPLETE
+};
+
+static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
+ // FTDI Interface includes 1 vendor interface + 2 bulk endpoints
+ TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2);
+ TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len);
+
+ cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
+
+ TU_LOG_DRV("FTDI opened\r\n");
+
+ p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
+
+ // endpoint pair
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ // data endpoints expected to be in pairs
+ return open_ep_stream_pair(p_cdc, desc_ep);
+}
+
+// set request without data
+static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_VENDOR,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = command,
+ .wValue = tu_htole16(value),
+ .wIndex = 0,
+ .wLength = 0
+ };
+
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
+static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data);
+}
+
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ TU_ASSERT(false, false); // TODO
+ return false;
+}
+
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ TU_LOG_DRV("CDC FTDI Set Control Line State\r\n");
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state,
+ complete_cb ? cdch_internal_control_complete : NULL, user_data));
+ return true;
+}
+
+static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base)
+{
+ const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+ uint32_t divisor;
+
+ /* divisor shifted 3 bits to the left */
+ uint32_t divisor3 = base / (2 * baud);
+ divisor = (divisor3 >> 3);
+ divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14;
+
+ /* Deal with special cases for highest baud rates. */
+ if (divisor == 1) { /* 1.0 */
+ divisor = 0;
+ }
+ else if (divisor == 0x4001) { /* 1.5 */
+ divisor = 1;
+ }
+
+ return divisor;
+}
+
+static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
+{
+ return ftdi_232bm_baud_base_to_divisor(baud, 48000000u);
+}
+
+static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate);
+ TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor);
+
+ p_cdc->user_control_cb = complete_cb;
+ p_cdc->baudrate_requested = baudrate;
+ TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor,
+ complete_cb ? cdch_internal_control_complete : NULL, user_data));
+
+ return true;
+}
+
+static void ftdi_process_config(tuh_xfer_t* xfer) {
+ uintptr_t const state = xfer->user_data;
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t * p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc, );
+
+ switch(state) {
+ // Note may need to read FTDI eeprom
+ case CONFIG_FTDI_RESET:
+ TU_ASSERT(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL),);
+ break;
+
+ case CONFIG_FTDI_MODEM_CTRL:
+ #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ TU_ASSERT(
+ ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+
+ case CONFIG_FTDI_SET_BAUDRATE: {
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, ftdi_process_config, CONFIG_FTDI_SET_DATA),);
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+ }
+
+ case CONFIG_FTDI_SET_DATA: {
+ #if 0 // TODO set data format
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE),);
+ break;
+ #endif
+ #endif
+
+ TU_ATTR_FALLTHROUGH;
+ }
+
+ case CONFIG_FTDI_COMPLETE:
+ set_config_complete(p_cdc, idx, itf_num);
+ #if 0 // TODO set data format
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ TU_LOG_LINE_CODING("CDCh FTDI Init ", p_cdc->line_coding);
+ #endif
+ #endif
+ #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ TU_LOG_CONTROL_LINE_STATE("CDCh FTDI Init ", p_cdc->line_state);
+ #endif
+ break;
+
+ default:
+ break;
+ }
+}
+
+#endif
+
+//--------------------------------------------------------------------+
+// CP210x
+//--------------------------------------------------------------------+
+
+#if CFG_TUH_CDC_CP210X
+
+enum {
+ CONFIG_CP210X_IFC_ENABLE = 0,
+ CONFIG_CP210X_SET_BAUDRATE,
+ CONFIG_CP210X_SET_LINE_CTL,
+ CONFIG_CP210X_SET_DTR_RTS,
+ CONFIG_CP210X_COMPLETE
+};
+
+static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+ // CP210x Interface includes 1 vendor interface + 2 bulk endpoints
+ TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2);
+ TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len);
+
+ cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
+
+ TU_LOG_DRV("CP210x opened\r\n");
+ p_cdc->serial_drid = SERIAL_DRIVER_CP210X;
+
+ // endpoint pair
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ // data endpoints expected to be in pairs
+ return open_ep_stream_pair(p_cdc, desc_ep);
+}
+
+static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_VENDOR,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = command,
+ .wValue = tu_htole16(value),
+ .wIndex = p_cdc->bInterfaceNumber,
+ .wLength = tu_htole16(length)
+ };
+
+ // use usbh enum buf since application variable does not live long enough
+ uint8_t* enum_buf = NULL;
+
+ if (buffer && length > 0) {
+ enum_buf = usbh_get_enum_buf();
+ tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ }
+
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = enum_buf,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
+static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data);
+}
+
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ TU_ASSERT(false, false); // TODO
+ return false;
+}
+
+static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\r\n", baudrate);
+ uint32_t baud_le = tu_htole32(baudrate);
+ p_cdc->user_control_cb = complete_cb;
+ return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4,
+ complete_cb ? cdch_internal_control_complete : NULL, user_data);
+}
+
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ TU_LOG_DRV("CDC CP210x Set Control Line State\r\n");
+ p_cdc->user_control_cb = complete_cb;
+ return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0,
+ complete_cb ? cdch_internal_control_complete : NULL, user_data);
+}
+
+static void cp210x_process_config(tuh_xfer_t* xfer) {
+ uintptr_t const state = xfer->user_data;
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t *p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc,);
+
+ switch (state) {
+ case CONFIG_CP210X_IFC_ENABLE:
+ TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, cp210x_process_config, CONFIG_CP210X_SET_BAUDRATE),);
+ break;
+
+ case CONFIG_CP210X_SET_BAUDRATE: {
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL),);
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+ }
+
+ case CONFIG_CP210X_SET_LINE_CTL: {
+ #if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+ }
+
+ case CONFIG_CP210X_SET_DTR_RTS:
+ #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ TU_ASSERT(
+ cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+
+ case CONFIG_CP210X_COMPLETE:
+ set_config_complete(p_cdc, idx, itf_num);
+ #if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now
+ TU_LOG_LINE_CODING("CDCh CP210x Init ", p_cdc->line_coding);
+ #endif
+ #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ TU_LOG_CONTROL_LINE_STATE("CDCh CP210x Init ", p_cdc->line_state);
+ #endif
+ break;
+
+ default: break;
+ }
+}
+
+#endif
+
+//--------------------------------------------------------------------+
+// CH34x (CH340 & CH341)
+//--------------------------------------------------------------------+
+
+#if CFG_TUH_CDC_CH34X
+
+#define CH34X_LOGS false
+
+enum {
+ CONFIG_CH34X_READ_VERSION = 0,
+ CONFIG_CH34X_SERIAL_INIT,
+ CONFIG_CH34X_SPECIAL_REG_WRITE,
+ CONFIG_CH34X_FLOW_CONTROL,
+ CONFIG_CH34X_MODEM_CONTROL,
+ CONFIG_CH34X_COMPLETE
+};
+
+static bool ch34x_open ( uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len )
+{
+ // CH34x Interface includes 1 vendor interface + 3 bulk endpoints
+ TU_VERIFY ( itf_desc->bNumEndpoints == 3 );
+ TU_VERIFY ( sizeof ( tusb_desc_interface_t ) + 2 * sizeof ( tusb_desc_endpoint_t ) <= max_len );
+
+ cdch_interface_t *p_cdc = make_new_itf ( daddr, itf_desc );
+ TU_VERIFY ( p_cdc );
+
+
+ p_cdc->serial_drid = SERIAL_DRIVER_CH34X;
+
+ // endpoint pair
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next ( itf_desc );
+
+ // data endpoints expected to be in pairs
+ TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep), false);
+
+ TU_LOG_DRV("[%u] CDCh CH34x opened\r\n", daddr);
+
+ return true;
+}
+
+static bool ch34x_set_request ( cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value, uint16_t index,
+ uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+{
+ tusb_control_request_t const request_setup = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_VENDOR,
+ .direction = direction
+ },
+ .bRequest = request,
+ .wValue = tu_htole16 ( value ),
+ .wIndex = tu_htole16 ( index ),
+ .wLength = tu_htole16 ( length )
+ };
+
+ // use usbh enum buf since application variable does not live long enough
+ uint8_t* enum_buf = NULL;
+
+ if ( buffer && length > 0 ) {
+ enum_buf = usbh_get_enum_buf();
+ tu_memcpy_s ( enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length );
+ }
+
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request_setup,
+ .buffer = enum_buf,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer ( &xfer );
+}
+
+static bool ch34x_control_out ( cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+{
+ return ch34x_set_request ( p_cdc, TUSB_DIR_OUT, request, value, index, /* buffer */ NULL, /* length */ 0, complete_cb, user_data );
+}
+
+static bool ch34x_control_in ( cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+ uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+{
+ return ch34x_set_request ( p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize, complete_cb, user_data );
+}
+
+static bool ch34x_write_reg ( cdch_interface_t* p_cdc, uint16_t reg, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+{
+ return ch34x_control_out ( p_cdc, CH34X_REQ_WRITE_REG, /* value */ reg, /* index */ value, complete_cb, user_data );
+}
+
+//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg,
+// uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+//{
+// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, /* index */ 0, buffer, buffersize, complete_cb, user_data );
+//}
+
+uint8_t ch34x_xfer_get_itf_num ( tuh_xfer_t* xfer )
+// CH34x needs a special handling to get bInterfaceNumber, because wIndex is used for other purposes and not for bInterfaceNumber
+// CH340 and CH341 derivates have always only one interface, so it's OK to check only daddr
+{
+ for ( uint8_t i=0; idaddr == xfer->daddr ) return p_cdc->bInterfaceNumber;
+ }
+
+ return INTERFACE_INVALID_NUMBER;
+}
+
+// internal control complete to update state such as line state, encoding
+static void ch34x_control_complete ( tuh_xfer_t* xfer )
+{
+ uint8_t const itf_num = ch34x_xfer_get_itf_num ( xfer );
+ uint8_t const idx = tuh_cdc_itf_get_index ( xfer->daddr, itf_num );
+ cdch_interface_t *p_cdc = get_itf ( idx );
+ uint16_t value = tu_le16toh ( xfer->setup->wValue );
+ TU_ASSERT ( p_cdc, );
+ TU_ASSERT ( p_cdc->serial_drid == SERIAL_DRIVER_CH34X, ); // ch34x_control_complete is only used for CH34x
+
+ if ( xfer->result == XFER_RESULT_SUCCESS ) {
+ switch ( xfer->setup->bRequest ) {
+ case CH34X_REQ_WRITE_REG: { // register write request
+ switch ( value ) {
+ case ( 0x1312 ): { // baudrate write
+ p_cdc->line_coding.bit_rate = p_cdc->baudrate_requested;
+ #if CH34X_LOGS
+ TU_LOG_BAUDRATE("CDCh CH34x Control Complete ", p_cdc->line_coding.bit_rate);
+ #endif
+ break;
+ }
+ default: {
+ TU_ASSERT(false, ); // unexpected register write
+ break;
+ }
+ }
+ break;
+ }
+ case CH34X_REQ_MODEM_CTRL: { // set modem controls RTS/DTR request
+ if ( ~value & CH34X_BIT_RTS )
+ p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS;
+ else
+ p_cdc->line_state &= ~CDC_CONTROL_LINE_STATE_RTS;
+ if ( ~value & CH34X_BIT_DTR )
+ p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR;
+ else
+ p_cdc->line_state &= ~CDC_CONTROL_LINE_STATE_DTR;
+ #if CH34X_LOGS
+ TU_LOG_CONTROL_LINE_STATE("CDCh CH34x Control Complete ", p_cdc->line_state);
+ #endif
+ break;
+ }
+ default: {
+ TU_ASSERT(false, ); // unexpected request
+ break;
+ }
+ }
+ xfer->complete_cb = p_cdc->user_control_cb;
+ if (xfer->complete_cb)
+ xfer->complete_cb(xfer);
+ }
+}
+
+static bool ch34x_get_factor_divisor ( uint32_t baval, uint8_t *factor, uint8_t *divisor )
+{ // calc baudrate factor and divisor
+ uint8_t a;
+ uint8_t b;
+ uint32_t c;
+
+ switch (baval) {
+ case 921600:
+ a = 0xf3;
+ b = 7;
+ break;
+ case 307200:
+ a = 0xd9;
+ b = 7;
+ break;
+ default:
+ if (baval > 6000000 / 255) {
+ b = 3;
+ c = 6000000;
+ } else if (baval > 750000 / 255) {
+ b = 2;
+ c = 750000;
+ } else if (baval > 93750 / 255) {
+ b = 1;
+ c = 93750;
+ } else {
+ b = 0;
+ c = 11719;
+ }
+ a = (unsigned char)(c / baval);
+ if (a == 0 || a == 0xFF)
+ return false;
+ if ((c / a - baval) > (baval - c / (a + 1)))
+ a++;
+ a = 256 - a;
+ break;
+ }
+ *factor = a;
+ *divisor = b;
+
+ return true;
+}
+
+static bool ch34x_get_lcr ( cdc_line_coding_t const* line_coding, uint8_t *lcr )
+{ // calc lcr register value (data bits, parity, stop bits)
+ *lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
+ switch ( line_coding->data_bits ) {
+ case 5:
+ *lcr |= CH34X_LCR_CS5;
+ break;
+ case 6:
+ *lcr |= CH34X_LCR_CS6;
+ break;
+ case 7:
+ *lcr |= CH34X_LCR_CS7;
+ break;
+ case 8:
+ *lcr |= CH34X_LCR_CS8;
+ break;
+ default:
+ TU_ASSERT ( false, ); // not supported data_bits
+ *lcr |= CH34X_LCR_CS8;
+ break;
+ }
+ if ( line_coding.parity != CDC_LINE_CODING_PARITY_NONE ) {
+ *lcr |= CH34X_LCR_ENABLE_PAR;
+ if ( line_coding.parity == CDC_LINE_CODING_PARITY_EVEN || line_coding.parity == CDC_LINE_CODING_PARITY_SPACE )
+ *lcr |= CH34X_LCR_PAR_EVEN;
+ if ( line_coding.parity == CDC_LINE_CODING_PARITY_MARK || line_coding.parity == CDC_LINE_CODING_PARITY_SPACE )
+ *lcr |= CH34X_LCR_MARK_SPACE;
+ }
+ TU_ASSERT ( line_coding.stop_bits == CDC_LINE_CODING_STOP_BITS_1 || line_coding.stop_bits == CDC_LINE_CODING_STOP_BITS_2, ); // not supported 1.5 stop bits
+ if ( line_coding.stop_bits == CDC_LINE_CODING_STOP_BITS_2 )
+ *lcr |= CH34X_LCR_STOP_BITS_2;
+
+ return true;
+}
+
+static bool ch34x_set_baudrate ( cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+{
+ p_cdc->baudrate_requested = baudrate;
+ p_cdc->user_control_cb = complete_cb;
+ uint8_t factor, divisor;
+ TU_ASSERT ( ch34x_get_factor_divisor ( baudrate, &factor, &divisor ), false );
+ TU_ASSERT ( ch34x_write_reg ( p_cdc, /* reg */ 0x1312, /* value */ (uint16_t)factor << 8 | 0x80 | divisor,
+ complete_cb ? ch34x_control_complete : NULL, user_data ), false );
+ return true;
+}
+
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ TU_ASSERT(false, false); // TODO
+ return false;
+}
+
+static bool ch34x_set_modem_ctrl ( cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+{
+ p_cdc->user_control_cb = complete_cb;
+ uint16_t control = 0;
+ if ( line_state & CDC_CONTROL_LINE_STATE_RTS )
+ control |= CH34X_BIT_RTS;
+ if ( line_state & CDC_CONTROL_LINE_STATE_DTR )
+ control |= CH34X_BIT_DTR;
+ TU_ASSERT ( ch34x_control_out ( p_cdc, /* request */ CH34X_REQ_MODEM_CTRL, /* value */ (uint8_t)~control,
+ /* index */ 0, complete_cb ? ch34x_control_complete : NULL, user_data ), false );
+ return true;
+}
+
+static void ch34x_process_config ( tuh_xfer_t* xfer )
+{
+ uint8_t const itf_num = ch34x_xfer_get_itf_num ( xfer );
+ uintptr_t const state = xfer->user_data;
+ uint8_t const idx = tuh_cdc_itf_get_index ( xfer->daddr, itf_num );
+ cdch_interface_t *p_cdc = get_itf ( idx );
+ cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
+ uint8_t buffer[2];
+ TU_ASSERT ( p_cdc, );
+
+ switch ( state ) {
+ case CONFIG_CH34X_READ_VERSION: // request version read
+ #if CH34X_LOGS
+ TU_LOG_DRV ( "[%u] CDCh CH34x Process Config started\r\n", p_cdc->daddr );
+ #endif
+ p_cdc->ch34x.version = 0;
+ TU_ASSERT ( ch34x_control_in ( p_cdc, /* request */ CH34X_REQ_READ_VERSION, /* value */ 0,
+ /* index */ 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT ), );
+ break;
+ case CONFIG_CH34X_SERIAL_INIT: { // handle version read data, request to init CH34x with line_coding and baudrate
+ p_cdc->ch34x.version = xfer->buffer[0];
+ #if CH34X_LOGS
+ TU_LOG_DRV ( "[%u] CDCh CH34x Chip version=%02x\r\n", p_cdc->daddr, p_cdc->ch34x.version );
+ #endif
+ // only versions >= 0x30 are tested, below 0x30 seems having other programming, see WCH vendor, linux kernel and FreeBSD drivers
+ TU_ASSERT ( p_cdc->ch34x.version >= 0x30, );
+ uint8_t factor, divisor;
+ TU_ASSERT ( ch34x_get_factor_divisor ( line_coding.bit_rate, &factor, &divisor ), );
+ TU_ASSERT ( ch34x_control_out ( p_cdc, /* request */ CH34X_REQ_SERIAL_INIT, /* value */ (uint16_t)lcr << 8 | 0x9c,
+ /* index */ (uint16_t)factor << 8 | 0x80 | divisor, ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE ), );
+ break;
+ }
+ case CONFIG_CH34X_SPECIAL_REG_WRITE: // do special reg write, purpose unknown, overtaken from WCH driver
+ TU_ASSERT ( ch34x_write_reg ( p_cdc, /* reg */ 0x0f2c, /* value */ 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL ), );
+ break;
+ case CONFIG_CH34X_FLOW_CONTROL: // no hardware flow control
+ TU_ASSERT ( ch34x_write_reg ( p_cdc, /* reg */ 0x2727, /* value */ 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL ), );
+ break;
+ case CONFIG_CH34X_MODEM_CONTROL: // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT)
+ TU_ASSERT ( ch34x_set_modem_ctrl ( p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE ), );
+ break;
+ case CONFIG_CH34X_COMPLETE:
+ p_cdc->line_coding = line_coding; // CONFIG_CH34X_SERIAL_INIT not handled by ch34x_control_complete
+ #if CH34X_LOGS
+ TU_LOG_DRV("CDCh CH34x Process Config Complete\r\n");
+ TU_LOG_LINE_CODING(" ", p_cdc->line_coding);
+ TU_LOG_CONTROL_LINE_STATE(" ", p_cdc->line_state);
+ #endif
+ set_config_complete ( p_cdc, idx, itf_num );
+ break;
+ default:
+ TU_ASSERT ( false, );
+ break;
+ }
+}
+
+#endif // CFG_TUH_CDC_CH34X
+
+#endif // (CFG_TUH_ENABLED && CFG_TUH_CDC)
diff --git a/src/class/cdc/cdc_host.h.bak b/src/class/cdc/cdc_host.h.bak
new file mode 100644
index 000000000..7b0d2a392
--- /dev/null
+++ b/src/class/cdc/cdc_host.h.bak
@@ -0,0 +1,207 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_CDC_HOST_H_
+#define _TUSB_CDC_HOST_H_
+
+#include "cdc.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+// Set Line Control state on enumeration/mounted, refer enums CDC_CONTROL_LINE_STATE_RTS, CDC_CONTROL_LINE_STATE_DTR
+#ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0
+#endif
+
+// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
+//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+//#endif
+
+// RX FIFO size
+#ifndef CFG_TUH_CDC_RX_BUFSIZE
+#define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX
+#endif
+
+// RX Endpoint size
+#ifndef CFG_TUH_CDC_RX_EPSIZE
+#define CFG_TUH_CDC_RX_EPSIZE USBH_EPSIZE_BULK_MAX
+#endif
+
+// TX FIFO size
+#ifndef CFG_TUH_CDC_TX_BUFSIZE
+#define CFG_TUH_CDC_TX_BUFSIZE USBH_EPSIZE_BULK_MAX
+#endif
+
+// TX Endpoint size
+#ifndef CFG_TUH_CDC_TX_EPSIZE
+#define CFG_TUH_CDC_TX_EPSIZE USBH_EPSIZE_BULK_MAX
+#endif
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// Get Interface index from device address + interface number
+// return TUSB_INDEX_INVALID_8 (0xFF) if not found
+uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num);
+
+// Get Interface information
+// return true if index is correct and interface is currently mounted
+bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
+
+// Check if a interface is mounted
+bool tuh_cdc_mounted(uint8_t idx);
+
+// Get current DTR status
+bool tuh_cdc_get_dtr(uint8_t idx);
+
+// Get current RTS status
+bool tuh_cdc_get_rts(uint8_t idx);
+
+// Get current line status
+bool tuh_cdc_get_control_line_state(uint8_t idx, uint8_t* line_state);
+
+// Check if interface is connected (DTR active)
+TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx)
+{
+ return tuh_cdc_get_dtr(idx);
+}
+
+// Get local (saved/cached) version of line coding.
+// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding()
+// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined.
+// NOTE: This function does not make any USB transfer request to device.
+bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding);
+
+//--------------------------------------------------------------------+
+// Write API
+//--------------------------------------------------------------------+
+
+// Get the number of bytes available for writing
+uint32_t tuh_cdc_write_available(uint8_t idx);
+
+// Write to cdc interface
+uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize);
+
+// Force sending data if possible, return number of forced bytes
+uint32_t tuh_cdc_write_flush(uint8_t idx);
+
+// Clear the transmit FIFO
+bool tuh_cdc_write_clear(uint8_t idx);
+
+//--------------------------------------------------------------------+
+// Read API
+//--------------------------------------------------------------------+
+
+// Get the number of bytes available for reading
+uint32_t tuh_cdc_read_available(uint8_t idx);
+
+// Read from cdc interface
+uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize);
+
+// Get a byte from RX FIFO without removing it
+bool tuh_cdc_peek(uint8_t idx, uint8_t* ch);
+
+// Clear the received FIFO
+bool tuh_cdc_read_clear (uint8_t idx);
+
+//--------------------------------------------------------------------+
+// Control Endpoint (Request) API
+// Each Function will make a USB control transfer request to/from device
+// - If complete_cb is provided, the function will return immediately and invoke
+// the callback when request is complete.
+// - If complete_cb is NULL, the function will block until request is complete.
+// - In this case, user_data should be pointed to xfer_result_t to hold the transfer result.
+// - The function will return true if transfer is successful, false otherwise.
+//--------------------------------------------------------------------+
+
+// Request to Set Control Line State, refer enums CDC_CONTROL_LINE_STATE_RTS, CDC_CONTROL_LINE_STATE_DTR
+bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to set baudrate
+bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to Set Line Coding (ACM only)
+// Should only use if you don't work with serial devices such as FTDI/CP210x
+bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to Get Line Coding (ACM only)
+// Should only use if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() never got invoked and
+// CFG_TUH_CDC_LINE_CODING_ON_ENUM is not defined
+// bool tuh_cdc_get_line_coding(uint8_t idx, cdc_line_coding_t* coding);
+
+// Connect by set both DTR, RTS
+TU_ATTR_ALWAYS_INLINE static inline
+bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
+}
+
+// Disconnect by clear both DTR, RTS
+TU_ATTR_ALWAYS_INLINE static inline
+bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
+}
+
+//--------------------------------------------------------------------+
+// CDC APPLICATION CALLBACKS
+//--------------------------------------------------------------------+
+
+// Invoked when a device with CDC interface is mounted
+// idx is index of cdc interface in the internal pool.
+TU_ATTR_WEAK extern void tuh_cdc_mount_cb(uint8_t idx);
+
+// Invoked when a device with CDC interface is unmounted
+TU_ATTR_WEAK extern void tuh_cdc_umount_cb(uint8_t idx);
+
+// Invoked when received new data
+TU_ATTR_WEAK extern void tuh_cdc_rx_cb(uint8_t idx);
+
+// Invoked when a TX is complete and therefore space becomes available in TX buffer
+TU_ATTR_WEAK extern void tuh_cdc_tx_complete_cb(uint8_t idx);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void cdch_init (void);
+bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
+bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num);
+bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void cdch_close (uint8_t dev_addr);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CDC_HOST_H_ */
diff --git a/src/class/cdc/serial/ch34x.h.bak b/src/class/cdc/serial/ch34x.h.bak
new file mode 100644
index 000000000..f77434b31
--- /dev/null
+++ b/src/class/cdc/serial/ch34x.h.bak
@@ -0,0 +1,60 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Heiko Kuester (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _CH34X_H_
+#define _CH34X_H_
+
+// set line_coding
+#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH341 CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#else // this default is necessary to work properly
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH341 { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+#endif
+
+// USB requests
+#define CH341_REQ_READ_VERSION 0x5F // dec 95
+#define CH341_REQ_WRITE_REG 0x9A // dec 154
+#define CH341_REQ_READ_REG 0x95 // dec 149
+#define CH341_REQ_SERIAL_INIT 0xA1 // dec 161
+#define CH341_REQ_MODEM_CTRL 0xA4 // dev 164
+
+// modem control bits
+#define CH341_BIT_RTS ( 1 << 6 )
+#define CH341_BIT_DTR ( 1 << 5 )
+
+// line control bits
+#define CH341_LCR_ENABLE_RX 0x80
+#define CH341_LCR_ENABLE_TX 0x40
+#define CH341_LCR_MARK_SPACE 0x20
+#define CH341_LCR_PAR_EVEN 0x10
+#define CH341_LCR_ENABLE_PAR 0x08
+#define CH341_LCR_STOP_BITS_2 0x04
+#define CH341_LCR_CS8 0x03
+#define CH341_LCR_CS7 0x02
+#define CH341_LCR_CS6 0x01
+#define CH341_LCR_CS5 0x00
+
+#endif /* _CH34X_H_ */
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index 1b5f53dfc..8aa66b4df 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -75,8 +75,8 @@
#define _MESS_FAILED() do {} while (0)
#endif
-// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33
-#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__)
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__)
#define TU_BREAKPOINT() do \
{ \
volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
diff --git a/src/common/tusb_verify.h.bak b/src/common/tusb_verify.h.bak
new file mode 100644
index 000000000..2b0967b2c
--- /dev/null
+++ b/src/common/tusb_verify.h.bak
@@ -0,0 +1,140 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+#ifndef TUSB_VERIFY_H_
+#define TUSB_VERIFY_H_
+
+#include
+#include
+#include "tusb_option.h"
+#include "tusb_compiler.h"
+#include "stm32wlxx_hal.h"
+
+
+/*------------------------------------------------------------------*/
+/* This file use an advanced macro technique to mimic the default parameter
+ * as C++ for the sake of code simplicity. Beware of a headache macro
+ * manipulation that you are told to stay away.
+ *
+ * This contains macros for both VERIFY and ASSERT:
+ *
+ * VERIFY: Used when there is an error condition which is not the
+ * fault of the MCU. For example, bounds checking on data
+ * sent to the micro over USB should use this function.
+ * Another example is checking for buffer overflows, where
+ * returning from the active function causes a NAK.
+ *
+ * ASSERT: Used for error conditions that are caused by MCU firmware
+ * bugs. This is used to discover bugs in the code more
+ * quickly. One example would be adding assertions in library
+ * function calls to confirm a function's (untainted)
+ * parameters are valid.
+ *
+ * The difference in behavior is that ASSERT triggers a breakpoint while
+ * verify does not.
+ *
+ * #define TU_VERIFY(cond) if(cond) return false;
+ * #define TU_VERIFY(cond,ret) if(cond) return ret;
+ *
+ * #define TU_ASSERT(cond) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return false;}
+ * #define TU_ASSERT(cond,ret) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return ret;}
+ *------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// TU_VERIFY Helper
+//--------------------------------------------------------------------+
+
+#if CFG_TUSB_DEBUG
+ #include
+ #define _MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
+#else
+ #define _MESS_FAILED() do {} while (0)
+#endif
+
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__)
+ #define TU_BREAKPOINT() do \
+ { \
+ volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
+ if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \
+ } while(0)
+
+#elif defined(__riscv)
+ #define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
+
+#elif defined(_mips)
+ #define TU_BREAKPOINT() do { __asm("sdbbp 0"); } while (0)
+
+#else
+ #define TU_BREAKPOINT() do {} while (0)
+#endif
+
+// Helper to implement optional parameter for TU_VERIFY Macro family
+#define _GET_3RD_ARG(arg1, arg2, arg3, ...) arg3
+#define _GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
+#define _GET_5TH_ARG(arg1, arg2, arg3, arg4, arg5, ...) arg5
+
+/*------------------------------------------------------------------*/
+/* TU_VERIFY
+ * - TU_VERIFY_1ARGS : return false if failed
+ * - TU_VERIFY_2ARGS : return provided value if failed
+ *------------------------------------------------------------------*/
+#define TU_VERIFY_DEFINE(_cond, _ret) \
+ do { \
+ if ( !(_cond) ) { return _ret; } \
+ } while(0)
+
+#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, false)
+#define TU_VERIFY_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _ret)
+
+#define TU_VERIFY(...) _GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, _dummy)(__VA_ARGS__)
+
+/*------------------------------------------------------------------*/
+/* ASSERT
+ * basically TU_VERIFY with TU_BREAKPOINT() as handler
+ * - 1 arg : return false if failed
+ * - 2 arg : return error if failed
+ *------------------------------------------------------------------*/
+#define TU_ASSERT_DEFINE(_cond, _ret) \
+ do { \
+ if ( !(_cond) ) { _MESS_FAILED(); HAL_Delay ( 1000 ); TU_BREAKPOINT(); return _ret; } \
+ } while(0)
+
+#define TU_ASSERT_1ARGS(_cond) TU_ASSERT_DEFINE(_cond, false)
+#define TU_ASSERT_2ARGS(_cond, _ret) TU_ASSERT_DEFINE(_cond, _ret)
+
+#ifndef TU_ASSERT
+#define TU_ASSERT(...) _GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_2ARGS, TU_ASSERT_1ARGS, _dummy)(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/device/dcd.h b/src/device/dcd.h
index 8c6813cf7..69c26bcf4 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -152,7 +152,7 @@ void dcd_sof_enable(uint8_t rhport, bool en);
// Invoked when a control transfer's status stage is complete.
// May help DCD to prepare for next control transfer, this API is optional.
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK;
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request);
// Configure endpoint's registers according to descriptor
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 59466b42e..5c94ebcc5 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -38,11 +38,19 @@
//--------------------------------------------------------------------+
// USBD Configuration
//--------------------------------------------------------------------+
-
#ifndef CFG_TUD_TASK_QUEUE_SZ
#define CFG_TUD_TASK_QUEUE_SZ 16
#endif
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
+ (void)rhport;
+ (void)eventid;
+ (void)in_isr;
+}
+
//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
@@ -50,10 +58,8 @@
// Invalid driver ID in itf2drv[] ep2drv[][] mapping
enum { DRVID_INVALID = 0xFFu };
-typedef struct
-{
- struct TU_ATTR_PACKED
- {
+typedef struct {
+ struct TU_ATTR_PACKED {
volatile uint8_t connected : 1;
volatile uint8_t addressed : 1;
volatile uint8_t suspended : 1;
@@ -85,151 +91,150 @@ tu_static usbd_device_t _usbd_dev;
#endif
// Built-in class drivers
-tu_static usbd_class_driver_t const _usbd_driver[] =
-{
- #if CFG_TUD_CDC
- {
- DRIVER_NAME("CDC")
- .init = cdcd_init,
- .reset = cdcd_reset,
- .open = cdcd_open,
- .control_xfer_cb = cdcd_control_xfer_cb,
- .xfer_cb = cdcd_xfer_cb,
- .sof = NULL
- },
- #endif
+tu_static usbd_class_driver_t const _usbd_driver[] = {
+ #if CFG_TUD_CDC
+ {
+ DRIVER_NAME("CDC")
+ .init = cdcd_init,
+ .reset = cdcd_reset,
+ .open = cdcd_open,
+ .control_xfer_cb = cdcd_control_xfer_cb,
+ .xfer_cb = cdcd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_MSC
- {
- DRIVER_NAME("MSC")
- .init = mscd_init,
- .reset = mscd_reset,
- .open = mscd_open,
- .control_xfer_cb = mscd_control_xfer_cb,
- .xfer_cb = mscd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_MSC
+ {
+ DRIVER_NAME("MSC")
+ .init = mscd_init,
+ .reset = mscd_reset,
+ .open = mscd_open,
+ .control_xfer_cb = mscd_control_xfer_cb,
+ .xfer_cb = mscd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_HID
- {
- DRIVER_NAME("HID")
- .init = hidd_init,
- .reset = hidd_reset,
- .open = hidd_open,
- .control_xfer_cb = hidd_control_xfer_cb,
- .xfer_cb = hidd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_HID
+ {
+ DRIVER_NAME("HID")
+ .init = hidd_init,
+ .reset = hidd_reset,
+ .open = hidd_open,
+ .control_xfer_cb = hidd_control_xfer_cb,
+ .xfer_cb = hidd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_AUDIO
- {
- DRIVER_NAME("AUDIO")
- .init = audiod_init,
- .reset = audiod_reset,
- .open = audiod_open,
- .control_xfer_cb = audiod_control_xfer_cb,
- .xfer_cb = audiod_xfer_cb,
- .sof = audiod_sof_isr
- },
- #endif
+ #if CFG_TUD_AUDIO
+ {
+ DRIVER_NAME("AUDIO")
+ .init = audiod_init,
+ .reset = audiod_reset,
+ .open = audiod_open,
+ .control_xfer_cb = audiod_control_xfer_cb,
+ .xfer_cb = audiod_xfer_cb,
+ .sof = audiod_sof_isr
+ },
+ #endif
- #if CFG_TUD_VIDEO
- {
- DRIVER_NAME("VIDEO")
- .init = videod_init,
- .reset = videod_reset,
- .open = videod_open,
- .control_xfer_cb = videod_control_xfer_cb,
- .xfer_cb = videod_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_VIDEO
+ {
+ DRIVER_NAME("VIDEO")
+ .init = videod_init,
+ .reset = videod_reset,
+ .open = videod_open,
+ .control_xfer_cb = videod_control_xfer_cb,
+ .xfer_cb = videod_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_MIDI
- {
- DRIVER_NAME("MIDI")
- .init = midid_init,
- .open = midid_open,
- .reset = midid_reset,
- .control_xfer_cb = midid_control_xfer_cb,
- .xfer_cb = midid_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_MIDI
+ {
+ DRIVER_NAME("MIDI")
+ .init = midid_init,
+ .open = midid_open,
+ .reset = midid_reset,
+ .control_xfer_cb = midid_control_xfer_cb,
+ .xfer_cb = midid_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_VENDOR
- {
- DRIVER_NAME("VENDOR")
- .init = vendord_init,
- .reset = vendord_reset,
- .open = vendord_open,
- .control_xfer_cb = tud_vendor_control_xfer_cb,
- .xfer_cb = vendord_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_VENDOR
+ {
+ DRIVER_NAME("VENDOR")
+ .init = vendord_init,
+ .reset = vendord_reset,
+ .open = vendord_open,
+ .control_xfer_cb = tud_vendor_control_xfer_cb,
+ .xfer_cb = vendord_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_USBTMC
- {
- DRIVER_NAME("TMC")
- .init = usbtmcd_init_cb,
- .reset = usbtmcd_reset_cb,
- .open = usbtmcd_open_cb,
- .control_xfer_cb = usbtmcd_control_xfer_cb,
- .xfer_cb = usbtmcd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_USBTMC
+ {
+ DRIVER_NAME("TMC")
+ .init = usbtmcd_init_cb,
+ .reset = usbtmcd_reset_cb,
+ .open = usbtmcd_open_cb,
+ .control_xfer_cb = usbtmcd_control_xfer_cb,
+ .xfer_cb = usbtmcd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_DFU_RUNTIME
- {
- DRIVER_NAME("DFU-RUNTIME")
- .init = dfu_rtd_init,
- .reset = dfu_rtd_reset,
- .open = dfu_rtd_open,
- .control_xfer_cb = dfu_rtd_control_xfer_cb,
- .xfer_cb = NULL,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_DFU_RUNTIME
+ {
+ DRIVER_NAME("DFU-RUNTIME")
+ .init = dfu_rtd_init,
+ .reset = dfu_rtd_reset,
+ .open = dfu_rtd_open,
+ .control_xfer_cb = dfu_rtd_control_xfer_cb,
+ .xfer_cb = NULL,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_DFU
- {
- DRIVER_NAME("DFU")
- .init = dfu_moded_init,
- .reset = dfu_moded_reset,
- .open = dfu_moded_open,
- .control_xfer_cb = dfu_moded_control_xfer_cb,
- .xfer_cb = NULL,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_DFU
+ {
+ DRIVER_NAME("DFU")
+ .init = dfu_moded_init,
+ .reset = dfu_moded_reset,
+ .open = dfu_moded_open,
+ .control_xfer_cb = dfu_moded_control_xfer_cb,
+ .xfer_cb = NULL,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
- {
- DRIVER_NAME("NET")
- .init = netd_init,
- .reset = netd_reset,
- .open = netd_open,
- .control_xfer_cb = netd_control_xfer_cb,
- .xfer_cb = netd_xfer_cb,
- .sof = NULL,
- },
- #endif
+ #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
+ {
+ DRIVER_NAME("NET")
+ .init = netd_init,
+ .reset = netd_reset,
+ .open = netd_open,
+ .control_xfer_cb = netd_control_xfer_cb,
+ .xfer_cb = netd_xfer_cb,
+ .sof = NULL,
+ },
+ #endif
- #if CFG_TUD_BTH
- {
- DRIVER_NAME("BTH")
- .init = btd_init,
- .reset = btd_reset,
- .open = btd_open,
- .control_xfer_cb = btd_control_xfer_cb,
- .xfer_cb = btd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_BTH
+ {
+ DRIVER_NAME("BTH")
+ .init = btd_init,
+ .reset = btd_reset,
+ .open = btd_open,
+ .control_xfer_cb = btd_control_xfer_cb,
+ .xfer_cb = btd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
};
enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };
@@ -275,7 +280,7 @@ tu_static osal_queue_t _usbd_q;
TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) {
bool ret = osal_queue_send(_usbd_q, event, in_isr);
- if (tud_event_hook_cb) tud_event_hook_cb(event->rhport, event->event_id, in_isr);
+ tud_event_hook_cb(event->rhport, event->event_id, in_isr);
return ret;
}
@@ -297,27 +302,23 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
// Debug
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
-tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] =
-{
- "Invalid" ,
- "Bus Reset" ,
- "Unplugged" ,
- "SOF" ,
- "Suspend" ,
- "Resume" ,
- "Setup Received" ,
- "Xfer Complete" ,
- "Func Call"
+tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] = {
+ "Invalid",
+ "Bus Reset",
+ "Unplugged",
+ "SOF",
+ "Suspend",
+ "Resume",
+ "Setup Received",
+ "Xfer Complete",
+ "Func Call"
};
// for usbd_control to print the name of control complete driver
-void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback)
-{
- for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
- {
- usbd_class_driver_t const * driver = get_driver(i);
- if ( driver && driver->control_xfer_cb == callback )
- {
+void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback) {
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+ usbd_class_driver_t const* driver = get_driver(i);
+ if (driver && driver->control_xfer_cb == callback) {
TU_LOG_USBD(" %s control complete\r\n", driver->name);
return;
}
@@ -329,43 +330,36 @@ void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback)
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
-tusb_speed_t tud_speed_get(void)
-{
+tusb_speed_t tud_speed_get(void) {
return (tusb_speed_t) _usbd_dev.speed;
}
-bool tud_connected(void)
-{
+bool tud_connected(void) {
return _usbd_dev.connected;
}
-bool tud_mounted(void)
-{
+bool tud_mounted(void) {
return _usbd_dev.cfg_num ? true : false;
}
-bool tud_suspended(void)
-{
+bool tud_suspended(void) {
return _usbd_dev.suspended;
}
-bool tud_remote_wakeup(void)
-{
+bool tud_remote_wakeup(void) {
// only wake up host if this feature is supported and enabled and we are suspended
- TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en );
+ TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en);
dcd_remote_wakeup(_usbd_rhport);
return true;
}
-bool tud_disconnect(void)
-{
+bool tud_disconnect(void) {
TU_VERIFY(dcd_disconnect);
dcd_disconnect(_usbd_rhport);
return true;
}
-bool tud_connect(void)
-{
+bool tud_connect(void) {
TU_VERIFY(dcd_connect);
dcd_connect(_usbd_rhport);
return true;
diff --git a/src/device/usbd.h b/src/device/usbd.h
index 5456148bf..3ab6c813f 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -147,7 +147,7 @@ TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
TU_ATTR_WEAK void tud_resume_cb(void);
// Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext()
-TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
+void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
// Invoked when received control request with VENDOR TYPE
TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c
index 76d062e40..35cce1f7e 100644
--- a/src/device/usbd_control.c
+++ b/src/device/usbd_control.c
@@ -32,24 +32,32 @@
#include "tusb.h"
#include "device/usbd_pvt.h"
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+ (void) rhport;
+ (void) request;
+}
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
extern void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback);
#endif
-enum
-{
+enum {
EDPT_CTRL_OUT = 0x00,
- EDPT_CTRL_IN = 0x80
+ EDPT_CTRL_IN = 0x80
};
-typedef struct
-{
+typedef struct {
tusb_control_request_t request;
-
uint8_t* buffer;
uint16_t data_len;
uint16_t total_xferred;
-
usbd_control_xfer_cb_t complete_cb;
} usbd_control_xfer_t;
@@ -63,20 +71,18 @@ tu_static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE];
//--------------------------------------------------------------------+
// Queue ZLP status transaction
-static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const * request)
-{
+static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const* request) {
// Opposite to endpoint in Data Phase
uint8_t const ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
return usbd_edpt_xfer(rhport, ep_addr, NULL, 0);
}
// Status phase
-bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
-{
- _ctrl_xfer.request = (*request);
- _ctrl_xfer.buffer = NULL;
+bool tud_control_status(uint8_t rhport, tusb_control_request_t const* request) {
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = NULL;
_ctrl_xfer.total_xferred = 0;
- _ctrl_xfer.data_len = 0;
+ _ctrl_xfer.data_len = 0;
return _status_stage_xact(rhport, request);
}
@@ -84,16 +90,15 @@ bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
// Queue a transaction in Data Stage
// Each transaction has up to Endpoint0's max packet size.
// This function can also transfer an zero-length packet
-static bool _data_stage_xact(uint8_t rhport)
-{
- uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
+static bool _data_stage_xact(uint8_t rhport) {
+ uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred,
+ CFG_TUD_ENDPOINT0_SIZE);
uint8_t ep_addr = EDPT_CTRL_OUT;
- if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN )
- {
+ if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) {
ep_addr = EDPT_CTRL_IN;
- if ( xact_len ) {
+ if (xact_len) {
TU_VERIFY(0 == tu_memcpy_s(_usbd_ctrl_buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len));
}
}
@@ -103,29 +108,24 @@ static bool _data_stage_xact(uint8_t rhport)
// Transmit data to/from the control endpoint.
// If the request's wLength is zero, a status packet is sent instead.
-bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len)
-{
- _ctrl_xfer.request = (*request);
- _ctrl_xfer.buffer = (uint8_t*) buffer;
+bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const* request, void* buffer, uint16_t len) {
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = (uint8_t*) buffer;
_ctrl_xfer.total_xferred = 0U;
- _ctrl_xfer.data_len = tu_min16(len, request->wLength);
+ _ctrl_xfer.data_len = tu_min16(len, request->wLength);
- if (request->wLength > 0U)
- {
- if(_ctrl_xfer.data_len > 0U)
- {
+ if (request->wLength > 0U) {
+ if (_ctrl_xfer.data_len > 0U) {
TU_ASSERT(buffer);
}
// TU_LOG2(" Control total data length is %u bytes\r\n", _ctrl_xfer.data_len);
// Data stage
- TU_ASSERT( _data_stage_xact(rhport) );
- }
- else
- {
+ TU_ASSERT(_data_stage_xact(rhport));
+ } else {
// Status stage
- TU_ASSERT( _status_stage_xact(rhport, request) );
+ TU_ASSERT(_status_stage_xact(rhport, request));
}
return true;
@@ -134,49 +134,42 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
//--------------------------------------------------------------------+
// USBD API
//--------------------------------------------------------------------+
-
void usbd_control_reset(void);
-void usbd_control_set_request(tusb_control_request_t const *request);
-void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp );
-bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void usbd_control_set_request(tusb_control_request_t const* request);
+void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp);
+bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void usbd_control_reset(void)
-{
+void usbd_control_reset(void) {
tu_varclr(&_ctrl_xfer);
}
// Set complete callback
-void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp )
-{
+void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) {
_ctrl_xfer.complete_cb = fp;
}
// for dcd_set_address where DCD is responsible for status response
-void usbd_control_set_request(tusb_control_request_t const *request)
-{
- _ctrl_xfer.request = (*request);
- _ctrl_xfer.buffer = NULL;
+void usbd_control_set_request(tusb_control_request_t const* request) {
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = NULL;
_ctrl_xfer.total_xferred = 0;
- _ctrl_xfer.data_len = 0;
+ _ctrl_xfer.data_len = 0;
}
// callback when a transaction complete on
// - DATA stage of control endpoint or
// - Status stage
-bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
// Endpoint Address is opposite to direction bit, this is Status Stage complete event
- if ( tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction )
- {
+ if (tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction) {
TU_ASSERT(0 == xferred_bytes);
// invoke optional dcd hook if available
- if (dcd_edpt0_status_complete) dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
+ dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
- if (_ctrl_xfer.complete_cb)
- {
+ if (_ctrl_xfer.complete_cb) {
// TODO refactor with usbd_driver_print_control_complete_name
_ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request);
}
@@ -184,8 +177,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
return true;
}
- if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
- {
+ if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) {
TU_VERIFY(_ctrl_xfer.buffer);
memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes);
TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _usbd_ctrl_buf, xferred_bytes, 2);
@@ -196,15 +188,14 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
// Data Stage is complete when all request's length are transferred or
// a short packet is sent including zero-length packet.
- if ( (_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE) )
- {
+ if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) ||
+ (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE)) {
// DATA stage is complete
bool is_ok = true;
// invoke complete callback if set
// callback can still stall control in status phase e.g out data does not make sense
- if ( _ctrl_xfer.complete_cb )
- {
+ if (_ctrl_xfer.complete_cb) {
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb);
#endif
@@ -212,21 +203,17 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request);
}
- if ( is_ok )
- {
+ if (is_ok) {
// Send status
- TU_ASSERT( _status_stage_xact(rhport, &_ctrl_xfer.request) );
- }else
- {
+ TU_ASSERT(_status_stage_xact(rhport, &_ctrl_xfer.request));
+ } else {
// Stall both IN and OUT control endpoint
dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
dcd_edpt_stall(rhport, EDPT_CTRL_IN);
}
- }
- else
- {
+ } else {
// More data to transfer
- TU_ASSERT( _data_stage_xact(rhport) );
+ TU_ASSERT(_data_stage_xact(rhport));
}
return true;
diff --git a/src/host/hub.c b/src/host/hub.c
index 32f5e0ac7..3bac18698 100644
--- a/src/host/hub.c
+++ b/src/host/hub.c
@@ -435,9 +435,12 @@ static void hub_port_get_status_complete (tuh_xfer_t* xfer)
// Other changes are: L1 state
// TODO clear change
- // prepare for next hub status
- // TODO continue with status_change, or maybe we can do it again with status
- hub_edpt_status_xfer(daddr);
+ else
+ {
+ // prepare for next hub status
+ // TODO continue with status_change, or maybe we can do it again with status
+ hub_edpt_status_xfer(daddr);
+ }
}
}
diff --git a/src/host/hub.c.bak b/src/host/hub.c.bak
new file mode 100644
index 000000000..9f1de32d1
--- /dev/null
+++ b/src/host/hub.c.bak
@@ -0,0 +1,497 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (CFG_TUH_ENABLED && CFG_TUH_HUB)
+
+#include "hcd.h"
+#include "usbh.h"
+#include "usbh_pvt.h"
+#include "hub.h"
+
+// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message
+#define HUB_DEBUG 1
+#define TU_LOG_DRV(...) TU_LOG(HUB_DEBUG, __VA_ARGS__)
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t port_count;
+
+ CFG_TUH_MEM_ALIGN uint8_t status_change;
+ CFG_TUH_MEM_ALIGN hub_port_status_response_t port_status;
+ CFG_TUH_MEM_ALIGN hub_status_response_t hub_status;
+} hub_interface_t;
+
+CFG_TUH_MEM_SECTION static hub_interface_t hub_data[CFG_TUH_HUB];
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t _hub_buffer[sizeof(descriptor_hub_desc_t)];
+
+TU_ATTR_ALWAYS_INLINE
+static inline hub_interface_t* get_itf(uint8_t dev_addr)
+{
+ return &hub_data[dev_addr-1-CFG_TUH_DEVICE_MAX];
+}
+
+#if CFG_TUSB_DEBUG >= 1
+static char const* const _hub_feature_str[] =
+{
+ [HUB_FEATURE_PORT_CONNECTION ] = "PORT_CONNECTION",
+ [HUB_FEATURE_PORT_ENABLE ] = "PORT_ENABLE",
+ [HUB_FEATURE_PORT_SUSPEND ] = "PORT_SUSPEND",
+ [HUB_FEATURE_PORT_OVER_CURRENT ] = "PORT_OVER_CURRENT",
+ [HUB_FEATURE_PORT_RESET ] = "PORT_RESET",
+ [HUB_FEATURE_PORT_POWER ] = "PORT_POWER",
+ [HUB_FEATURE_PORT_LOW_SPEED ] = "PORT_LOW_SPEED",
+ [HUB_FEATURE_PORT_CONNECTION_CHANGE ] = "PORT_CONNECTION_CHANGE",
+ [HUB_FEATURE_PORT_ENABLE_CHANGE ] = "PORT_ENABLE_CHANGE",
+ [HUB_FEATURE_PORT_SUSPEND_CHANGE ] = "PORT_SUSPEND_CHANGE",
+ [HUB_FEATURE_PORT_OVER_CURRENT_CHANGE ] = "PORT_OVER_CURRENT_CHANGE",
+ [HUB_FEATURE_PORT_RESET_CHANGE ] = "PORT_RESET_CHANGE",
+ [HUB_FEATURE_PORT_TEST ] = "PORT_TEST",
+ [HUB_FEATURE_PORT_INDICATOR ] = "PORT_INDICATOR",
+};
+#endif
+
+//--------------------------------------------------------------------+
+// HUB
+//--------------------------------------------------------------------+
+bool hub_port_clear_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HUB_REQUEST_CLEAR_FEATURE,
+ .wValue = feature,
+ .wIndex = hub_port,
+ .wLength = 0
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = hub_addr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ TU_LOG2("HUB Clear Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
+ TU_ASSERT( tuh_control_xfer(&xfer) );
+ return true;
+}
+
+bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HUB_REQUEST_SET_FEATURE,
+ .wValue = feature,
+ .wIndex = hub_port,
+ .wLength = 0
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = hub_addr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ TU_LOG2("HUB Set Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
+ TU_ASSERT( tuh_control_xfer(&xfer) );
+ return true;
+}
+
+bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = HUB_REQUEST_GET_STATUS,
+ .wValue = 0,
+ .wIndex = hub_port,
+ .wLength = 4
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = hub_addr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = resp,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ TU_LOG2("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port);
+ TU_VERIFY( tuh_control_xfer(&xfer) );
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// CLASS-USBH API (don't require to verify parameters)
+//--------------------------------------------------------------------+
+void hub_init(void)
+{
+ tu_memclr(hub_data, sizeof(hub_data));
+}
+
+bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
+{
+ (void) rhport;
+
+ TU_VERIFY(TUSB_CLASS_HUB == itf_desc->bInterfaceClass &&
+ 0 == itf_desc->bInterfaceSubClass);
+
+ // hub driver does not support multiple TT yet
+ TU_VERIFY(itf_desc->bInterfaceProtocol <= 1);
+
+ // msc driver length is fixed
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t);
+ TU_ASSERT(drv_len <= max_len);
+
+ //------------- Interrupt Status endpoint -------------//
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
+ TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
+
+ TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep));
+
+ hub_interface_t* p_hub = get_itf(dev_addr);
+
+ p_hub->itf_num = itf_desc->bInterfaceNumber;
+ p_hub->ep_in = desc_ep->bEndpointAddress;
+
+ return true;
+}
+
+void hub_close(uint8_t dev_addr)
+{
+ TU_VERIFY(dev_addr > CFG_TUH_DEVICE_MAX, );
+ hub_interface_t* p_hub = get_itf(dev_addr);
+
+ if (p_hub->ep_in) {
+ TU_LOG_DRV(" HUB close addr = %d\r\n", dev_addr);
+ tu_memclr(p_hub, sizeof( hub_interface_t));
+ }
+}
+
+bool hub_edpt_status_xfer(uint8_t dev_addr)
+{
+ hub_interface_t* hub_itf = get_itf(dev_addr);
+ return usbh_edpt_xfer(dev_addr, hub_itf->ep_in, &hub_itf->status_change, 1);
+}
+
+
+//--------------------------------------------------------------------+
+// Set Configure
+//--------------------------------------------------------------------+
+
+static void config_set_port_power (tuh_xfer_t* xfer);
+static void config_port_power_complete (tuh_xfer_t* xfer);
+
+bool hub_set_config(uint8_t dev_addr, uint8_t itf_num)
+{
+ hub_interface_t* p_hub = get_itf(dev_addr);
+ TU_ASSERT(itf_num == p_hub->itf_num);
+
+ // Get Hub Descriptor
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = HUB_REQUEST_GET_DESCRIPTOR,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = sizeof(descriptor_hub_desc_t)
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = dev_addr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = _hub_buffer,
+ .complete_cb = config_set_port_power,
+ .user_data = 0
+ };
+
+ TU_ASSERT( tuh_control_xfer(&xfer) );
+
+ return true;
+}
+
+static void config_set_port_power (tuh_xfer_t* xfer)
+{
+ TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, );
+
+ uint8_t const daddr = xfer->daddr;
+ hub_interface_t* p_hub = get_itf(daddr);
+
+ // only use number of ports in hub descriptor
+ descriptor_hub_desc_t const* desc_hub = (descriptor_hub_desc_t const*) _hub_buffer;
+ p_hub->port_count = desc_hub->bNbrPorts;
+
+ // May need to GET_STATUS
+
+ // Set Port Power to be able to detect connection, starting with port 1
+ uint8_t const hub_port = 1;
+ hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0);
+}
+
+static void config_port_power_complete (tuh_xfer_t* xfer)
+{
+ TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, );
+
+ uint8_t const daddr = xfer->daddr;
+ hub_interface_t* p_hub = get_itf(daddr);
+
+ if (xfer->setup->wIndex == p_hub->port_count)
+ {
+ // All ports are power -> queue notification status endpoint and
+ // complete the SET CONFIGURATION
+ TU_ASSERT( usbh_edpt_xfer(daddr, p_hub->ep_in, &p_hub->status_change, 1), );
+
+ usbh_driver_set_config_complete(daddr, p_hub->itf_num);
+ }else
+ {
+ // power next port
+ uint8_t const hub_port = (uint8_t) (xfer->setup->wIndex + 1);
+ hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0);
+ }
+}
+
+//--------------------------------------------------------------------+
+// Connection Changes
+//--------------------------------------------------------------------+
+
+static void hub_port_get_status_complete (tuh_xfer_t* xfer);
+static void hub_get_status_complete (tuh_xfer_t* xfer);
+static void connection_clear_conn_change_complete (tuh_xfer_t* xfer);
+static void connection_port_reset_complete (tuh_xfer_t* xfer);
+
+// callback as response of interrupt endpoint polling
+bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void) xferred_bytes; // TODO can be more than 1 for hub with lots of ports
+ (void) ep_addr;
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+
+ hub_interface_t* p_hub = get_itf(dev_addr);
+
+ uint8_t const status_change = p_hub->status_change;
+ TU_LOG2(" Hub Status Change = 0x%02X\r\n", status_change);
+
+ if ( status_change == 0 ) {
+ // The status change event was neither for the hub, nor for any of its ports.
+ // This shouldn't happen, but it does with some devices.
+ // Initiate the next interrupt poll here.
+ return hub_edpt_status_xfer(dev_addr);
+ }
+
+ if (tu_bit_test(status_change, 0)) {
+ // Hub bit 0 is for the hub device events
+ if (hub_port_get_status(dev_addr, 0, &p_hub->hub_status, hub_get_status_complete, 0) == false) {
+ //Hub status control transfer failed, retry
+ hub_edpt_status_xfer(dev_addr);
+ }
+ }
+ else {
+ // Hub bits 1 to n are hub port events
+ for (uint8_t port=1; port <= p_hub->port_count; port++) {
+ if ( tu_bit_test(status_change, port) ) {
+ if (hub_port_get_status(dev_addr, port, &p_hub->port_status, hub_port_get_status_complete, 0) == false) {
+ //Hub status control transfer failed, retry
+ hub_edpt_status_xfer(dev_addr);
+ }
+ break;
+ }
+ }
+ }
+
+ // NOTE: next status transfer is queued by usbh.c after handling this request
+ return true;
+}
+
+static void hub_clear_feature_complete_stub(tuh_xfer_t* xfer)
+{
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
+ hub_edpt_status_xfer(xfer->daddr);
+}
+
+static void hub_get_status_complete (tuh_xfer_t* xfer)
+{
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
+
+ uint8_t const daddr = xfer->daddr;
+ hub_interface_t* p_hub = get_itf(daddr);
+ uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ TU_ASSERT(port_num == 0 , );
+
+ TU_LOG2("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, p_hub->hub_status.change.value);
+
+ if (p_hub->hub_status.change.local_power_source)
+ {
+ TU_LOG2("HUB Local Power Change, addr = %u\r\n", daddr);
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ else if (p_hub->hub_status.change.over_current)
+ {
+ TU_LOG1("HUB Over Current, addr = %u\r\n", daddr);
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+}
+
+static void hub_port_get_status_complete (tuh_xfer_t* xfer)
+{
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
+
+ uint8_t const daddr = xfer->daddr;
+ hub_interface_t* p_hub = get_itf(daddr);
+ uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+
+ // Connection change
+ if (p_hub->port_status.change.connection)
+ {
+ // Port is powered and enabled
+ //TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
+
+ // Acknowledge Port Connection Change
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0);
+ }else
+ {
+ // Clear other port status change interrupts. TODO Not currently handled - just cleared.
+ if (p_hub->port_status.change.port_enable)
+ {
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ else if (p_hub->port_status.change.suspend)
+ {
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ else if (p_hub->port_status.change.over_current)
+ {
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ else if (p_hub->port_status.change.reset)
+ {
+ hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE, hub_clear_feature_complete_stub, 0);
+ }
+ // Other changes are: L1 state
+ // TODO clear change
+
+ // prepare for next hub status
+ // TODO continue with status_change, or maybe we can do it again with status
+ hub_edpt_status_xfer(daddr);
+ }
+}
+
+static void connection_clear_conn_change_complete (tuh_xfer_t* xfer)
+{
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
+
+ uint8_t const daddr = xfer->daddr;
+ hub_interface_t* p_hub = get_itf(daddr);
+ uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+
+ if ( p_hub->port_status.status.connection )
+ {
+ // Reset port if attach event
+ hub_port_reset(daddr, port_num, connection_port_reset_complete, 0);
+ }else
+ {
+ // submit detach event
+ hcd_event_t event =
+ {
+ .rhport = usbh_get_rhport(daddr),
+ .event_id = HCD_EVENT_DEVICE_REMOVE,
+ .connection =
+ {
+ .hub_addr = daddr,
+ .hub_port = port_num
+ }
+ };
+
+ hcd_event_handler(&event, false);
+ }
+}
+
+static void connection_port_reset_complete (tuh_xfer_t* xfer)
+{
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
+
+ uint8_t const daddr = xfer->daddr;
+ // hub_interface_t* p_hub = get_itf(daddr);
+ uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+
+ // submit attach event
+ hcd_event_t event =
+ {
+ .rhport = usbh_get_rhport(daddr),
+ .event_id = HCD_EVENT_DEVICE_ATTACH,
+ .connection =
+ {
+ .hub_addr = daddr,
+ .hub_port = port_num
+ }
+ };
+
+ hcd_event_handler(&event, false);
+}
+
+#endif
diff --git a/src/host/usbh.c b/src/host/usbh.c
index 4d30d9f81..dfe6ddb42 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -36,7 +36,6 @@
//--------------------------------------------------------------------+
// USBH Configuration
//--------------------------------------------------------------------+
-
#ifndef CFG_TUH_TASK_QUEUE_SZ
#define CFG_TUH_TASK_QUEUE_SZ 16
#endif
@@ -45,12 +44,19 @@
#define CFG_TUH_INTERFACE_MAX 8
#endif
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
+ (void) rhport;
+ (void) eventid;
+ (void) in_isr;
+}
+
//--------------------------------------------------------------------+
// USBH-HCD common data structure
//--------------------------------------------------------------------+
-
-typedef struct
-{
+typedef struct {
// port
uint8_t rhport;
uint8_t hub_addr;
@@ -112,60 +118,58 @@ typedef struct {
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-
#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
#define DRIVER_NAME(_name) .name = _name,
#else
#define DRIVER_NAME(_name)
#endif
-static usbh_class_driver_t const usbh_class_drivers[] =
-{
- #if CFG_TUH_CDC
+static usbh_class_driver_t const usbh_class_drivers[] = {
+ #if CFG_TUH_CDC
{
- DRIVER_NAME("CDC")
- .init = cdch_init,
- .open = cdch_open,
- .set_config = cdch_set_config,
- .xfer_cb = cdch_xfer_cb,
- .close = cdch_close
+ DRIVER_NAME("CDC")
+ .init = cdch_init,
+ .open = cdch_open,
+ .set_config = cdch_set_config,
+ .xfer_cb = cdch_xfer_cb,
+ .close = cdch_close
},
- #endif
+ #endif
- #if CFG_TUH_MSC
+ #if CFG_TUH_MSC
{
- DRIVER_NAME("MSC")
- .init = msch_init,
- .open = msch_open,
- .set_config = msch_set_config,
- .xfer_cb = msch_xfer_cb,
- .close = msch_close
+ DRIVER_NAME("MSC")
+ .init = msch_init,
+ .open = msch_open,
+ .set_config = msch_set_config,
+ .xfer_cb = msch_xfer_cb,
+ .close = msch_close
},
- #endif
+ #endif
- #if CFG_TUH_HID
+ #if CFG_TUH_HID
{
- DRIVER_NAME("HID")
- .init = hidh_init,
- .open = hidh_open,
- .set_config = hidh_set_config,
- .xfer_cb = hidh_xfer_cb,
- .close = hidh_close
+ DRIVER_NAME("HID")
+ .init = hidh_init,
+ .open = hidh_open,
+ .set_config = hidh_set_config,
+ .xfer_cb = hidh_xfer_cb,
+ .close = hidh_close
},
- #endif
+ #endif
- #if CFG_TUH_HUB
+ #if CFG_TUH_HUB
{
- DRIVER_NAME("HUB")
- .init = hub_init,
- .open = hub_open,
- .set_config = hub_set_config,
- .xfer_cb = hub_xfer_cb,
- .close = hub_close
+ DRIVER_NAME("HUB")
+ .init = hub_init,
+ .open = hub_open,
+ .set_config = hub_set_config,
+ .xfer_cb = hub_xfer_cb,
+ .close = hub_close
},
- #endif
+ #endif
- #if CFG_TUH_VENDOR
+ #if CFG_TUH_VENDOR
{
DRIVER_NAME("VENDOR")
.init = cush_init,
@@ -173,7 +177,7 @@ static usbh_class_driver_t const usbh_class_drivers[] =
.xfer_cb = cush_isr,
.close = cush_close
}
- #endif
+ #endif
};
enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) };
@@ -233,8 +237,7 @@ static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE];
// Control transfers: since most controllers do not support multiple control transfers
// on multiple devices concurrently and control transfers are not used much except for
// enumeration, we will only execute control transfers one at a time.
-CFG_TUH_MEM_SECTION struct
-{
+CFG_TUH_MEM_SECTION struct {
CFG_TUH_MEM_ALIGN tusb_control_request_t request;
uint8_t* buffer;
tuh_xfer_cb_t complete_cb;
@@ -268,7 +271,7 @@ TU_ATTR_WEAK void osal_task_delay(uint32_t msec) {
TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
bool ret = osal_queue_send(_usbh_q, event, in_isr);
- if (tuh_event_hook_cb) tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
+ tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
return ret;
}
@@ -367,17 +370,14 @@ bool tuh_init(uint8_t controller_id) {
tu_memclr(_usbh_devices, sizeof(_usbh_devices));
tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
- for(uint8_t i=0; iname);
driver->init();
}
@@ -456,7 +456,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
#if CFG_TUH_HUB
// TODO remove
- if ( event.connection.hub_addr != 0) {
+ if ( event.connection.hub_addr != 0 && event.connection.hub_port != 0) {
// done with hub, waiting for next data on status pipe
(void) hub_edpt_status_xfer( event.connection.hub_addr );
}
@@ -545,8 +545,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
// Control transfer
//--------------------------------------------------------------------+
-static void _control_blocking_complete_cb(tuh_xfer_t* xfer)
-{
+static void _control_blocking_complete_cb(tuh_xfer_t* xfer) {
// update result
*((xfer_result_t*) xfer->user_data) = xfer->result;
}
@@ -625,21 +624,18 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
return true;
}
-TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage)
-{
+TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage) {
(void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
_ctrl_xfer.stage = stage;
(void) osal_mutex_unlock(_usbh_mutex);
}
-static void _xfer_complete(uint8_t daddr, xfer_result_t result)
-{
+static void _xfer_complete(uint8_t daddr, xfer_result_t result) {
TU_LOG_USBH("\r\n");
// duplicate xfer since user can execute control transfer within callback
tusb_control_request_t const request = _ctrl_xfer.request;
- tuh_xfer_t xfer_temp =
- {
+ tuh_xfer_t xfer_temp = {
.daddr = daddr,
.ep_addr = 0,
.result = result,
@@ -652,8 +648,7 @@ static void _xfer_complete(uint8_t daddr, xfer_result_t result)
_set_control_xfer_stage(CONTROL_STAGE_IDLE);
- if (xfer_temp.complete_cb)
- {
+ if (xfer_temp.complete_cb) {
xfer_temp.complete_cb(&xfer_temp);
}
}
@@ -710,17 +705,16 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
//
//--------------------------------------------------------------------+
-bool tuh_edpt_xfer(tuh_xfer_t* xfer)
-{
- uint8_t const daddr = xfer->daddr;
+bool tuh_edpt_xfer(tuh_xfer_t* xfer) {
+ uint8_t const daddr = xfer->daddr;
uint8_t const ep_addr = xfer->ep_addr;
TU_VERIFY(daddr && ep_addr);
TU_VERIFY(usbh_edpt_claim(daddr, ep_addr));
- if ( !usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen, xfer->complete_cb, xfer->user_data) )
- {
+ if (!usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen,
+ xfer->complete_cb, xfer->user_data)) {
usbh_edpt_release(daddr, ep_addr);
return false;
}
diff --git a/src/host/usbh.c.bak b/src/host/usbh.c.bak
new file mode 100644
index 000000000..10e55dc32
--- /dev/null
+++ b/src/host/usbh.c.bak
@@ -0,0 +1,1773 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED
+
+#include "host/hcd.h"
+#include "tusb.h"
+#include "host/usbh_pvt.h"
+#include "hub.h"
+
+//--------------------------------------------------------------------+
+// USBH Configuration
+//--------------------------------------------------------------------+
+
+#ifndef CFG_TUH_TASK_QUEUE_SZ
+ #define CFG_TUH_TASK_QUEUE_SZ 16
+#endif
+
+#ifndef CFG_TUH_INTERFACE_MAX
+ #define CFG_TUH_INTERFACE_MAX 8
+#endif
+
+//--------------------------------------------------------------------+
+// USBH-HCD common data structure
+//--------------------------------------------------------------------+
+
+typedef struct
+{
+ // port
+ uint8_t rhport;
+ uint8_t hub_addr;
+ uint8_t hub_port;
+
+ struct TU_ATTR_PACKED {
+ uint8_t speed : 4; // packed speed to save footprint
+ volatile uint8_t enumerating : 1; // enumeration is in progress, false if not connected or all interfaces are configured
+ uint8_t TU_RESERVED : 3;
+ };
+} usbh_dev0_t;
+
+typedef struct {
+ // port, must be same layout as usbh_dev0_t
+ uint8_t rhport;
+ uint8_t hub_addr;
+ uint8_t hub_port;
+ uint8_t speed;
+
+ // Device State
+ struct TU_ATTR_PACKED {
+ volatile uint8_t connected : 1; // After 1st transfer
+ volatile uint8_t addressed : 1; // After SET_ADDR
+ volatile uint8_t configured : 1; // After SET_CONFIG and all drivers are configured
+ volatile uint8_t suspended : 1; // Bus suspended
+
+ // volatile uint8_t removing : 1; // Physically disconnected, waiting to be processed by usbh
+ };
+
+ // Device Descriptor
+ uint8_t ep0_size;
+
+ uint16_t vid;
+ uint16_t pid;
+
+ uint8_t i_manufacturer;
+ uint8_t i_product;
+ uint8_t i_serial;
+
+ // Configuration Descriptor
+ // uint8_t interface_count; // bNumInterfaces alias
+
+ // Endpoint & Interface
+ uint8_t itf2drv[CFG_TUH_INTERFACE_MAX]; // map interface number to driver (0xff is invalid)
+ uint8_t ep2drv[CFG_TUH_ENDPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each
+
+ tu_edpt_state_t ep_status[CFG_TUH_ENDPOINT_MAX][2];
+
+#if CFG_TUH_API_EDPT_XFER
+ // TODO array can be CFG_TUH_ENDPOINT_MAX-1
+ struct {
+ tuh_xfer_cb_t complete_cb;
+ uintptr_t user_data;
+ }ep_callback[CFG_TUH_ENDPOINT_MAX][2];
+#endif
+
+} usbh_device_t;
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
+ #define DRIVER_NAME(_name) .name = _name,
+#else
+ #define DRIVER_NAME(_name)
+#endif
+
+static usbh_class_driver_t const usbh_class_drivers[] =
+{
+ #if CFG_TUH_CDC
+ {
+ DRIVER_NAME("CDC")
+ .init = cdch_init,
+ .open = cdch_open,
+ .set_config = cdch_set_config,
+ .xfer_cb = cdch_xfer_cb,
+ .close = cdch_close
+ },
+ #endif
+
+ #if CFG_TUH_MSC
+ {
+ DRIVER_NAME("MSC")
+ .init = msch_init,
+ .open = msch_open,
+ .set_config = msch_set_config,
+ .xfer_cb = msch_xfer_cb,
+ .close = msch_close
+ },
+ #endif
+
+ #if CFG_TUH_HID
+ {
+ DRIVER_NAME("HID")
+ .init = hidh_init,
+ .open = hidh_open,
+ .set_config = hidh_set_config,
+ .xfer_cb = hidh_xfer_cb,
+ .close = hidh_close
+ },
+ #endif
+
+ #if CFG_TUH_HUB
+ {
+ DRIVER_NAME("HUB")
+ .init = hub_init,
+ .open = hub_open,
+ .set_config = hub_set_config,
+ .xfer_cb = hub_xfer_cb,
+ .close = hub_close
+ },
+ #endif
+
+ #if CFG_TUH_VENDOR
+ {
+ DRIVER_NAME("VENDOR")
+ .init = cush_init,
+ .open = cush_open_subtask,
+ .xfer_cb = cush_isr,
+ .close = cush_close
+ }
+ #endif
+};
+
+enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) };
+enum { CONFIG_NUM = 1 }; // default to use configuration 1
+
+// Additional class drivers implemented by application
+tu_static usbh_class_driver_t const * _app_driver = NULL;
+tu_static uint8_t _app_driver_count = 0;
+
+#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT)
+
+static inline usbh_class_driver_t const *get_driver(uint8_t drv_id) {
+ usbh_class_driver_t const *driver = NULL;
+
+ if ( drv_id < _app_driver_count ) {
+ driver = &_app_driver[drv_id];
+ } else if ( drv_id < TOTAL_DRIVER_COUNT && BUILTIN_DRIVER_COUNT > 0) {
+ driver = &usbh_class_drivers[drv_id - _app_driver_count];
+ }
+
+ return driver;
+}
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+
+// sum of end device + hub
+#define TOTAL_DEVICES (CFG_TUH_DEVICE_MAX + CFG_TUH_HUB)
+
+static uint8_t _usbh_controller = TUSB_INDEX_INVALID_8;
+
+// Device with address = 0 for enumeration
+static usbh_dev0_t _dev0;
+
+// all devices excluding zero-address
+// hub address start from CFG_TUH_DEVICE_MAX+1
+// TODO: hub can has its own simpler struct to save memory
+static usbh_device_t _usbh_devices[TOTAL_DEVICES];
+
+// Mutex for claiming endpoint
+#if OSAL_MUTEX_REQUIRED
+ static osal_mutex_def_t _usbh_mutexdef;
+ static osal_mutex_t _usbh_mutex;
+#else
+ #define _usbh_mutex NULL
+#endif
+
+// Event queue
+// usbh_int_set is used as mutex in OS NONE config
+OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
+static osal_queue_t _usbh_q;
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN
+static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE];
+
+// Control transfers: since most controllers do not support multiple control transfers
+// on multiple devices concurrently and control transfers are not used much except for
+// enumeration, we will only execute control transfers one at a time.
+CFG_TUH_MEM_SECTION struct
+{
+ CFG_TUH_MEM_ALIGN tusb_control_request_t request;
+ uint8_t* buffer;
+ tuh_xfer_cb_t complete_cb;
+ uintptr_t user_data;
+
+ uint8_t daddr;
+ volatile uint8_t stage;
+ volatile uint16_t actual_len;
+}_ctrl_xfer;
+
+//------------- Helper Function -------------//
+
+TU_ATTR_ALWAYS_INLINE static inline usbh_device_t* get_device(uint8_t dev_addr) {
+ TU_VERIFY(dev_addr > 0 && dev_addr <= TOTAL_DEVICES, NULL);
+ return &_usbh_devices[dev_addr-1];
+}
+
+static bool enum_new_device(hcd_event_t* event);
+static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port);
+static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size);
+static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+// TODO rework time-related function later
+// weak and overridable
+TU_ATTR_WEAK void osal_task_delay(uint32_t msec) {
+ const uint32_t start = hcd_frame_number(_usbh_controller);
+ while ( ( hcd_frame_number(_usbh_controller) - start ) < msec ) {}
+}
+#endif
+
+TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
+ bool ret = osal_queue_send(_usbh_q, event, in_isr);
+ if (tuh_event_hook_cb) tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
+ return ret;
+}
+
+//--------------------------------------------------------------------+
+// Device API
+//--------------------------------------------------------------------+
+
+bool tuh_mounted(uint8_t dev_addr) {
+ usbh_device_t *dev = get_device(dev_addr);
+ TU_VERIFY(dev);
+ return dev->configured;
+}
+
+bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t *vid, uint16_t *pid) {
+ *vid = *pid = 0;
+
+ usbh_device_t const *dev = get_device(dev_addr);
+<<<<<<< HEAD
+ TU_LOG_USBH("vid = 0x%X pid = 0x%X\r\n", dev->vid, dev->pid);
+=======
+ TU_LOG_USBH("[%u] vid = 0x%X pid = 0x%X\r\n", dev_addr, dev->vid, dev->pid);
+>>>>>>> vid_pid_log
+ TU_VERIFY(dev && dev->addressed && dev->vid != 0);
+
+ *vid = dev->vid;
+ *pid = dev->pid;
+
+ return true;
+}
+
+tusb_speed_t tuh_speed_get(uint8_t dev_addr) {
+ usbh_device_t *dev = get_device(dev_addr);
+ return (tusb_speed_t) (dev ? get_device(dev_addr)->speed : _dev0.speed);
+}
+
+bool tuh_rhport_is_active(uint8_t rhport) {
+ return _usbh_controller == rhport;
+}
+
+bool tuh_rhport_reset_bus(uint8_t rhport, bool active) {
+ TU_VERIFY(tuh_rhport_is_active(rhport));
+ if ( active ) {
+ hcd_port_reset(rhport);
+ } else {
+ hcd_port_reset_end(rhport);
+ }
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// PUBLIC API (Parameter Verification is required)
+//--------------------------------------------------------------------+
+
+bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param) {
+ if ( hcd_configure ) {
+ return hcd_configure(rhport, cfg_id, cfg_param);
+ } else {
+ return false;
+ }
+}
+
+static void clear_device(usbh_device_t* dev) {
+ tu_memclr(dev, sizeof(usbh_device_t));
+ memset(dev->itf2drv, TUSB_INDEX_INVALID_8, sizeof(dev->itf2drv)); // invalid mapping
+ memset(dev->ep2drv , TUSB_INDEX_INVALID_8, sizeof(dev->ep2drv )); // invalid mapping
+}
+
+bool tuh_inited(void) {
+ return _usbh_controller != TUSB_INDEX_INVALID_8;
+}
+
+bool tuh_init(uint8_t controller_id) {
+ // skip if already initialized
+ if ( tuh_inited() ) return true;
+
+ TU_LOG_USBH("USBH init on controller %u\r\n", controller_id);
+ TU_LOG_INT(CFG_TUH_LOG_LEVEL, sizeof(usbh_device_t));
+ TU_LOG_INT(CFG_TUH_LOG_LEVEL, sizeof(hcd_event_t));
+ TU_LOG_INT(CFG_TUH_LOG_LEVEL, sizeof(_ctrl_xfer));
+ TU_LOG_INT(CFG_TUH_LOG_LEVEL, sizeof(tuh_xfer_t));
+ TU_LOG_INT(CFG_TUH_LOG_LEVEL, sizeof(tu_fifo_t));
+ TU_LOG_INT(CFG_TUH_LOG_LEVEL, sizeof(tu_edpt_stream_t));
+
+ // Event queue
+ _usbh_q = osal_queue_create( &_usbh_qdef );
+ TU_ASSERT(_usbh_q != NULL);
+
+#if OSAL_MUTEX_REQUIRED
+ // Init mutex
+ _usbh_mutex = osal_mutex_create(&_usbh_mutexdef);
+ TU_ASSERT(_usbh_mutex);
+#endif
+
+ // Get application driver if available
+ if ( usbh_app_driver_get_cb ) {
+ _app_driver = usbh_app_driver_get_cb(&_app_driver_count);
+ }
+
+ // Device
+ tu_memclr(&_dev0, sizeof(_dev0));
+ tu_memclr(_usbh_devices, sizeof(_usbh_devices));
+ tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
+
+ for(uint8_t i=0; iname);
+ driver->init();
+ }
+ }
+
+ _usbh_controller = controller_id;;
+
+ TU_ASSERT(hcd_init(controller_id));
+ hcd_int_enable(controller_id);
+
+ return true;
+}
+
+bool tuh_task_event_ready(void) {
+ // Skip if stack is not initialized
+ if ( !tuh_inited() ) return false;
+
+ return !osal_queue_empty(_usbh_q);
+}
+
+/* USB Host Driver task
+ * This top level thread manages all host controller event and delegates events to class-specific drivers.
+ * This should be called periodically within the mainloop or rtos thread.
+ *
+ @code
+ int main(void)
+ {
+ application_init();
+ tusb_init();
+
+ while(1) // the mainloop
+ {
+ application_code();
+ tuh_task(); // tinyusb host task
+ }
+ }
+ @endcode
+ */
+void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
+ (void) in_isr; // not implemented yet
+
+ // Skip if stack is not initialized
+ if ( !tuh_inited() ) return;
+
+ // Loop until there is no more events in the queue
+ while (1)
+ {
+ hcd_event_t event;
+ if ( !osal_queue_receive(_usbh_q, &event, timeout_ms) ) return;
+
+ switch (event.event_id)
+ {
+ case HCD_EVENT_DEVICE_ATTACH:
+ // due to the shared _usbh_ctrl_buf, we must complete enumerating one device before enumerating another one.
+ // TODO better to have an separated queue for newly attached devices
+ if ( _dev0.enumerating ) {
+ TU_LOG_USBH("[%u:] USBH Defer Attach until current enumeration complete\r\n", event.rhport);
+
+ bool is_empty = osal_queue_empty(_usbh_q);
+ queue_event(&event, in_isr);
+
+ if (is_empty) {
+ // Exit if this is the only event in the queue, otherwise we may loop forever
+ return;
+ }
+ }else {
+ TU_LOG_USBH("[%u:] USBH DEVICE ATTACH\r\n", event.rhport);
+ _dev0.enumerating = 1;
+ enum_new_device(&event);
+ }
+ break;
+
+ case HCD_EVENT_DEVICE_REMOVE:
+ TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
+ process_removing_device(event.rhport, event.connection.hub_addr, event.connection.hub_port);
+
+ #if CFG_TUH_HUB
+ // TODO remove
+ if ( event.connection.hub_addr != 0) {
+ // done with hub, waiting for next data on status pipe
+ (void) hub_edpt_status_xfer( event.connection.hub_addr );
+ }
+ #endif
+ break;
+
+ case HCD_EVENT_XFER_COMPLETE:
+ {
+ uint8_t const ep_addr = event.xfer_complete.ep_addr;
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = tu_edpt_dir(ep_addr);
+
+ TU_LOG_USBH("on EP %02X with %u bytes: %s\r\n", ep_addr, (unsigned int) event.xfer_complete.len,
+ tu_str_xfer_result[event.xfer_complete.result]);
+
+ if (event.dev_addr == 0) {
+ // device 0 only has control endpoint
+ TU_ASSERT(epnum == 0, );
+ usbh_control_xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);
+ } else {
+ usbh_device_t* dev = get_device(event.dev_addr);
+ TU_VERIFY(dev && dev->connected, );
+
+ dev->ep_status[epnum][ep_dir].busy = 0;
+ dev->ep_status[epnum][ep_dir].claimed = 0;
+
+ if ( 0 == epnum ) {
+ usbh_control_xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result,
+ event.xfer_complete.len);
+ }else {
+ // Prefer application callback over built-in one if available. This occurs when tuh_edpt_xfer() is used
+ // with enabled driver e.g HID endpoint
+ #if CFG_TUH_API_EDPT_XFER
+ tuh_xfer_cb_t const complete_cb = dev->ep_callback[epnum][ep_dir].complete_cb;
+ if ( complete_cb ) {
+ // re-construct xfer info
+ tuh_xfer_t xfer = {
+ .daddr = event.dev_addr,
+ .ep_addr = ep_addr,
+ .result = event.xfer_complete.result,
+ .actual_len = event.xfer_complete.len,
+ .buflen = 0, // not available
+ .buffer = NULL, // not available
+ .complete_cb = complete_cb,
+ .user_data = dev->ep_callback[epnum][ep_dir].user_data
+ };
+
+ complete_cb(&xfer);
+ }else
+ #endif
+ {
+ uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
+ usbh_class_driver_t const * driver = get_driver(drv_id);
+ if ( driver )
+ {
+ TU_LOG_USBH("%s xfer callback\r\n", driver->name);
+ driver->xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result,
+ event.xfer_complete.len);
+ }
+ else
+ {
+ // no driver/callback responsible for this transfer
+ TU_ASSERT(false,);
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case USBH_EVENT_FUNC_CALL:
+ if ( event.func_call.func ) event.func_call.func(event.func_call.param);
+ break;
+
+ default: break;
+ }
+
+#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO
+ // return if there is no more events, for application to run other background
+ if (osal_queue_empty(_usbh_q)) return;
+#endif
+ }
+}
+
+//--------------------------------------------------------------------+
+// Control transfer
+//--------------------------------------------------------------------+
+
+static void _control_blocking_complete_cb(tuh_xfer_t* xfer)
+{
+ // update result
+ *((xfer_result_t*) xfer->user_data) = xfer->result;
+}
+
+// TODO timeout_ms is not supported yet
+bool tuh_control_xfer (tuh_xfer_t* xfer) {
+ // EP0 with setup packet
+ TU_VERIFY(xfer->ep_addr == 0 && xfer->setup);
+
+ // Check if device is still connected (enumerating for dev0)
+ uint8_t const daddr = xfer->daddr;
+ if ( daddr == 0 ) {
+ if (!_dev0.enumerating) return false;
+ } else {
+ usbh_device_t const* dev = get_device(daddr);
+ if (dev && dev->connected == 0) return false;
+ }
+
+ // pre-check to help reducing mutex lock
+ TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
+ (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+
+ bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
+ if (is_idle) {
+ _ctrl_xfer.stage = CONTROL_STAGE_SETUP;
+ _ctrl_xfer.daddr = daddr;
+ _ctrl_xfer.actual_len = 0;
+
+ _ctrl_xfer.request = (*xfer->setup);
+ _ctrl_xfer.buffer = xfer->buffer;
+ _ctrl_xfer.complete_cb = xfer->complete_cb;
+ _ctrl_xfer.user_data = xfer->user_data;
+ }
+
+ (void) osal_mutex_unlock(_usbh_mutex);
+
+ TU_VERIFY(is_idle);
+ const uint8_t rhport = usbh_get_rhport(daddr);
+
+ TU_LOG_USBH("[%u:%u] %s: ", rhport, daddr,
+ (xfer->setup->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME) ?
+ tu_str_std_request[xfer->setup->bRequest] : "Class Request");
+ TU_LOG_BUF(CFG_TUH_LOG_LEVEL, xfer->setup, 8);
+ TU_LOG_USBH("\r\n");
+
+ if (xfer->complete_cb) {
+ TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_ctrl_xfer.request) );
+ }else {
+ // blocking if complete callback is not provided
+ // change callback to internal blocking, and result as user argument
+ volatile xfer_result_t result = XFER_RESULT_INVALID;
+
+ // use user_data to point to xfer_result_t
+ _ctrl_xfer.user_data = (uintptr_t) &result;
+ _ctrl_xfer.complete_cb = _control_blocking_complete_cb;
+
+ TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) );
+
+ while (result == XFER_RESULT_INVALID) {
+ // Note: this can be called within an callback ie. part of tuh_task()
+ // therefore event with RTOS tuh_task() still need to be invoked
+ if (tuh_task_event_ready()) {
+ tuh_task();
+ }
+ // TODO probably some timeout to prevent hanged
+ }
+
+ // update transfer result, user_data is expected to point to xfer_result_t
+ if (xfer->user_data != 0) {
+ *((xfer_result_t*) xfer->user_data) = result;
+ }
+ xfer->result = result;
+ xfer->actual_len = _ctrl_xfer.actual_len;
+ }
+
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage)
+{
+ (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+ _ctrl_xfer.stage = stage;
+ (void) osal_mutex_unlock(_usbh_mutex);
+}
+
+static void _xfer_complete(uint8_t daddr, xfer_result_t result)
+{
+ TU_LOG_USBH("\r\n");
+
+ // duplicate xfer since user can execute control transfer within callback
+ tusb_control_request_t const request = _ctrl_xfer.request;
+ tuh_xfer_t xfer_temp =
+ {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .result = result,
+ .setup = &request,
+ .actual_len = (uint32_t) _ctrl_xfer.actual_len,
+ .buffer = _ctrl_xfer.buffer,
+ .complete_cb = _ctrl_xfer.complete_cb,
+ .user_data = _ctrl_xfer.user_data
+ };
+
+ _set_control_xfer_stage(CONTROL_STAGE_IDLE);
+
+ if (xfer_temp.complete_cb)
+ {
+ xfer_temp.complete_cb(&xfer_temp);
+ }
+}
+
+static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void) ep_addr;
+
+ const uint8_t rhport = usbh_get_rhport(dev_addr);
+ tusb_control_request_t const * request = &_ctrl_xfer.request;
+
+ if (XFER_RESULT_SUCCESS != result) {
+ TU_LOG1("[%u:%u] Control %s, xferred_bytes = %lu\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
+ TU_LOG1_BUF(request, 8);
+ TU_LOG1("\r\n");
+
+ // terminate transfer if any stage failed
+ _xfer_complete(dev_addr, result);
+ }else {
+ switch(_ctrl_xfer.stage) {
+ case CONTROL_STAGE_SETUP:
+ if (request->wLength) {
+ // DATA stage: initial data toggle is always 1
+ _set_control_xfer_stage(CONTROL_STAGE_DATA);
+ TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength) );
+ return true;
+ }
+ TU_ATTR_FALLTHROUGH;
+
+ case CONTROL_STAGE_DATA:
+ if (request->wLength) {
+ TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, dev_addr);
+ TU_LOG_MEM(CFG_TUH_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2);
+ TU_LOG_USBH("\r\n");
+ }
+
+ _ctrl_xfer.actual_len = (uint16_t) xferred_bytes;
+
+ // ACK stage: toggle is always 1
+ _set_control_xfer_stage(CONTROL_STAGE_ACK);
+ TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0) );
+ break;
+
+ case CONTROL_STAGE_ACK:
+ _xfer_complete(dev_addr, result);
+ break;
+
+ default: return false;
+ }
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+bool tuh_edpt_xfer(tuh_xfer_t* xfer)
+{
+ uint8_t const daddr = xfer->daddr;
+ uint8_t const ep_addr = xfer->ep_addr;
+
+ TU_VERIFY(daddr && ep_addr);
+
+ TU_VERIFY(usbh_edpt_claim(daddr, ep_addr));
+
+ if ( !usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen, xfer->complete_cb, xfer->user_data) )
+ {
+ usbh_edpt_release(daddr, ep_addr);
+ return false;
+ }
+
+ return true;
+}
+
+bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) {
+ usbh_device_t* dev = get_device(daddr);
+ TU_VERIFY(dev);
+
+ TU_LOG_USBH("[%u] Aborted transfer on EP %02X\r\n", daddr, ep_addr);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if ( epnum == 0 ) {
+ // control transfer: only 1 control at a time, check if we are aborting the current one
+ TU_VERIFY(daddr == _ctrl_xfer.daddr && _ctrl_xfer.stage != CONTROL_STAGE_IDLE);
+ TU_VERIFY(hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr));
+ // reset control transfer state to idle
+ _set_control_xfer_stage(CONTROL_STAGE_IDLE);
+ } else {
+ // non-control skip if not busy
+ TU_VERIFY(dev->ep_status[epnum][dir].busy);
+ TU_VERIFY(hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr));
+ // mark as ready and release endpoint if transfer is aborted
+ dev->ep_status[epnum][dir].busy = false;
+ usbh_edpt_release(daddr, ep_addr);
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// USBH API For Class Driver
+//--------------------------------------------------------------------+
+
+uint8_t usbh_get_rhport(uint8_t dev_addr) {
+ usbh_device_t *dev = get_device(dev_addr);
+ return dev ? dev->rhport : _dev0.rhport;
+}
+
+uint8_t *usbh_get_enum_buf(void) {
+ return _usbh_ctrl_buf;
+}
+
+void usbh_int_set(bool enabled) {
+ // TODO all host controller if multiple are used since they shared the same event queue
+ if (enabled) {
+ hcd_int_enable(_usbh_controller);
+ } else {
+ hcd_int_disable(_usbh_controller);
+ }
+}
+
+void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr) {
+ hcd_event_t event = { 0 };
+ event.event_id = USBH_EVENT_FUNC_CALL;
+ event.func_call.func = func;
+ event.func_call.param = param;
+
+ queue_event(&event, in_isr);
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+// Claim an endpoint for transfer
+bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr)
+{
+ // Note: addr0 only use tuh_control_xfer
+ usbh_device_t* dev = get_device(dev_addr);
+ TU_ASSERT(dev && dev->connected);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ TU_VERIFY(tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex));
+ TU_LOG_USBH("[%u] Claimed EP 0x%02x\r\n", dev_addr, ep_addr);
+
+ return true;
+}
+
+// Release an claimed endpoint due to failed transfer attempt
+bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr)
+{
+ // Note: addr0 only use tuh_control_xfer
+ usbh_device_t* dev = get_device(dev_addr);
+ TU_VERIFY(dev && dev->connected);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ TU_VERIFY(tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex));
+ TU_LOG_USBH("[%u] Released EP 0x%02x\r\n", dev_addr, ep_addr);
+
+ return true;
+}
+
+// Submit an transfer
+// TODO call usbh_edpt_release if failed
+bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ (void) complete_cb;
+ (void) user_data;
+
+ usbh_device_t* dev = get_device(dev_addr);
+ TU_VERIFY(dev);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir];
+
+ TU_LOG_USBH(" Queue EP %02X with %u bytes ... \r\n", ep_addr, total_bytes);
+
+ // Attempt to transfer on a busy endpoint, sound like an race condition !
+ TU_ASSERT(ep_state->busy == 0);
+
+ // Set busy first since the actual transfer can be complete before hcd_edpt_xfer()
+ // could return and USBH task can preempt and clear the busy
+ ep_state->busy = 1;
+
+#if CFG_TUH_API_EDPT_XFER
+ dev->ep_callback[epnum][dir].complete_cb = complete_cb;
+ dev->ep_callback[epnum][dir].user_data = user_data;
+#endif
+
+ if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) )
+ {
+ TU_LOG_USBH("OK\r\n");
+ return true;
+ }else
+ {
+ // HCD error, mark endpoint as ready to allow next transfer
+ ep_state->busy = 0;
+ ep_state->claimed = 0;
+ TU_LOG1("Failed\r\n");
+// TU_BREAKPOINT();
+ return false;
+ }
+}
+
+static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size)
+{
+ TU_LOG_USBH("[%u:%u] Open EP0 with Size = %u\r\n", usbh_get_rhport(dev_addr), dev_addr, max_packet_size);
+
+ tusb_desc_endpoint_t ep0_desc =
+ {
+ .bLength = sizeof(tusb_desc_endpoint_t),
+ .bDescriptorType = TUSB_DESC_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
+ .wMaxPacketSize = max_packet_size,
+ .bInterval = 0
+ };
+
+ return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, &ep0_desc);
+}
+
+bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep)
+{
+ TU_ASSERT( tu_edpt_validate(desc_ep, tuh_speed_get(dev_addr)) );
+
+ return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, desc_ep);
+}
+
+bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) {
+ usbh_device_t* dev = get_device(dev_addr);
+ TU_VERIFY(dev);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ return dev->ep_status[epnum][dir].busy;
+}
+
+//--------------------------------------------------------------------+
+// HCD Event Handler
+//--------------------------------------------------------------------+
+
+void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info)
+{
+ usbh_device_t const* dev = get_device(dev_addr);
+
+ if (dev)
+ {
+ devtree_info->rhport = dev->rhport;
+ devtree_info->hub_addr = dev->hub_addr;
+ devtree_info->hub_port = dev->hub_port;
+ devtree_info->speed = dev->speed;
+ }else
+ {
+ devtree_info->rhport = _dev0.rhport;
+ devtree_info->hub_addr = _dev0.hub_addr;
+ devtree_info->hub_port = _dev0.hub_port;
+ devtree_info->speed = _dev0.speed;
+ }
+}
+
+TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr) {
+ switch (event->event_id) {
+ case HCD_EVENT_DEVICE_REMOVE:
+ // FIXME device remove from a hub need an HCD API for hcd to free up endpoint
+ // mark device as removing to prevent further xfer before the event is processed in usbh task
+
+ // Check if dev0 is removed
+ if ((event->rhport == _dev0.rhport) && (event->connection.hub_addr == _dev0.hub_addr) &&
+ (event->connection.hub_port == _dev0.hub_port)) {
+ _dev0.enumerating = 0;
+ }
+ break;
+
+ default: break;
+ }
+
+ queue_event(event, in_isr);
+}
+
+//--------------------------------------------------------------------+
+// Descriptors Async
+//--------------------------------------------------------------------+
+
+// generic helper to get a descriptor
+// if blocking, user_data is pointed to xfer_result
+static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = TUSB_REQ_GET_DESCRIPTOR,
+ .wValue = tu_htole16( TU_U16(type, index) ),
+ .wIndex = tu_htole16(language_id),
+ .wLength = tu_htole16(len)
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = buffer,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
+bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ return _get_descriptor(daddr, type, index, 0x0000, buffer, len, complete_cb, user_data);
+}
+
+bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ len = tu_min16(len, sizeof(tusb_desc_device_t));
+ return tuh_descriptor_get(daddr, TUSB_DESC_DEVICE, 0, buffer, len, complete_cb, user_data);
+}
+
+bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ return tuh_descriptor_get(daddr, TUSB_DESC_CONFIGURATION, index, buffer, len, complete_cb, user_data);
+}
+
+//------------- String Descriptor -------------//
+
+bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ return _get_descriptor(daddr, TUSB_DESC_STRING, index, language_id, buffer, len, complete_cb, user_data);
+}
+
+// Get manufacturer string descriptor
+bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ usbh_device_t const* dev = get_device(daddr);
+ TU_VERIFY(dev && dev->i_manufacturer);
+ return tuh_descriptor_get_string(daddr, dev->i_manufacturer, language_id, buffer, len, complete_cb, user_data);
+}
+
+// Get product string descriptor
+bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ usbh_device_t const* dev = get_device(daddr);
+ TU_VERIFY(dev && dev->i_product);
+ return tuh_descriptor_get_string(daddr, dev->i_product, language_id, buffer, len, complete_cb, user_data);
+}
+
+// Get serial string descriptor
+bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ usbh_device_t const* dev = get_device(daddr);
+ TU_VERIFY(dev && dev->i_serial);
+ return tuh_descriptor_get_string(daddr, dev->i_serial, language_id, buffer, len, complete_cb, user_data);
+}
+
+// Get HID report descriptor
+// if blocking, user_data is pointed to xfer_result
+bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ TU_LOG_USBH("HID Get Report Descriptor\r\n");
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = TUSB_REQ_GET_DESCRIPTOR,
+ .wValue = tu_htole16(TU_U16(desc_type, index)),
+ .wIndex = tu_htole16((uint16_t) itf_num),
+ .wLength = len
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = buffer,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
+bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ TU_LOG_USBH("Set Configuration = %d\r\n", config_num);
+
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = TUSB_REQ_SET_CONFIGURATION,
+ .wValue = tu_htole16(config_num),
+ .wIndex = 0,
+ .wLength = 0
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
+bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data)
+{
+ TU_LOG_USBH("Set Interface %u Alternate %u\r\n", itf_num, itf_alt);
+
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = TUSB_REQ_SET_INTERFACE,
+ .wValue = tu_htole16(itf_alt),
+ .wIndex = tu_htole16(itf_num),
+ .wLength = 0
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
+//--------------------------------------------------------------------+
+// Descriptor Sync
+//--------------------------------------------------------------------+
+
+#define _CONTROL_SYNC_API(_async_func, ...) \
+ xfer_result_t result = XFER_RESULT_INVALID;\
+ TU_VERIFY(_async_func(__VA_ARGS__, NULL, (uintptr_t) &result), XFER_RESULT_TIMEOUT); \
+ return (uint8_t) result
+
+uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len)
+{
+ _CONTROL_SYNC_API(tuh_descriptor_get, daddr, type, index, buffer, len);
+}
+
+uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len)
+{
+ _CONTROL_SYNC_API(tuh_descriptor_get_device, daddr, buffer, len);
+}
+
+uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len)
+{
+ _CONTROL_SYNC_API(tuh_descriptor_get_configuration, daddr, index, buffer, len);
+}
+
+uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len)
+{
+ _CONTROL_SYNC_API(tuh_descriptor_get_hid_report, daddr, itf_num, desc_type, index, buffer, len);
+}
+
+uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len)
+{
+ _CONTROL_SYNC_API(tuh_descriptor_get_string, daddr, index, language_id, buffer, len);
+}
+
+uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len)
+{
+ _CONTROL_SYNC_API(tuh_descriptor_get_manufacturer_string, daddr, language_id, buffer, len);
+}
+
+uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len)
+{
+ _CONTROL_SYNC_API(tuh_descriptor_get_product_string, daddr, language_id, buffer, len);
+}
+
+uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len)
+{
+ _CONTROL_SYNC_API(tuh_descriptor_get_serial_string, daddr, language_id, buffer, len);
+}
+
+//--------------------------------------------------------------------+
+// Detaching
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE
+static inline bool is_hub_addr(uint8_t daddr)
+{
+ return (CFG_TUH_HUB > 0) && (daddr > CFG_TUH_DEVICE_MAX);
+}
+
+//static void mark_removing_device_isr(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) {
+// for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) {
+// usbh_device_t *dev = &_usbh_devices[dev_id];
+// uint8_t const daddr = dev_id + 1;
+//
+// // hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub
+// if (dev->rhport == rhport && dev->connected &&
+// (hub_addr == 0 || dev->hub_addr == hub_addr) &&
+// (hub_port == 0 || dev->hub_port == hub_port)) {
+// if (is_hub_addr(daddr)) {
+// // If the device itself is a usb hub, mark all downstream devices.
+// // FIXME recursive calls
+// mark_removing_device_isr(rhport, daddr, 0);
+// }
+//
+// dev->removing = 1;
+// }
+// }
+//}
+
+// a device unplugged from rhport:hub_addr:hub_port
+static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port)
+{
+ //------------- find the all devices (star-network) under port that is unplugged -------------//
+ // TODO mark as disconnected in ISR, also handle dev0
+
+#if 0
+ // index as hub addr, value is hub port (0xFF for invalid)
+ uint8_t removing_hubs[CFG_TUH_HUB];
+ memset(removing_hubs, TUSB_INDEX_INVALID_8, sizeof(removing_hubs));
+
+ removing_hubs[hub_addr-CFG_TUH_DEVICE_MAX] = hub_port;
+
+ // consecutive non-removing hub
+ uint8_t nop_count = 0;
+#endif
+
+ for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++)
+ {
+ usbh_device_t *dev = &_usbh_devices[dev_id];
+ uint8_t const daddr = dev_id + 1;
+
+ // hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub
+ if (dev->rhport == rhport && dev->connected &&
+ (hub_addr == 0 || dev->hub_addr == hub_addr) &&
+ (hub_port == 0 || dev->hub_port == hub_port)) {
+ TU_LOG_USBH("Device unplugged address = %u\r\n", daddr);
+
+ if (is_hub_addr(daddr)) {
+ TU_LOG_USBH(" is a HUB device %u\r\n", daddr);
+
+ // Submit removed event If the device itself is a hub (un-rolled recursive)
+ // TODO a better to unroll recursrive is using array of removing_hubs and mark it here
+ hcd_event_t event;
+ event.rhport = rhport;
+ event.event_id = HCD_EVENT_DEVICE_REMOVE;
+ event.connection.hub_addr = daddr;
+ event.connection.hub_port = 0;
+
+ hcd_event_handler(&event, false);
+ } else {
+ // Invoke callback before closing driver (maybe call it later ?)
+ if (tuh_umount_cb) tuh_umount_cb(daddr);
+ }
+
+ // Close class driver
+ for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
+ usbh_class_driver_t const * driver = get_driver(drv_id);
+ if ( driver ) driver->close(daddr);
+ }
+
+ hcd_device_close(rhport, daddr);
+ clear_device(dev);
+ // abort on-going control xfer if any
+ if (_ctrl_xfer.daddr == daddr) _set_control_xfer_stage(CONTROL_STAGE_IDLE);
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Enumeration Process
+// is a lengthy process with a series of control transfer to configure
+// newly attached device.
+// NOTE: due to the shared _usbh_ctrl_buf, we must complete enumerating
+// one device before enumerating another one.
+//--------------------------------------------------------------------+
+
+enum {
+ ENUM_RESET_DELAY = 50, // USB specs: 10 to 50ms
+ ENUM_CONTACT_DEBOUNCING_DELAY = 450, // when plug/unplug a device, physical connection can be bouncing and may
+ // generate a series of attach/detach event. This delay wait for stable connection
+};
+
+enum {
+ ENUM_IDLE,
+ ENUM_RESET_1, // 1st reset when attached
+ //ENUM_HUB_GET_STATUS_1,
+ ENUM_HUB_CLEAR_RESET_1,
+ ENUM_ADDR0_DEVICE_DESC,
+ ENUM_RESET_2, // 2nd reset before set address (not used)
+ ENUM_HUB_GET_STATUS_2,
+ ENUM_HUB_CLEAR_RESET_2,
+ ENUM_SET_ADDR,
+
+ ENUM_GET_DEVICE_DESC,
+ ENUM_GET_9BYTE_CONFIG_DESC,
+ ENUM_GET_FULL_CONFIG_DESC,
+ ENUM_SET_CONFIG,
+ ENUM_CONFIG_DRIVER
+};
+
+static bool enum_request_set_addr(void);
+static bool _parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
+static void enum_full_complete(void);
+
+// process device enumeration
+static void process_enumeration(tuh_xfer_t* xfer) {
+ // Retry a few times with transfers in enumeration since device can be unstable when starting up
+ enum {
+ ATTEMPT_COUNT_MAX = 3,
+ ATTEMPT_DELAY_MS = 100
+ };
+ static uint8_t failed_count = 0;
+
+ if (XFER_RESULT_SUCCESS != xfer->result) {
+ // retry if not reaching max attempt
+ bool retry = _dev0.enumerating && (failed_count < ATTEMPT_COUNT_MAX);
+ if ( retry ) {
+ failed_count++;
+ osal_task_delay(ATTEMPT_DELAY_MS); // delay a bit
+ TU_LOG1("Enumeration attempt %u\r\n", failed_count);
+ retry = tuh_control_xfer(xfer);
+ }
+
+ if (!retry) {
+ enum_full_complete();
+ }
+
+ return;
+ }
+ failed_count = 0;
+
+ uint8_t const daddr = xfer->daddr;
+ uintptr_t const state = xfer->user_data;
+
+ switch(state)
+ {
+#if CFG_TUH_HUB
+ //case ENUM_HUB_GET_STATUS_1: break;
+
+ case ENUM_HUB_CLEAR_RESET_1:
+ {
+ hub_port_status_response_t port_status;
+ memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
+
+ if ( !port_status.status.connection )
+ {
+ // device unplugged while delaying, nothing else to do
+ enum_full_complete();
+ return;
+ }
+
+ _dev0.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH :
+ (port_status.status.low_speed ) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
+
+ // Acknowledge Port Reset Change
+ if (port_status.change.reset)
+ {
+ hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_ADDR0_DEVICE_DESC);
+ }
+ }
+ break;
+
+ case ENUM_HUB_GET_STATUS_2:
+ osal_task_delay(ENUM_RESET_DELAY);
+ TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_2), );
+ break;
+
+ case ENUM_HUB_CLEAR_RESET_2:
+ {
+ hub_port_status_response_t port_status;
+ memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
+
+ // Acknowledge Port Reset Change if Reset Successful
+ if (port_status.change.reset)
+ {
+ TU_ASSERT( hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_SET_ADDR), );
+ }
+ }
+ break;
+#endif
+
+ case ENUM_ADDR0_DEVICE_DESC:
+ {
+ // TODO probably doesn't need to open/close each enumeration
+ uint8_t const addr0 = 0;
+ TU_ASSERT( usbh_edpt_control_open(addr0, 8), );
+
+ // Get first 8 bytes of device descriptor for Control Endpoint size
+ TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n");
+ TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8, process_enumeration, ENUM_SET_ADDR), );
+ }
+ break;
+
+#if 0
+ case ENUM_RESET_2:
+ // TODO not used by now, but may be needed for some devices !?
+ // Reset device again before Set Address
+ TU_LOG_USBH("Port reset2 \r\n");
+ if (_dev0.hub_addr == 0)
+ {
+ // connected directly to roothub
+ hcd_port_reset( _dev0.rhport );
+ osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
+ // sof of controller may not running while resetting
+ hcd_port_reset_end(_dev0.rhport);
+ // TODO: fall through to SET ADDRESS, refactor later
+ }
+ #if CFG_TUH_HUB
+ else
+ {
+ // after RESET_DELAY the hub_port_reset() already complete
+ TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_HUB_GET_STATUS_2), );
+ break;
+ }
+ #endif
+ TU_ATTR_FALLTHROUGH;
+#endif
+
+ case ENUM_SET_ADDR:
+ enum_request_set_addr();
+ break;
+
+ case ENUM_GET_DEVICE_DESC:
+ {
+ uint8_t const new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue);
+
+ usbh_device_t* new_dev = get_device(new_addr);
+ TU_ASSERT(new_dev, );
+ new_dev->addressed = 1;
+
+ // Close device 0
+ hcd_device_close(_dev0.rhport, 0);
+
+ // open control pipe for new address
+ TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_size), );
+
+ // Get full device descriptor
+ TU_LOG_USBH("Get Device Descriptor\r\n");
+ TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t), process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC), );
+ }
+ break;
+
+ case ENUM_GET_9BYTE_CONFIG_DESC:
+ {
+ tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
+ usbh_device_t* dev = get_device(daddr);
+ TU_ASSERT(dev, );
+
+ dev->vid = desc_device->idVendor;
+ dev->pid = desc_device->idProduct;
+ dev->i_manufacturer = desc_device->iManufacturer;
+ dev->i_product = desc_device->iProduct;
+ dev->i_serial = desc_device->iSerialNumber;
+
+ // if (tuh_attach_cb) tuh_attach_cb((tusb_desc_device_t*) _usbh_ctrl_buf);
+
+ // Get 9-byte for total length
+ uint8_t const config_idx = CONFIG_NUM - 1;
+ TU_LOG_USBH("Get Configuration[0] Descriptor (9 bytes)\r\n");
+ TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, 9, process_enumeration, ENUM_GET_FULL_CONFIG_DESC), );
+ }
+ break;
+
+ case ENUM_GET_FULL_CONFIG_DESC:
+ {
+ uint8_t const * desc_config = _usbh_ctrl_buf;
+
+ // Use offsetof to avoid pointer to the odd/misaligned address
+ uint16_t const total_len = tu_le16toh( tu_unaligned_read16(desc_config + offsetof(tusb_desc_configuration_t, wTotalLength)) );
+
+ // TODO not enough buffer to hold configuration descriptor
+ TU_ASSERT(total_len <= CFG_TUH_ENUMERATION_BUFSIZE, );
+
+ // Get full configuration descriptor
+ uint8_t const config_idx = CONFIG_NUM - 1;
+ TU_LOG_USBH("Get Configuration[0] Descriptor\r\n");
+ TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, total_len, process_enumeration, ENUM_SET_CONFIG), );
+ }
+ break;
+
+ case ENUM_SET_CONFIG:
+ // Parse configuration & set up drivers
+ // Driver open aren't allowed to make any usb transfer yet
+ TU_ASSERT( _parse_configuration_descriptor(daddr, (tusb_desc_configuration_t*) _usbh_ctrl_buf), );
+
+ TU_ASSERT( tuh_configuration_set(daddr, CONFIG_NUM, process_enumeration, ENUM_CONFIG_DRIVER), );
+ break;
+
+ case ENUM_CONFIG_DRIVER:
+ {
+ TU_LOG_USBH("Device configured\r\n");
+ usbh_device_t* dev = get_device(daddr);
+ TU_ASSERT(dev, );
+
+ dev->configured = 1;
+
+ // Start the Set Configuration process for interfaces (itf = TUSB_INDEX_INVALID_8)
+ // Since driver can perform control transfer within its set_config, this is done asynchronously.
+ // The process continue with next interface when class driver complete its sequence with usbh_driver_set_config_complete()
+ // TODO use separated API instead of using TUSB_INDEX_INVALID_8
+ usbh_driver_set_config_complete(daddr, TUSB_INDEX_INVALID_8);
+ }
+ break;
+
+ default:
+ // stop enumeration if unknown state
+ enum_full_complete();
+ break;
+ }
+}
+
+static bool enum_new_device(hcd_event_t* event)
+{
+ _dev0.rhport = event->rhport;
+ _dev0.hub_addr = event->connection.hub_addr;
+ _dev0.hub_port = event->connection.hub_port;
+
+ if (_dev0.hub_addr == 0)
+ {
+ // connected/disconnected directly with roothub
+ hcd_port_reset(_dev0.rhport);
+ osal_task_delay(ENUM_RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
+ // sof of controller may not running while resetting
+ hcd_port_reset_end( _dev0.rhport);
+
+ // wait until device connection is stable TODO non blocking
+ osal_task_delay(ENUM_CONTACT_DEBOUNCING_DELAY);
+
+ // device unplugged while delaying
+ if ( !hcd_port_connect_status(_dev0.rhport) ) {
+ enum_full_complete();
+ return true;
+ }
+
+ _dev0.speed = hcd_port_speed_get(_dev0.rhport );
+ TU_LOG_USBH("%s Speed\r\n", tu_str_speed[_dev0.speed]);
+
+ // fake transfer to kick-off the enumeration process
+ tuh_xfer_t xfer;
+ xfer.daddr = 0;
+ xfer.result = XFER_RESULT_SUCCESS;
+ xfer.user_data = ENUM_ADDR0_DEVICE_DESC;
+
+ process_enumeration(&xfer);
+ }
+#if CFG_TUH_HUB
+ else
+ {
+ // connected/disconnected via external hub
+ // wait until device connection is stable TODO non blocking
+ osal_task_delay(ENUM_CONTACT_DEBOUNCING_DELAY);
+
+ // ENUM_HUB_GET_STATUS
+ //TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status0_complete, 0) );
+ TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_1) );
+ }
+#endif // hub
+
+ return true;
+}
+
+static uint8_t get_new_address(bool is_hub) {
+ uint8_t start;
+ uint8_t end;
+
+ if ( is_hub ) {
+ start = CFG_TUH_DEVICE_MAX;
+ end = start + CFG_TUH_HUB;
+ }else {
+ start = 0;
+ end = start + CFG_TUH_DEVICE_MAX;
+ }
+
+ for (uint8_t idx = start; idx < end; idx++) {
+ if (!_usbh_devices[idx].connected) return (idx+1);
+ }
+
+ return 0; // invalid address
+}
+
+static bool enum_request_set_addr(void)
+{
+ tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
+
+ // Get new address
+ uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB);
+ TU_ASSERT(new_addr != 0);
+
+ TU_LOG_USBH("Set Address = %d\r\n", new_addr);
+
+ usbh_device_t* new_dev = get_device(new_addr);
+
+ new_dev->rhport = _dev0.rhport;
+ new_dev->hub_addr = _dev0.hub_addr;
+ new_dev->hub_port = _dev0.hub_port;
+ new_dev->speed = _dev0.speed;
+ new_dev->connected = 1;
+ new_dev->ep0_size = desc_device->bMaxPacketSize0;
+
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = TUSB_REQ_SET_ADDRESS,
+ .wValue = tu_htole16(new_addr),
+ .wIndex = 0,
+ .wLength = 0
+ };
+
+ tuh_xfer_t xfer =
+ {
+ .daddr = 0, // dev0
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = process_enumeration,
+ .user_data = ENUM_GET_DEVICE_DESC
+ };
+
+ TU_ASSERT( tuh_control_xfer(&xfer) );
+
+ return true;
+}
+
+static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg)
+{
+ usbh_device_t* dev = get_device(dev_addr);
+
+ uint16_t const total_len = tu_le16toh(desc_cfg->wTotalLength);
+ uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + total_len;
+ uint8_t const* p_desc = tu_desc_next(desc_cfg);
+
+ TU_LOG_USBH("Parsing Configuration descriptor (wTotalLength = %u)\r\n", total_len);
+
+ // parse each interfaces
+ while( p_desc < desc_end )
+ {
+ uint8_t assoc_itf_count = 1;
+
+ // Class will always starts with Interface Association (if any) and then Interface descriptor
+ if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) )
+ {
+ tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc;
+ assoc_itf_count = desc_iad->bInterfaceCount;
+
+ p_desc = tu_desc_next(p_desc); // next to Interface
+
+ // IAD's first interface number and class should match with opened interface
+ //TU_ASSERT(desc_iad->bFirstInterface == desc_itf->bInterfaceNumber &&
+ // desc_iad->bFunctionClass == desc_itf->bInterfaceClass);
+ }
+
+ TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) );
+ tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc;
+
+#if CFG_TUH_MIDI
+ // MIDI has 2 interfaces (Audio Control v1 + MIDIStreaming) but does not have IAD
+ // manually force associated count = 2
+ if (1 == assoc_itf_count &&
+ TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol)
+ {
+ assoc_itf_count = 2;
+ }
+#endif
+
+#if CFG_TUH_CDC
+ // Some legacy CDC device does not use IAD but rather use device class as hint to combine 2 interfaces
+ // manually force associated count = 2
+ if (1 == assoc_itf_count &&
+ TUSB_CLASS_CDC == desc_itf->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == desc_itf->bInterfaceSubClass)
+ {
+ assoc_itf_count = 2;
+ }
+#endif
+
+ uint16_t const drv_len = tu_desc_get_interface_total_len(desc_itf, assoc_itf_count, (uint16_t) (desc_end-p_desc));
+ TU_ASSERT(drv_len >= sizeof(tusb_desc_interface_t));
+
+ // Find driver for this interface
+ for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++)
+ {
+ usbh_class_driver_t const * driver = get_driver(drv_id);
+
+ if (driver && driver->open(dev->rhport, dev_addr, desc_itf, drv_len) )
+ {
+ // open successfully
+ TU_LOG_USBH(" %s opened\r\n", driver->name);
+
+ // bind (associated) interfaces to found driver
+ for(uint8_t i=0; ibInterfaceNumber+i;
+
+ // Interface number must not be used already
+ TU_ASSERT( TUSB_INDEX_INVALID_8 == dev->itf2drv[itf_num] );
+ dev->itf2drv[itf_num] = drv_id;
+ }
+
+ // bind all endpoints to found driver
+ tu_edpt_bind_driver(dev->ep2drv, desc_itf, drv_len, drv_id);
+
+ break; // exit driver find loop
+ }
+
+ if ( drv_id == TOTAL_DRIVER_COUNT - 1 )
+ {
+ TU_LOG_USBH("[%u:%u] Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n",
+ dev->rhport, dev_addr, desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass, desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol);
+ }
+ }
+
+ // next Interface or IAD descriptor
+ p_desc += drv_len;
+ }
+
+ return true;
+}
+
+void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) {
+ usbh_device_t* dev = get_device(dev_addr);
+
+ for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++) {
+ // continue with next valid interface
+ // IAD binding interface such as CDCs should return itf_num + 1 when complete
+ // with usbh_driver_set_config_complete()
+ uint8_t const drv_id = dev->itf2drv[itf_num];
+ usbh_class_driver_t const * driver = get_driver(drv_id);
+ if (driver) {
+ TU_LOG_USBH("%s set config: itf = %u\r\n", driver->name, itf_num);
+ driver->set_config(dev_addr, itf_num);
+ break;
+ }
+ }
+
+ // all interface are configured
+ if (itf_num == CFG_TUH_INTERFACE_MAX) {
+ enum_full_complete();
+
+ if (is_hub_addr(dev_addr)) {
+ TU_LOG_USBH("HUB address = %u is mounted\r\n", dev_addr);
+ }else {
+ // Invoke callback if available
+ if (tuh_mount_cb) tuh_mount_cb(dev_addr);
+ }
+ }
+}
+
+static void enum_full_complete(void) {
+ // mark enumeration as complete
+ _dev0.enumerating = 0;
+
+#if CFG_TUH_HUB
+ // get next hub status
+ if (_dev0.hub_addr) hub_edpt_status_xfer(_dev0.hub_addr);
+#endif
+
+}
+
+#endif
diff --git a/src/host/usbh.h b/src/host/usbh.h
index 7591c7672..9ff118543 100644
--- a/src/host/usbh.h
+++ b/src/host/usbh.h
@@ -94,7 +94,7 @@ TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr);
TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr);
// Invoked when there is a new usb event, which need to be processed by tuh_task()/tuh_task_ext()
-TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
+void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
//--------------------------------------------------------------------+
// APPLICATION API
diff --git a/src/host/usbh.h.bak b/src/host/usbh.h.bak
new file mode 100644
index 000000000..a5f9e47a9
--- /dev/null
+++ b/src/host/usbh.h.bak
@@ -0,0 +1,294 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_USBH_H_
+#define _TUSB_USBH_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include "common/tusb_common.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+// forward declaration
+struct tuh_xfer_s;
+typedef struct tuh_xfer_s tuh_xfer_t;
+
+typedef void (*tuh_xfer_cb_t)(tuh_xfer_t* xfer);
+
+// Note1: layout and order of this will be changed in near future
+// it is advised to initialize it using member name
+// Note2: not all field is available/meaningful in callback,
+// some info is not saved by usbh to save SRAM
+struct tuh_xfer_s {
+ uint8_t daddr;
+ uint8_t ep_addr;
+ uint8_t TU_RESERVED; // reserved
+ xfer_result_t result;
+
+ uint32_t actual_len; // excluding setup packet
+
+ union {
+ tusb_control_request_t const* setup; // setup packet pointer if control transfer
+ uint32_t buflen; // expected length if not control transfer (not available in callback)
+ };
+
+ uint8_t* buffer; // not available in callback if not control transfer
+ tuh_xfer_cb_t complete_cb;
+ uintptr_t user_data;
+ uint8_t itf_num;
+
+ // uint32_t timeout_ms; // place holder, not supported yet
+};
+
+// Subject to change
+typedef struct {
+ uint8_t daddr;
+ tusb_desc_interface_t desc;
+} tuh_itf_info_t;
+
+// ConfigID for tuh_config()
+enum {
+ TUH_CFGID_RPI_PIO_USB_CONFIGURATION = OPT_MCU_RP2040 << 8 // cfg_param: pio_usb_configuration_t
+};
+
+//--------------------------------------------------------------------+
+// APPLICATION CALLBACK
+//--------------------------------------------------------------------+
+
+//TU_ATTR_WEAK uint8_t tuh_attach_cb (tusb_desc_device_t const *desc_device);
+
+// Invoked when a device is mounted (configured)
+TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr);
+
+// Invoked when a device failed to mount during enumeration process
+// TU_ATTR_WEAK void tuh_mount_failed_cb (uint8_t daddr);
+
+// Invoked when a device is unmounted (detached)
+TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr);
+
+// Invoked when there is a new usb event, which need to be processed by tuh_task()/tuh_task_ext()
+TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
+
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+
+// Configure host stack behavior with dynamic or port-specific parameters.
+// Should be called before tuh_init()
+// - cfg_id : configure ID (TBD)
+// - cfg_param: configure data, structure depends on the ID
+bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param);
+
+// Init host stack
+bool tuh_init(uint8_t rhport);
+
+// Check if host stack is already initialized with any roothub ports
+bool tuh_inited(void);
+
+// Task function should be called in main/rtos loop, extended version of tuh_task()
+// - timeout_ms: millisecond to wait, zero = no wait, 0xFFFFFFFF = wait forever
+// - in_isr: if function is called in ISR
+void tuh_task_ext(uint32_t timeout_ms, bool in_isr);
+
+// Task function should be called in main/rtos loop
+TU_ATTR_ALWAYS_INLINE static inline
+void tuh_task(void) {
+ tuh_task_ext(UINT32_MAX, false);
+}
+
+// Check if there is pending events need processing by tuh_task()
+bool tuh_task_event_ready(void);
+
+#ifndef _TUSB_HCD_H_
+extern void hcd_int_handler(uint8_t rhport, bool in_isr);
+#endif
+
+// Interrupt handler alias to HCD with in_isr as optional parameter
+// - tuh_int_handler(rhport) --> hcd_int_handler(rhport, true)
+// - tuh_int_handler(rhport, in_isr) --> hcd_int_handler(rhport, in_isr)
+// Note: this is similar to TU_VERIFY(), _GET_3RD_ARG() is defined in tusb_verify.h
+#define _tuh_int_handler_1arg(_rhport) hcd_int_handler(_rhport, true)
+#define _tuh_int_hanlder_2arg(_rhport, _in_isr) hcd_int_handler(_rhport, _in_isr)
+#define tuh_int_handler(...) _GET_3RD_ARG(__VA_ARGS__, _tuh_int_hanlder_2arg, _tuh_int_handler_1arg, _dummy)(__VA_ARGS__)
+
+// Check if roothub port is initialized and active as a host
+bool tuh_rhport_is_active(uint8_t rhport);
+
+// Assert/de-assert Bus Reset signal to roothub port. USB specs: it should last 10-50ms
+bool tuh_rhport_reset_bus(uint8_t rhport, bool active);
+
+//--------------------------------------------------------------------+
+// Device API
+//--------------------------------------------------------------------+
+
+// Get VID/PID of device
+bool tuh_vid_pid_get(uint8_t daddr, uint16_t* vid, uint16_t* pid);
+
+// Get speed of device
+tusb_speed_t tuh_speed_get(uint8_t daddr);
+
+// Check if device is connected and configured
+bool tuh_mounted(uint8_t daddr);
+
+// Check if device is suspended
+TU_ATTR_ALWAYS_INLINE static inline
+bool tuh_suspended(uint8_t daddr) {
+ // TODO implement suspend & resume on host
+ (void) daddr;
+ return false;
+}
+
+// Check if device is ready to communicate with
+TU_ATTR_ALWAYS_INLINE static inline
+bool tuh_ready(uint8_t daddr) {
+ return tuh_mounted(daddr) && !tuh_suspended(daddr);
+}
+
+//--------------------------------------------------------------------+
+// Transfer API
+//--------------------------------------------------------------------+
+
+// Submit a control transfer
+// - async: complete callback invoked when finished.
+// - sync : blocking if complete callback is NULL.
+bool tuh_control_xfer(tuh_xfer_t* xfer);
+
+// Submit a bulk/interrupt transfer
+// - async: complete callback invoked when finished.
+// - sync : blocking if complete callback is NULL.
+bool tuh_edpt_xfer(tuh_xfer_t* xfer);
+
+// Open a non-control endpoint
+bool tuh_edpt_open(uint8_t daddr, tusb_desc_endpoint_t const * desc_ep);
+
+// Abort a queued transfer. Note: it can only abort transfer that has not been started
+// Return true if a queued transfer is aborted, false if there is no transfer to abort
+bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr);
+
+// Set Configuration (control transfer)
+// config_num = 0 will un-configure device. Note: config_num = config_descriptor_index + 1
+// true on success, false if there is on-going control transfer or incorrect parameters
+// if complete_cb == NULL i.e blocking, user_data should be pointed to xfer_reuslt_t*
+bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Set Interface (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+// if complete_cb == NULL i.e blocking, user_data should be pointed to xfer_reuslt_t*
+bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+//--------------------------------------------------------------------+
+// Descriptors Asynchronous (non-blocking)
+//--------------------------------------------------------------------+
+
+// Get an descriptor (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Get device descriptor (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Get configuration descriptor (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Get HID report descriptor (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Get string descriptor (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+// Blocking if complete callback is NULL, in this case 'user_data' must contain xfer_result_t variable
+bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Get manufacturer string descriptor (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Get product string descriptor (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Get serial string descriptor (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+//--------------------------------------------------------------------+
+// Descriptors Synchronous (blocking)
+//--------------------------------------------------------------------+
+
+// Sync (blocking) version of tuh_descriptor_get()
+// return transfer result
+uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len);
+
+// Sync (blocking) version of tuh_descriptor_get_device()
+// return transfer result
+uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len);
+
+// Sync (blocking) version of tuh_descriptor_get_configuration()
+// return transfer result
+uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len);
+
+// Sync (blocking) version of tuh_descriptor_get_hid_report()
+// return transfer result
+uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len);
+
+// Sync (blocking) version of tuh_descriptor_get_string()
+// return transfer result
+uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len);
+
+// Sync (blocking) version of tuh_descriptor_get_manufacturer_string()
+// return transfer result
+uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len);
+
+// Sync (blocking) version of tuh_descriptor_get_product_string()
+// return transfer result
+uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len);
+
+// Sync (blocking) version of tuh_descriptor_get_serial_string()
+// return transfer result
+uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c
index f978b0965..c59d4755e 100644
--- a/src/portable/ohci/ohci.c
+++ b/src/portable/ohci/ohci.c
@@ -157,6 +157,7 @@ static ohci_ed_t * const p_ed_head[] =
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed);
static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
+static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd);
//--------------------------------------------------------------------+
// USBH-HCD API
@@ -345,7 +346,7 @@ static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes)
tu_memclr(p_td, sizeof(ohci_gtd_t));
p_td->used = 1;
- p_td->expected_bytes = total_bytes;
+ gtd_get_extra_data(p_td)->expected_bytes = total_bytes;
p_td->buffer_rounding = 1; // less than queued length is not a error
p_td->delay_interrupt = OHCI_INT_ON_COMPLETE_NO;
@@ -610,6 +611,15 @@ static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd)
}
}
+static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd) {
+ if ( gtd_is_control(gtd) ) {
+ uint8_t idx = ((uintptr_t)gtd - (uintptr_t)&ohci_data.control->gtd) / sizeof(ohci_data.control[0]);
+ return &ohci_data.gtd_extra_control[idx];
+ }else {
+ return &ohci_data.gtd_extra[gtd - ohci_data.gtd_pool];
+ }
+}
+
static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer)
{
// 5.2.9 OHCI sample code
@@ -641,8 +651,7 @@ static void done_queue_isr(uint8_t hostid)
if ( (qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) )
{
ohci_ed_t * const ed = gtd_get_ed(qtd);
-
- uint32_t const xferred_bytes = qtd->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
+ uint32_t const xferred_bytes = gtd_get_extra_data(qtd)->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
// NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs.
// When there is a error resulting this ED is halted, and this EP still has other queued TD
@@ -651,7 +660,7 @@ static void done_queue_isr(uint8_t hostid)
// --> HC will not process Control list (due to service ratio when Bulk list not empty)
// To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt
// the TailP must be set back to NULL for processing remaining TDs
- if ((event != XFER_RESULT_SUCCESS))
+ if (event != XFER_RESULT_SUCCESS)
{
ed->td_tail &= 0x0Ful;
ed->td_tail |= tu_align16(ed->td_head.address); // mark halted EP as empty queue
diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h
index 2081ffabb..4feefd771 100644
--- a/src/portable/ohci/ohci.h
+++ b/src/portable/ohci/ohci.h
@@ -45,6 +45,9 @@ enum {
#define ED_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX)
#define GTD_MAX ED_MAX
+// tinyUSB's OHCI implementation caps number of EDs to 8 bits
+TU_VERIFY_STATIC (ED_MAX <= 256, "Reduce CFG_TUH_DEVICE_MAX or CFG_TUH_ENDPOINT_MAX");
+
//--------------------------------------------------------------------+
// OHCI Data Structure
//--------------------------------------------------------------------+
@@ -70,9 +73,8 @@ typedef struct TU_ATTR_ALIGNED(16)
{
// Word 0
uint32_t used : 1;
- uint32_t index : 4; // endpoint index the td belongs to, or device address in case of control xfer
- uint32_t expected_bytes : 13; // TODO available for hcd
-
+ uint32_t index : 8; // endpoint index the gtd belongs to, or device address in case of control xfer
+ uint32_t : 9; // can be used
uint32_t buffer_rounding : 1;
uint32_t pid : 2;
uint32_t delay_interrupt : 3;
@@ -152,9 +154,12 @@ typedef struct TU_ATTR_ALIGNED(32)
TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" );
+typedef struct {
+ uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits
+} gtd_extra_data_t;
+
// structure with member alignment required from large to small
-typedef struct TU_ATTR_ALIGNED(256)
-{
+typedef struct TU_ATTR_ALIGNED(256) {
ohci_hcca_t hcca;
ohci_ed_t bulk_head_ed; // static bulk head (dummy)
@@ -164,14 +169,17 @@ typedef struct TU_ATTR_ALIGNED(256)
struct {
ohci_ed_t ed;
ohci_gtd_t gtd;
- }control[CFG_TUH_DEVICE_MAX+CFG_TUH_HUB+1];
+ } control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
// ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32
ohci_ed_t ed_pool[ED_MAX];
ohci_gtd_t gtd_pool[GTD_MAX];
- volatile uint16_t frame_number_hi;
+ // extra data needed by TDs that can't fit in the TD struct
+ gtd_extra_data_t gtd_extra_control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
+ gtd_extra_data_t gtd_extra[GTD_MAX];
+ volatile uint16_t frame_number_hi;
} ohci_data_t;
//--------------------------------------------------------------------+
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 21cd3da26..9c37f1f98 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -128,8 +128,8 @@
# define DCD_STM32_BTABLE_BASE 0U
#endif
-#ifndef DCD_STM32_BTABLE_LENGTH
-# define DCD_STM32_BTABLE_LENGTH (PMA_LENGTH - DCD_STM32_BTABLE_BASE)
+#ifndef DCD_STM32_BTABLE_SIZE
+# define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE)
#endif
/***************************************************
@@ -137,7 +137,7 @@
*/
TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware");
-TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_LENGTH))<=(PMA_LENGTH), "BTABLE does not fit in PMA RAM");
+TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM");
TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes");
//--------------------------------------------------------------------+
@@ -559,7 +559,7 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
// Must reset EP to NAK (in case it had been stalling) (though, maybe too late here)
pcd_set_ep_rx_status(USB,0u,USB_EP_RX_NAK);
pcd_set_ep_tx_status(USB,0u,USB_EP_TX_NAK);
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
dcd_event_setup_received(0, (uint8_t*)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true);
#else
// The setup_received function uses memcpy, so this must first copy the setup data into
@@ -673,13 +673,13 @@ void dcd_int_handler(uint8_t rhport) {
/* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */
if(int_status & USB_ISTR_SOF) {
- USB->ISTR &=~USB_ISTR_SOF;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
dcd_event_sof(0, USB->FNR & USB_FNR_FN, true);
}
if(int_status & USB_ISTR_RESET) {
// USBRST is start of reset.
- USB->ISTR &=~USB_ISTR_RESET;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET;
dcd_handle_bus_reset();
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
return; // Don't do the rest of the things here; perhaps they've been cleared?
@@ -697,7 +697,7 @@ void dcd_int_handler(uint8_t rhport) {
USB->CNTR &= ~USB_CNTR_LPMODE;
USB->CNTR &= ~USB_CNTR_FSUSP;
- USB->ISTR &=~USB_ISTR_WKUP;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP;
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
@@ -711,7 +711,7 @@ void dcd_int_handler(uint8_t rhport) {
USB->CNTR |= USB_CNTR_LPMODE;
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
- USB->ISTR &=~USB_ISTR_SUSP;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
@@ -724,7 +724,7 @@ void dcd_int_handler(uint8_t rhport) {
{
remoteWakeCountdown--;
}
- USB->ISTR &=~USB_ISTR_ESOF;
+ USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF;
}
}
@@ -786,7 +786,7 @@ static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length)
}
// Ensure allocated buffer is aligned
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
length = (length + 3) & ~0x03;
#else
length = (length + 1) & ~0x01;
@@ -798,7 +798,7 @@ static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length)
ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer
// Verify no overflow
- TU_ASSERT(ep_buf_ptr <= PMA_LENGTH, 0xFFFF);
+ TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF);
epXferCtl->pma_ptr = addr;
epXferCtl->pma_alloc_size = length;
@@ -1227,7 +1227,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
}
}
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes)
{
const uint8_t* srcVal = src;
@@ -1283,7 +1283,7 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui
__IO uint16_t *pdwVal;
srcVal = src;
- pdwVal = &pma[PMA_STRIDE*(dst>>1)];
+ pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)];
while (n--)
{
@@ -1291,7 +1291,7 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui
srcVal++;
temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)) ;
*pdwVal = temp2;
- pdwVal += PMA_STRIDE;
+ pdwVal += FSDEV_PMA_STRIDE;
srcVal++;
}
@@ -1323,7 +1323,7 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
// We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
// last lin byte will be combined with wrapped part
// To ensure PMA is always access aligned (dst aligned to 16 or 32 bit)
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
if((cnt_lin & 0x03) && cnt_wrap)
{
// Copy first linear part
@@ -1386,7 +1386,7 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
return true;
}
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes)
{
uint8_t* dstVal = dst;
@@ -1434,13 +1434,13 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t
__IO const uint16_t *pdwVal;
uint32_t temp;
- pdwVal = &pma[PMA_STRIDE*(src>>1)];
+ pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)];
uint8_t *dstVal = (uint8_t*)dst;
while (n--)
{
temp = *pdwVal;
- pdwVal += PMA_STRIDE;
+ pdwVal += FSDEV_PMA_STRIDE;
*dstVal++ = ((temp >> 0) & 0xFF);
*dstVal++ = ((temp >> 8) & 0xFF);
}
@@ -1448,7 +1448,7 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t
if (wNBytes & 0x01)
{
temp = *pdwVal;
- pdwVal += PMA_STRIDE;
+ pdwVal += FSDEV_PMA_STRIDE;
*dstVal++ = ((temp >> 0) & 0xFF);
}
return true;
@@ -1475,7 +1475,7 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNB
// We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part,
// last lin byte will be combined with wrapped part
// To ensure PMA is always access aligned (src aligned to 16 or 32 bit)
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
if((cnt_lin & 0x03) && cnt_wrap)
{
// Copy first linear part
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
index b71e4f498..3f4db985d 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
@@ -28,7 +28,7 @@
// This file contains source copied from ST's HAL, and thus should have their copyright statement.
-// PMA_LENGTH is PMA buffer size in bytes.
+// FSDEV_PMA_SIZE is PMA buffer size in bytes.
// On 512-byte devices, access with a stride of two words (use every other 16-bit address)
// On 1024-byte devices, access with a stride of one word (use every 16-bit address)
@@ -37,7 +37,7 @@
#if CFG_TUSB_MCU == OPT_MCU_STM32F0
#include "stm32f0xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
// F0x2 models are crystal-less
// All have internal D+ pull-up
// 070RB: 2 x 16 bits/word memory LPM Support, BCD Support
@@ -45,7 +45,7 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
#include "stm32f1xx.h"
- #define PMA_LENGTH (512u)
+ #define FSDEV_PMA_SIZE (512u)
// NO internal Pull-ups
// *B, and *C: 2 x 16 bits/word
@@ -56,7 +56,7 @@
defined(STM32F303xB) || defined(STM32F303xC) || \
defined(STM32F373xC)
#include "stm32f3xx.h"
- #define PMA_LENGTH (512u)
+ #define FSDEV_PMA_SIZE (512u)
// NO internal Pull-ups
// *B, and *C: 1 x 16 bits/word
// PMA dedicated to USB (no sharing with CAN)
@@ -65,27 +65,27 @@
defined(STM32F302xD) || defined(STM32F302xE) || \
defined(STM32F303xD) || defined(STM32F303xE)
#include "stm32f3xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
// NO internal Pull-ups
// *6, *8, *D, and *E: 2 x 16 bits/word LPM Support
// When CAN clock is enabled, USB can use first 768 bytes ONLY.
#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
#include "stm32l0xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
#include "stm32l1xx.h"
- #define PMA_LENGTH (512u)
+ #define FSDEV_PMA_SIZE (512u)
#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
#include "stm32g4xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
#include "stm32g0xx.h"
- #define PMA_32BIT_ACCESS
- #define PMA_LENGTH (2048u)
+ #define FSDEV_BUS_32BIT
+ #define FSDEV_PMA_SIZE (2048u)
#undef USB_PMAADDR
#define USB_PMAADDR USB_DRD_PMAADDR
#define USB_TypeDef USB_DRD_TypeDef
@@ -112,8 +112,8 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
#include "stm32h5xx.h"
- #define PMA_32BIT_ACCESS
- #define PMA_LENGTH (2048u)
+ #define FSDEV_BUS_32BIT
+ #define FSDEV_PMA_SIZE (2048u)
#undef USB_PMAADDR
#define USB_PMAADDR USB_DRD_PMAADDR
#define USB_TypeDef USB_DRD_TypeDef
@@ -141,18 +141,18 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
#include "stm32wbxx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
/* ST provided header has incorrect value */
#undef USB_PMAADDR
#define USB_PMAADDR USB1_PMAADDR
#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
#include "stm32l4xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
#include "stm32l5xx.h"
- #define PMA_LENGTH (1024u)
+ #define FSDEV_PMA_SIZE (1024u)
#ifndef USB_PMAADDR
#define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
@@ -164,24 +164,28 @@
#endif
// For purposes of accessing the packet
-#if ((PMA_LENGTH) == 512u)
- #define PMA_STRIDE (2u)
-#elif ((PMA_LENGTH) == 1024u)
- #define PMA_STRIDE (1u)
+#if ((FSDEV_PMA_SIZE) == 512u)
+ #define FSDEV_PMA_STRIDE (2u)
+#elif ((FSDEV_PMA_SIZE) == 1024u)
+ #define FSDEV_PMA_STRIDE (1u)
#endif
+// The fsdev_bus_t type can be used for both register and PMA access necessities
// For type-safety create a new macro for the volatile address of PMAADDR
// The compiler should warn us if we cast it to a non-volatile type?
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
+typedef uint32_t fsdev_bus_t;
static __IO uint32_t * const pma32 = (__IO uint32_t*)USB_PMAADDR;
+
#else
+typedef uint16_t fsdev_bus_t;
// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden)
static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR;
TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x)
{
size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
- total_word_offset *= PMA_STRIDE;
+ total_word_offset *= FSDEV_PMA_STRIDE;
return &(pma[total_word_offset]);
}
@@ -212,7 +216,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t si
/* SetENDPOINT */
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
__O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4);
*reg = wRegValue;
@@ -224,7 +228,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, ui
/* GetENDPOINT */
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
__I uint32_t *reg = (__I uint32_t *)(USB_DRD_BASE + bEpIdx*4);
#else
@@ -279,7 +283,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx,
*/
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
return (pma32[2*bEpIdx] & 0x03FF0000) >> 16;
#else
@@ -290,7 +294,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USB
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16;
#else
@@ -317,7 +321,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx,
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
return pma32[2*bEpIdx] & 0x0000FFFFu ;
#else
@@ -327,7 +331,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef *
TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
return pma32[2*bEpIdx + 1] & 0x0000FFFFu;
#else
@@ -337,7 +341,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef *
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
#else
@@ -347,7 +351,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USB
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
#else
@@ -357,7 +361,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USB
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
#else
@@ -368,7 +372,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, u
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount)
{
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
#else
@@ -380,7 +384,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, u
TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t blocksize, uint32_t numblocks)
{
/* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
-#ifdef PMA_32BIT_ACCESS
+#ifdef FSDEV_BUS_32BIT
(void) USBx;
pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26);
#else
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index dd78ccd06..3237a50f6 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -149,7 +149,7 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
// https://community.st.com/t5/stm32cubemx-mcus/why-stm32h743-usb-fs-doesn-t-work-if-freertos-tickless-idle/m-p/349480#M18867
// H7 running on full-speed phy need to disable ULPI clock in sleep mode.
// Otherwise, USB won't work when mcu executing WFI/WFE instruction i.e tick-less RTOS.
- // Note: there may be other family that is affected by this, but only H7 is tested so far
+ // Note: there may be other family that is affected by this, but only H7 and F7 is tested so far
#if defined(USB_OTG_FS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB2OTGFSULPILPEN)
if ( USB_OTG_FS_PERIPH_BASE == (uint32_t) dwc2 ) {
RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB2OTGFSULPILPEN;
@@ -161,6 +161,13 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB1OTGHSULPILPEN;
}
#endif
+
+ #if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_OTGHSULPILPEN)
+ if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) {
+ RCC->AHB1LPENR &= ~RCC_AHB1LPENR_OTGHSULPILPEN;
+ }
+ #endif
+
} else {
#if CFG_TUSB_MCU != OPT_MCU_STM32U5
// Disable FS PHY, TODO on U5A5 (dwc2 4.11a) 16th bit is 'Host CDP behavior enable'
diff --git a/src/tusb_option.h.bak b/src/tusb_option.h.bak
new file mode 100644
index 000000000..33214d7b2
--- /dev/null
+++ b/src/tusb_option.h.bak
@@ -0,0 +1,537 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_OPTION_H_
+#define _TUSB_OPTION_H_
+
+#include "common/tusb_compiler.h"
+
+#define TUSB_VERSION_MAJOR 0
+#define TUSB_VERSION_MINOR 16
+#define TUSB_VERSION_REVISION 0
+#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
+
+//--------------------------------------------------------------------+
+// Supported MCUs
+// CFG_TUSB_MCU must be defined to one of following value
+//--------------------------------------------------------------------+
+
+#define OPT_MCU_NONE 0
+
+// LPC
+#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx
+#define OPT_MCU_LPC13XX 2 ///< NXP LPC13xx
+#define OPT_MCU_LPC15XX 3 ///< NXP LPC15xx
+#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x
+#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x
+#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx
+#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx
+#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx
+#define OPT_MCU_LPC51UXX 9 ///< NXP LPC51U6x
+#define OPT_MCU_LPC54 10 ///< NXP LPC54
+#define OPT_MCU_LPC55 11 ///< NXP LPC55
+// legacy naming
+#define OPT_MCU_LPC54XXX OPT_MCU_LPC54
+#define OPT_MCU_LPC55XX OPT_MCU_LPC55
+
+// NRF
+#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series
+
+// SAM
+#define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21
+#define OPT_MCU_SAMD51 201 ///< MicroChip SAMD51
+#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series
+#define OPT_MCU_SAME5X 203 ///< MicroChip SAM E5x
+#define OPT_MCU_SAMD11 204 ///< MicroChip SAMD11
+#define OPT_MCU_SAML22 205 ///< MicroChip SAML22
+#define OPT_MCU_SAML21 206 ///< MicroChip SAML21
+#define OPT_MCU_SAMX7X 207 ///< MicroChip SAME70, S70, V70, V71 family
+
+// STM32
+#define OPT_MCU_STM32F0 300 ///< ST F0
+#define OPT_MCU_STM32F1 301 ///< ST F1
+#define OPT_MCU_STM32F2 302 ///< ST F2
+#define OPT_MCU_STM32F3 303 ///< ST F3
+#define OPT_MCU_STM32F4 304 ///< ST F4
+#define OPT_MCU_STM32F7 305 ///< ST F7
+#define OPT_MCU_STM32H7 306 ///< ST H7
+#define OPT_MCU_STM32L1 308 ///< ST L1
+#define OPT_MCU_STM32L0 307 ///< ST L0
+#define OPT_MCU_STM32L4 309 ///< ST L4
+#define OPT_MCU_STM32G0 310 ///< ST G0
+#define OPT_MCU_STM32G4 311 ///< ST G4
+#define OPT_MCU_STM32WB 312 ///< ST WB
+#define OPT_MCU_STM32U5 313 ///< ST U5
+#define OPT_MCU_STM32L5 314 ///< ST L5
+#define OPT_MCU_STM32H5 315 ///< ST H5
+
+// Sony
+#define OPT_MCU_CXD56 400 ///< SONY CXD56
+
+// TI
+#define OPT_MCU_MSP430x5xx 500 ///< TI MSP430x5xx
+#define OPT_MCU_MSP432E4 510 ///< TI MSP432E4xx
+#define OPT_MCU_TM4C123 511 ///< TI Tiva-C 123x
+#define OPT_MCU_TM4C129 512 ///< TI Tiva-C 129x
+
+// ValentyUSB eptri
+#define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config
+
+// NXP iMX RT
+#define OPT_MCU_MIMXRT1XXX 700 ///< NXP iMX RT1xxx Series
+#define OPT_MCU_MIMXRT10XX OPT_MCU_MIMXRT1XXX ///< RT10xx
+#define OPT_MCU_MIMXRT11XX OPT_MCU_MIMXRT1XXX ///< RT11xx
+
+// Nuvoton
+#define OPT_MCU_NUC121 800
+#define OPT_MCU_NUC126 801
+#define OPT_MCU_NUC120 802
+#define OPT_MCU_NUC505 803
+
+// Espressif
+#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2
+#define OPT_MCU_ESP32S3 901 ///< Espressif ESP32-S3
+
+// Dialog
+#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
+
+// Raspberry Pi
+#define OPT_MCU_RP2040 1100 ///< Raspberry Pi RP2040
+
+// NXP Kinetis
+#define OPT_MCU_KINETIS_KL 1200 ///< NXP KL series
+#define OPT_MCU_KINETIS_K32L 1201 ///< NXP K32L series
+#define OPT_MCU_KINETIS_K32 1201 ///< Alias to K32L
+
+#define OPT_MCU_MKL25ZXX 1200 ///< Alias to KL (obsolete)
+#define OPT_MCU_K32L2BXX 1201 ///< Alias to K32 (obsolete)
+
+// Silabs
+#define OPT_MCU_EFM32GG 1300 ///< Silabs EFM32GG
+
+// Renesas RX
+#define OPT_MCU_RX63X 1400 ///< Renesas RX63N/631
+#define OPT_MCU_RX65X 1401 ///< Renesas RX65N/RX651
+#define OPT_MCU_RX72N 1402 ///< Renesas RX72N
+#define OPT_MCU_RAXXX 1403 ///< Renesas RAxxx families
+
+
+// Mind Motion
+#define OPT_MCU_MM32F327X 1500 ///< Mind Motion MM32F327
+
+// GigaDevice
+#define OPT_MCU_GD32VF103 1600 ///< GigaDevice GD32VF103
+
+// Broadcom
+#define OPT_MCU_BCM2711 1700 ///< Broadcom BCM2711
+#define OPT_MCU_BCM2835 1701 ///< Broadcom BCM2835
+#define OPT_MCU_BCM2837 1702 ///< Broadcom BCM2837
+
+// Infineon
+#define OPT_MCU_XMC4000 1800 ///< Infineon XMC4000
+
+// PIC
+#define OPT_MCU_PIC32MZ 1900 ///< MicroChip PIC32MZ family
+#define OPT_MCU_PIC32MM 1901 ///< MicroChip PIC32MM family
+#define OPT_MCU_PIC32MX 1902 ///< MicroChip PIC32MX family
+#define OPT_MCU_PIC32MK 1903 ///< MicroChip PIC32MK family
+#define OPT_MCU_PIC24 1910 ///< MicroChip PIC24 family
+#define OPT_MCU_DSPIC33 1911 ///< MicroChip DSPIC33 family
+
+// BridgeTek
+#define OPT_MCU_FT90X 2000 ///< BridgeTek FT90x
+#define OPT_MCU_FT93X 2001 ///< BridgeTek FT93x
+
+// Allwinner
+#define OPT_MCU_F1C100S 2100 ///< Allwinner F1C100s family
+
+// WCH
+#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307
+#define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x
+
+
+// NXP LPC MCX
+#define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series
+
+// Check if configured MCU is one of listed
+// Apply _TU_CHECK_MCU with || as separator to list of input
+#define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m)
+#define TU_CHECK_MCU(...) (TU_ARGS_APPLY(_TU_CHECK_MCU, ||, __VA_ARGS__))
+
+//--------------------------------------------------------------------+
+// Supported OS
+//--------------------------------------------------------------------+
+
+#define OPT_OS_NONE 1 ///< No RTOS
+#define OPT_OS_FREERTOS 2 ///< FreeRTOS
+#define OPT_OS_MYNEWT 3 ///< Mynewt OS
+#define OPT_OS_CUSTOM 4 ///< Custom OS is implemented by application
+#define OPT_OS_PICO 5 ///< Raspberry Pi Pico SDK
+#define OPT_OS_RTTHREAD 6 ///< RT-Thread
+#define OPT_OS_RTX4 7 ///< Keil RTX 4
+
+// Allow to use command line to change the config name/location
+#ifdef CFG_TUSB_CONFIG_FILE
+ #include CFG_TUSB_CONFIG_FILE
+#else
+ #include "tusb_config.h"
+#endif
+
+#include "common/tusb_mcu.h"
+
+//--------------------------------------------------------------------
+// RootHub Mode Configuration
+// CFG_TUSB_RHPORTx_MODE contains operation mode and speed for that port
+//--------------------------------------------------------------------
+
+// Low byte is operational mode
+#define OPT_MODE_NONE 0x0000 ///< Disabled
+#define OPT_MODE_DEVICE 0x0001 ///< Device Mode
+#define OPT_MODE_HOST 0x0002 ///< Host Mode
+
+// High byte is max operational speed (corresponding to tusb_speed_t)
+#define OPT_MODE_DEFAULT_SPEED 0x0000 ///< Default (max) speed supported by MCU
+#define OPT_MODE_LOW_SPEED 0x0100 ///< Low Speed
+#define OPT_MODE_FULL_SPEED 0x0200 ///< Full Speed
+#define OPT_MODE_HIGH_SPEED 0x0400 ///< High Speed
+#define OPT_MODE_SPEED_MASK 0xff00
+
+//------------- Roothub as Device -------------//
+
+#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE)
+ #define TUD_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
+ #define TUD_OPT_RHPORT 0
+#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE)
+ #define TUD_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE)
+ #define TUD_OPT_RHPORT 1
+#else
+ #define TUD_RHPORT_MODE OPT_MODE_NONE
+#endif
+
+#ifndef CFG_TUD_ENABLED
+ // fallback to use CFG_TUSB_RHPORTx_MODE
+ #define CFG_TUD_ENABLED (TUD_RHPORT_MODE & OPT_MODE_DEVICE)
+#endif
+
+#ifndef CFG_TUD_MAX_SPEED
+ // fallback to use CFG_TUSB_RHPORTx_MODE
+ #define CFG_TUD_MAX_SPEED (TUD_RHPORT_MODE & OPT_MODE_SPEED_MASK)
+#endif
+
+// For backward compatible
+#define TUSB_OPT_DEVICE_ENABLED CFG_TUD_ENABLED
+
+// highspeed support indicator
+#define TUD_OPT_HIGH_SPEED (CFG_TUD_MAX_SPEED ? (CFG_TUD_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
+
+//------------- Roothub as Host -------------//
+
+#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST)
+ #define TUH_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
+ #define TUH_OPT_RHPORT 0
+#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST)
+ #define TUH_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE)
+ #define TUH_OPT_RHPORT 1
+#else
+ #define TUH_RHPORT_MODE OPT_MODE_NONE
+#endif
+
+#ifndef CFG_TUH_ENABLED
+ // fallback to use CFG_TUSB_RHPORTx_MODE
+ #define CFG_TUH_ENABLED (TUH_RHPORT_MODE & OPT_MODE_HOST)
+#endif
+
+#ifndef CFG_TUH_MAX_SPEED
+ // fallback to use CFG_TUSB_RHPORTx_MODE
+ #define CFG_TUH_MAX_SPEED (TUH_RHPORT_MODE & OPT_MODE_SPEED_MASK)
+#endif
+
+// For backward compatible
+#define TUSB_OPT_HOST_ENABLED CFG_TUH_ENABLED
+
+// highspeed support indicator
+#define TUH_OPT_HIGH_SPEED (CFG_TUH_MAX_SPEED ? (CFG_TUH_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
+
+
+//--------------------------------------------------------------------+
+// TODO move later
+//--------------------------------------------------------------------+
+
+// TUP_MCU_STRICT_ALIGN will overwrite TUP_ARCH_STRICT_ALIGN.
+// In case TUP_MCU_STRICT_ALIGN = 1 and TUP_ARCH_STRICT_ALIGN =0, we will not reply on compiler
+// to generate unaligned access code.
+// LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM
+#if TUD_OPT_HIGH_SPEED && TU_CHECK_MCU(OPT_MCU_LPC54XXX, OPT_MCU_LPC55XX)
+ #define TUP_MCU_STRICT_ALIGN 1
+#else
+ #define TUP_MCU_STRICT_ALIGN 0
+#endif
+
+
+//--------------------------------------------------------------------+
+// Common Options (Default)
+//--------------------------------------------------------------------+
+
+// Debug enable to print out error message
+#ifndef CFG_TUSB_DEBUG
+ #define CFG_TUSB_DEBUG 0
+#endif
+
+// Level where CFG_TUSB_DEBUG must be at least for USBH is logged
+#ifndef CFG_TUH_LOG_LEVEL
+ #define CFG_TUH_LOG_LEVEL 2
+#endif
+
+// Level where CFG_TUSB_DEBUG must be at least for USBD is logged
+#ifndef CFG_TUD_LOG_LEVEL
+ #define CFG_TUD_LOG_LEVEL 2
+#endif
+
+// Memory section for placing buffer used for usb transferring. If MEM_SECTION is different for
+// host and device use: CFG_TUD_MEM_SECTION, CFG_TUH_MEM_SECTION instead
+#ifndef CFG_TUSB_MEM_SECTION
+ #define CFG_TUSB_MEM_SECTION
+#endif
+
+// Alignment requirement of buffer used for usb transferring. if MEM_ALIGN is different for
+// host and device controller use: CFG_TUD_MEM_ALIGN, CFG_TUH_MEM_ALIGN instead
+#ifndef CFG_TUSB_MEM_ALIGN
+ #define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
+#endif
+
+// OS selection
+#ifndef CFG_TUSB_OS
+ #define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_OS_INC_PATH
+ #define CFG_TUSB_OS_INC_PATH
+#endif
+
+//--------------------------------------------------------------------
+// Device Options (Default)
+//--------------------------------------------------------------------
+
+// Attribute to place data in accessible RAM for device controller (default: CFG_TUSB_MEM_SECTION)
+#ifndef CFG_TUD_MEM_SECTION
+ #define CFG_TUD_MEM_SECTION CFG_TUSB_MEM_SECTION
+#endif
+
+// Attribute to align memory for device controller (default: CFG_TUSB_MEM_ALIGN)
+#ifndef CFG_TUD_MEM_ALIGN
+ #define CFG_TUD_MEM_ALIGN CFG_TUSB_MEM_ALIGN
+#endif
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+ #define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+#ifndef CFG_TUD_INTERFACE_MAX
+ #define CFG_TUD_INTERFACE_MAX 16
+#endif
+
+//------------- Device Class Driver -------------//
+#ifndef CFG_TUD_BTH
+ #define CFG_TUD_BTH 0
+#endif
+
+#if CFG_TUD_BTH && !defined(CFG_TUD_BTH_ISO_ALT_COUNT)
+#error CFG_TUD_BTH_ISO_ALT_COUNT must be defined to tell Bluetooth driver the number of ISO endpoints to use
+#endif
+
+#ifndef CFG_TUD_CDC
+ #define CFG_TUD_CDC 0
+#endif
+
+#ifndef CFG_TUD_MSC
+ #define CFG_TUD_MSC 0
+#endif
+
+#ifndef CFG_TUD_HID
+ #define CFG_TUD_HID 0
+#endif
+
+#ifndef CFG_TUD_AUDIO
+ #define CFG_TUD_AUDIO 0
+#endif
+
+#ifndef CFG_TUD_VIDEO
+ #define CFG_TUD_VIDEO 0
+#endif
+
+#ifndef CFG_TUD_MIDI
+ #define CFG_TUD_MIDI 0
+#endif
+
+#ifndef CFG_TUD_VENDOR
+ #define CFG_TUD_VENDOR 0
+#endif
+
+#ifndef CFG_TUD_USBTMC
+ #define CFG_TUD_USBTMC 0
+#endif
+
+#ifndef CFG_TUD_DFU_RUNTIME
+ #define CFG_TUD_DFU_RUNTIME 0
+#endif
+
+#ifndef CFG_TUD_DFU
+ #define CFG_TUD_DFU 0
+#endif
+
+#ifndef CFG_TUD_ECM_RNDIS
+ #ifdef CFG_TUD_NET
+ #warning "CFG_TUD_NET is renamed to CFG_TUD_ECM_RNDIS"
+ #define CFG_TUD_ECM_RNDIS CFG_TUD_NET
+ #else
+ #define CFG_TUD_ECM_RNDIS 0
+ #endif
+#endif
+
+#ifndef CFG_TUD_NCM
+ #define CFG_TUD_NCM 0
+#endif
+
+//--------------------------------------------------------------------
+// Host Options (Default)
+//--------------------------------------------------------------------
+#if CFG_TUH_ENABLED
+ #ifndef CFG_TUH_DEVICE_MAX
+ #define CFG_TUH_DEVICE_MAX 1
+ #endif
+
+ #ifndef CFG_TUH_ENUMERATION_BUFSIZE
+ #define CFG_TUH_ENUMERATION_BUFSIZE 256
+ #endif
+#endif // CFG_TUH_ENABLED
+
+// Attribute to place data in accessible RAM for host controller (default: CFG_TUSB_MEM_SECTION)
+#ifndef CFG_TUH_MEM_SECTION
+ #define CFG_TUH_MEM_SECTION CFG_TUSB_MEM_SECTION
+#endif
+
+// Attribute to align memory for host controller
+#ifndef CFG_TUH_MEM_ALIGN
+ #define CFG_TUH_MEM_ALIGN CFG_TUSB_MEM_ALIGN
+#endif
+
+//------------- CLASS -------------//
+
+#ifndef CFG_TUH_HUB
+ #define CFG_TUH_HUB 0
+#endif
+
+#ifndef CFG_TUH_CDC
+ #define CFG_TUH_CDC 0
+#endif
+
+#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ #define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+#endif
+
+#ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ #define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM ( CDC_CONTROL_LINE_STATE_RTS | CDC_CONTROL_LINE_STATE_DTR )
+#endif
+
+#ifndef CFG_TUH_CDC_FTDI
+ // FTDI is not part of CDC class, only to re-use CDC driver API
+ #define CFG_TUH_CDC_FTDI 0
+#endif
+
+#ifndef CFG_TUH_CDC_FTDI_PID_LIST
+ // List of product IDs that can use the FTDI CDC driver
+ #define CFG_TUH_CDC_FTDI_PID_LIST \
+ 0x6001, 0x6006, 0x6010, 0x6011, 0x6014, 0x6015, 0x8372, 0xFBFA, 0xCD18
+#endif
+
+#ifndef CFG_TUH_CDC_CP210X
+ // CP210X is not part of CDC class, only to re-use CDC driver API
+ #define CFG_TUH_CDC_CP210X 0
+#endif
+
+#ifndef CFG_TUH_CDC_CP210X_PID_LIST
+ // List of product IDs that can use the CP210X CDC driver
+ #define CFG_TUH_CDC_CP210X_PID_LIST \
+ 0xEA60, 0xEA70
+#endif
+
+#ifndef CFG_TUH_HID
+ #define CFG_TUH_HID 0
+#endif
+
+#ifndef CFG_TUH_MIDI
+ #define CFG_TUH_MIDI 0
+#endif
+
+#ifndef CFG_TUH_MSC
+ #define CFG_TUH_MSC 0
+#endif
+
+#ifndef CFG_TUH_VENDOR
+ #define CFG_TUH_VENDOR 0
+#endif
+
+#ifndef CFG_TUH_API_EDPT_XFER
+ #define CFG_TUH_API_EDPT_XFER 0
+#endif
+
+// Enable PIO-USB software host controller
+#ifndef CFG_TUH_RPI_PIO_USB
+ #define CFG_TUH_RPI_PIO_USB 0
+#endif
+
+#ifndef CFG_TUD_RPI_PIO_USB
+ #define CFG_TUD_RPI_PIO_USB 0
+#endif
+
+// MAX3421 Host controller option
+#ifndef CFG_TUH_MAX3421
+ #define CFG_TUH_MAX3421 0
+#endif
+
+//--------------------------------------------------------------------+
+// TypeC Options (Default)
+//--------------------------------------------------------------------+
+
+#ifndef CFG_TUC_ENABLED
+#define CFG_TUC_ENABLED 0
+
+#define tuc_int_handler(_p)
+#endif
+
+//------------------------------------------------------------------
+// Configuration Validation
+//------------------------------------------------------------------
+#if CFG_TUD_ENDPOINT0_SIZE > 64
+ #error Control Endpoint Max Packet Size cannot be larger than 64
+#endif
+
+// To avoid GCC compiler warnings when -pedantic option is used (strict ISO C)
+typedef int make_iso_compilers_happy;
+
+#endif /* _TUSB_OPTION_H_ */
+
+/** @} */