diff --git a/.github/workflows/build_arm.yml b/.github/workflows/build_arm.yml
index 975f351a0..171c1fec3 100644
--- a/.github/workflows/build_arm.yml
+++ b/.github/workflows/build_arm.yml
@@ -36,11 +36,11 @@ jobs:
         - 'broadcom_32bit'
         - 'kinetis_k32l2'
         - 'lpc11 lpc13 lpc15 lpc17'
-        - 'lpc51 lpc54'
+        - 'lpc51'
         - 'mm32 msp432e4'
         - 'samd11 same5x saml2x'
         - 'stm32f2 stm32f3'
-        - 'stm32l0 stm32u5 stm32wb'
+        - 'stm32l0 stm32wb'
         - 'tm4c123 xmc4000'
     steps:
     - name: Setup Python
diff --git a/.github/workflows/build_esp.yml b/.github/workflows/build_esp.yml
index 29585cb36..897616f09 100644
--- a/.github/workflows/build_esp.yml
+++ b/.github/workflows/build_esp.yml
@@ -29,12 +29,10 @@ jobs:
       fail-fast: false
       matrix:
         board:
-        # Alphabetical order
         # ESP32-S2
-        - 'espressif_saola_1'
+        - 'espressif_kaluga_1'
         # ESP32-S3
-        #- 'espressif_s3_devkitm'
-        # S3 compile error with "dangerous relocation: call8: call target out of range: memcpy"
+        - 'espressif_s3_devkitm'
 
     steps:
     - name: Setup Python
@@ -48,20 +46,5 @@ jobs:
     - name: Checkout TinyUSB
       uses: actions/checkout@v3
 
-    - name: Checkout hathach/linkermap
-      uses: actions/checkout@v3
-      with:
-         repository: hathach/linkermap
-         path: linkermap
-
     - name: Build
       run: docker run --rm -v $PWD:/project -w /project espressif/idf:latest python3 tools/build_esp32.py ${{ matrix.board }}
-
-    - name: Linker Map
-      run: |
-        pip install linkermap/
-        # find -quit to only print linkermap of 1 board per example
-        for ex in `ls -d examples/device/*/`
-        do
-          find ${ex} -maxdepth 3 -name *.map -print -quit | xargs -I % sh -c 'echo "::group::%"; linkermap -v %; echo "::endgroup::"'
-        done
diff --git a/.github/workflows/cmake_arm.yml b/.github/workflows/cmake_arm.yml
index 4b788a213..e57c297d7 100644
--- a/.github/workflows/cmake_arm.yml
+++ b/.github/workflows/cmake_arm.yml
@@ -36,7 +36,7 @@ jobs:
           - 'imxrt'
           - 'kinetis_kl'
           - 'lpc18 lpc40 lpc43'
-          - 'lpc55'
+          - 'lpc54 lpc55'
           - 'mcx'
           - 'nrf'
           - 'ra'
@@ -51,6 +51,7 @@ jobs:
           - 'stm32g4'
           - 'stm32h7'
           - 'stm32l4'
+          - 'stm32u5'
     steps:
     - name: Setup Python
       uses: actions/setup-python@v4
diff --git a/.idea/cmake.xml b/.idea/cmake.xml
index 89ec1066c..9721af5ea 100644
--- a/.idea/cmake.xml
+++ b/.idea/cmake.xml
@@ -2,23 +2,24 @@
 
   
     
-      
+      
         
           
             
-            
-            
-            
           
         
       
-      
+      
+        
+          
+            
+          
+        
+      
+      
         
           
             
-            
-            
-            
           
         
       
@@ -55,6 +56,14 @@
       
       
       
+      
+      
+        
+          
+            
+          
+        
+      
     
   
 
\ No newline at end of file
diff --git a/examples/device/cdc_msc_freertos/Makefile b/examples/device/cdc_msc_freertos/Makefile
index 0bee668b7..84c833fb5 100644
--- a/examples/device/cdc_msc_freertos/Makefile
+++ b/examples/device/cdc_msc_freertos/Makefile
@@ -1,5 +1,3 @@
-DEPS_SUBMODULES += lib/FreeRTOS-Kernel
-
 include ../../make.mk
 
 FREERTOS_SRC = lib/FreeRTOS-Kernel
@@ -10,7 +8,7 @@ INC += \
 	src/FreeRTOSConfig \
 	$(TOP)/hw \
 	$(TOP)/$(FREERTOS_SRC)/include \
-	$(TOP)/$(FREERTOS_PORTABLE_SRC)
+	$(TOP)/$(FREERTOS_PORTABLE_SRC) \
 
 # Example source
 EXAMPLE_SOURCE = \
diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c
index 0b115a595..1dadc4513 100644
--- a/examples/device/cdc_msc_freertos/src/main.c
+++ b/examples/device/cdc_msc_freertos/src/main.c
@@ -41,6 +41,7 @@
 
   #define USBD_STACK_SIZE     4096
 #else
+
   #include "FreeRTOS.h"
   #include "semphr.h"
   #include "queue.h"
@@ -54,7 +55,7 @@
 #define CDC_STACK_SZIE      configMINIMAL_STACK_SIZE
 
 //--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF PROTYPES
+// MACRO CONSTANT TYPEDEF PROTOTYPES
 //--------------------------------------------------------------------+
 
 /* Blink pattern
@@ -62,7 +63,7 @@
  * - 1000 ms : device mounted
  * - 2500 ms : device is suspended
  */
-enum  {
+enum {
   BLINK_NOT_MOUNTED = 250,
   BLINK_MOUNTED = 1000,
   BLINK_SUSPENDED = 2500,
@@ -81,16 +82,15 @@ StaticTask_t cdc_taskdef;
 
 TimerHandle_t blinky_tm;
 
-void led_blinky_cb(TimerHandle_t xTimer);
-void usb_device_task(void* param);
-void cdc_task(void* params);
+static void led_blinky_cb(TimerHandle_t xTimer);
+static void usb_device_task(void *param);
+void cdc_task(void *params);
 
 //--------------------------------------------------------------------+
 // Main
 //--------------------------------------------------------------------+
 
-int main(void)
-{
+int main(void) {
   board_init();
 
 #if configSUPPORT_STATIC_ALLOCATION
@@ -104,8 +104,8 @@ int main(void)
   xTaskCreateStatic(cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
 #else
   blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb);
-  xTaskCreate( usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, NULL);
-  xTaskCreate( cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, NULL);
+  xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+  xTaskCreate(cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL);
 #endif
 
   xTimerStart(blinky_tm, 0);
@@ -119,16 +119,14 @@ int main(void)
 }
 
 #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
-void app_main(void)
-{
+void app_main(void) {
   main();
 }
 #endif
 
 // USB Device Driver task
 // This top level thread process all usb events and invoke callbacks
-void usb_device_task(void* param)
-{
+static void usb_device_task(void *param) {
   (void) param;
 
   // init device stack on configured roothub port
@@ -141,8 +139,7 @@ void usb_device_task(void* param)
   }
 
   // RTOS forever loop
-  while (1)
-  {
+  while (1) {
     // put this thread to waiting state until there is new events
     tud_task();
 
@@ -156,35 +153,28 @@ void usb_device_task(void* param)
 //--------------------------------------------------------------------+
 
 // Invoked when device is mounted
-void tud_mount_cb(void)
-{
+void tud_mount_cb(void) {
   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0);
 }
 
 // Invoked when device is unmounted
-void tud_umount_cb(void)
-{
+void tud_umount_cb(void) {
   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), 0);
 }
 
 // Invoked when usb bus is suspended
 // remote_wakeup_en : if host allow us  to perform remote wakeup
 // Within 7ms, device must draw an average of current less than 2.5 mA from bus
-void tud_suspend_cb(bool remote_wakeup_en)
-{
+void tud_suspend_cb(bool remote_wakeup_en) {
   (void) remote_wakeup_en;
   xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_SUSPENDED), 0);
 }
 
 // Invoked when usb bus is resumed
-void tud_resume_cb(void)
-{
-  if (tud_mounted())
-  {
+void tud_resume_cb(void) {
+  if (tud_mounted()) {
     xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_MOUNTED), 0);
-  }
-  else
-  {
+  } else {
     xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), 0);
   }
 }
@@ -192,20 +182,17 @@ void tud_resume_cb(void)
 //--------------------------------------------------------------------+
 // USB CDC
 //--------------------------------------------------------------------+
-void cdc_task(void* params)
-{
+void cdc_task(void *params) {
   (void) params;
 
   // RTOS forever loop
-  while ( 1 )
-  {
+  while (1) {
     // connected() check for DTR bit
     // Most but not all terminal client set this when making connection
     // if ( tud_cdc_connected() )
     {
       // There are data available
-      while ( tud_cdc_available() )
-      {
+      while (tud_cdc_available()) {
         uint8_t buf[64];
 
         // read and echo back
@@ -228,32 +215,27 @@ void cdc_task(void* params)
 }
 
 // Invoked when cdc when line state changed e.g connected/disconnected
-void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
-{
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
   (void) itf;
   (void) rts;
 
   // TODO set some indicator
-  if ( dtr )
-  {
+  if (dtr) {
     // Terminal connected
-  }else
-  {
+  } else {
     // Terminal disconnected
   }
 }
 
 // Invoked when CDC interface received data from host
-void tud_cdc_rx_cb(uint8_t itf)
-{
+void tud_cdc_rx_cb(uint8_t itf) {
   (void) itf;
 }
 
 //--------------------------------------------------------------------+
 // BLINKING TASK
 //--------------------------------------------------------------------+
-void led_blinky_cb(TimerHandle_t xTimer)
-{
+static void led_blinky_cb(TimerHandle_t xTimer) {
   (void) xTimer;
   static bool led_state = false;
 
diff --git a/examples/host/bare_api/CMakeLists.txt b/examples/host/bare_api/CMakeLists.txt
index 05398b079..76182d6fa 100644
--- a/examples/host/bare_api/CMakeLists.txt
+++ b/examples/host/bare_api/CMakeLists.txt
@@ -10,6 +10,11 @@ project(${PROJECT} C CXX ASM)
 # Checks this example is valid for the family and initializes the project
 family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
 
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+  return()
+endif()
+
 add_executable(${PROJECT})
 
 # Example source
diff --git a/examples/host/bare_api/Makefile b/examples/host/bare_api/Makefile
index 058307c40..161f8c774 100644
--- a/examples/host/bare_api/Makefile
+++ b/examples/host/bare_api/Makefile
@@ -10,14 +10,4 @@ EXAMPLE_SOURCE += \
 
 SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
 
-# TinyUSB Host Stack source
-SRC_C += \
-	src/class/cdc/cdc_host.c \
-	src/class/hid/hid_host.c \
-	src/class/msc/msc_host.c \
-	src/host/hub.c \
-	src/host/usbh.c \
-	src/portable/ohci/ohci.c \
-	src/portable/nxp/lpc17_40/hcd_lpc17_40.c
-
 include ../../rules.mk
diff --git a/examples/host/bare_api/only.txt b/examples/host/bare_api/only.txt
index 3837ac8a2..fee10f9e2 100644
--- a/examples/host/bare_api/only.txt
+++ b/examples/host/bare_api/only.txt
@@ -1,3 +1,4 @@
+mcu:KINETIS_KL
 mcu:LPC175X_6X
 mcu:LPC177X_8X
 mcu:LPC18XX
diff --git a/examples/host/cdc_msc_hid/CMakeLists.txt b/examples/host/cdc_msc_hid/CMakeLists.txt
index ad5751705..a7c372a34 100644
--- a/examples/host/cdc_msc_hid/CMakeLists.txt
+++ b/examples/host/cdc_msc_hid/CMakeLists.txt
@@ -10,6 +10,11 @@ project(${PROJECT} C CXX ASM)
 # Checks this example is valid for the family and initializes the project
 family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
 
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+  return()
+endif()
+
 add_executable(${PROJECT})
 
 # Example source
diff --git a/examples/host/cdc_msc_hid/Makefile b/examples/host/cdc_msc_hid/Makefile
index 7c16b39d3..15b8a5b31 100644
--- a/examples/host/cdc_msc_hid/Makefile
+++ b/examples/host/cdc_msc_hid/Makefile
@@ -13,14 +13,4 @@ EXAMPLE_SOURCE = \
 
 SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
 
-# TinyUSB Host Stack source
-SRC_C += \
-	src/class/cdc/cdc_host.c \
-	src/class/hid/hid_host.c \
-	src/class/msc/msc_host.c \
-	src/host/hub.c \
-	src/host/usbh.c \
-	src/portable/ohci/ohci.c \
-	src/portable/nxp/lpc17_40/hcd_lpc17_40.c
-
 include ../../rules.mk
diff --git a/examples/host/cdc_msc_hid/only.txt b/examples/host/cdc_msc_hid/only.txt
index 3837ac8a2..fee10f9e2 100644
--- a/examples/host/cdc_msc_hid/only.txt
+++ b/examples/host/cdc_msc_hid/only.txt
@@ -1,3 +1,4 @@
+mcu:KINETIS_KL
 mcu:LPC175X_6X
 mcu:LPC177X_8X
 mcu:LPC18XX
diff --git a/examples/host/cdc_msc_hid_freertos/CMakeLists.txt b/examples/host/cdc_msc_hid_freertos/CMakeLists.txt
new file mode 100644
index 000000000..2e95a18e0
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 3.17)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+# gets PROJECT name for the example (e.g. -)
+family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+
+project(${PROJECT} C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+  return()
+endif()
+
+add_executable(${PROJECT})
+
+# Example source
+target_sources(${PROJECT} PUBLIC
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_app.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/freertos_hook.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_app.c
+  )
+
+# Example include
+target_include_directories(${PROJECT} PUBLIC
+  ${CMAKE_CURRENT_SOURCE_DIR}/src
+  )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_host_example(${PROJECT} freertos)
diff --git a/examples/host/cdc_msc_hid_freertos/Makefile b/examples/host/cdc_msc_hid_freertos/Makefile
new file mode 100644
index 000000000..a9670b4f2
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/Makefile
@@ -0,0 +1,34 @@
+include ../../make.mk
+
+FREERTOS_SRC = lib/FreeRTOS-Kernel
+FREERTOS_PORTABLE_PATH= $(FREERTOS_SRC)/portable/$(if $(USE_IAR),IAR,GCC)
+
+INC += \
+	src \
+	src/FreeRTOSConfig \
+	$(TOP)/hw \
+	$(TOP)/$(FREERTOS_SRC)/include \
+	$(TOP)/$(FREERTOS_PORTABLE_SRC) \
+
+# Example source
+EXAMPLE_SOURCE = \
+  src/cdc_app.c \
+  src/freertos_hook.c \
+  src/hid_app.c \
+  src/main.c \
+  src/msc_app.c \
+
+SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+
+# FreeRTOS source, all files in port folder
+SRC_C += \
+	$(FREERTOS_SRC)/list.c \
+	$(FREERTOS_SRC)/queue.c \
+	$(FREERTOS_SRC)/tasks.c \
+	$(FREERTOS_SRC)/timers.c \
+	$(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.c))
+
+SRC_S += \
+	$(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.s))
+
+include ../../rules.mk
diff --git a/examples/host/cdc_msc_hid_freertos/only.txt b/examples/host/cdc_msc_hid_freertos/only.txt
new file mode 100644
index 000000000..3837ac8a2
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/only.txt
@@ -0,0 +1,13 @@
+mcu:LPC175X_6X
+mcu:LPC177X_8X
+mcu:LPC18XX
+mcu:LPC40XX
+mcu:LPC43XX
+mcu:MIMXRT1XXX
+mcu:MIMXRT10XX
+mcu:MIMXRT11XX
+mcu:RP2040
+mcu:MSP432E4
+mcu:RX65X
+mcu:RAXXX
+mcu:MAX3421
diff --git a/examples/host/cdc_msc_hid_freertos/src/CMakeLists.txt b/examples/host/cdc_msc_hid_freertos/src/CMakeLists.txt
new file mode 100644
index 000000000..6f057c106
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/src/CMakeLists.txt
@@ -0,0 +1,6 @@
+# This file is for ESP-IDF only
+idf_component_register(SRCS "cdc_app.c" "hid_app.c" "main.c" "msc_app.c"
+                    INCLUDE_DIRS "."
+                    REQUIRES boards tinyusb_src)
+
+target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error=format)
diff --git a/examples/host/cdc_msc_hid_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/host/cdc_msc_hid_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..a1fc8bb09
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,215 @@
+/*
+ * 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 MCU header
+#include "bsp/board_mcu.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
+  #error "ESP32-Sx should use IDF's FreeRTOSConfig.h"
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+// TODO fix later
+// FIXME cause redundant-decls warnings
+#if CFG_TUSB_MCU == OPT_MCU_MM32F327X
+  extern u32 SystemCoreClock;
+#else
+  extern uint32_t SystemCoreClock;
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU								        0
+#define configENABLE_FPU								        1
+#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
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#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      0
+#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
+
+/* Define to trap errors during development. */
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
+  #define configASSERT(_exp) \
+    do {\
+      if ( !(_exp) ) { \
+        volatile uint32_t* ARM_CM_DHCSR =  ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
+        if ( (*ARM_CM_DHCSR) & 1UL ) {  /* Only halt mcu if debugger is attached */ \
+          taskDISABLE_INTERRUPTS(); \
+           __asm("BKPT #0\n"); \
+        }\
+      }\
+    } while(0)
+#else
+  #define configASSERT( x )
+#endif
+
+#ifdef __RX__
+/* Renesas RX series */
+#define vSoftwareInterruptISR					        INT_Excep_ICU_SWINT
+#define vTickISR								              INT_Excep_CMT0_CMI0
+#define configPERIPHERAL_CLOCK_HZ				      (configCPU_CLOCK_HZ/2)
+#define configKERNEL_INTERRUPT_PRIORITY			  1
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY	4
+
+#else
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+#if defined(__NVIC_PRIO_BITS)
+  // For Cortex-M specific: __NVIC_PRIO_BITS is defined in core_cmx.h
+	#define configPRIO_BITS       __NVIC_PRIO_BITS
+
+#elif defined(__ECLIC_INTCTLBITS)
+  // RISC-V Bumblebee core from nuclei
+  #define configPRIO_BITS       __ECLIC_INTCTLBITS
+
+#elif defined(__IASMARM__)
+  // FIXME: IAR Assembler cannot include mcu header directly to get __NVIC_PRIO_BITS.
+  // Therefore we will hard coded it to minimum value of 2 to get pass ci build.
+  // IAR user must update this to correct value of the target MCU
+  #message "configPRIO_BITS is hard coded to 2 to pass IAR build only. User should update it per MCU"
+  #define configPRIO_BITS       2
+
+#else
+  #error "FreeRTOS configPRIO_BITS to be defined"
+#endif
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1< cdc interfaces
+          tuh_cdc_write(idx, buf, count);
+          tuh_cdc_write_flush(idx);
+        }
+      }
+    }
+
+    vTaskDelay(1);
+  }
+}
+
+//--------------------------------------------------------------------+
+// TinyUSB Callbacks
+//--------------------------------------------------------------------+
+
+// Invoked when received new data
+void tuh_cdc_rx_cb(uint8_t idx) {
+  uint8_t buf[64 + 1]; // +1 for extra null character
+  uint32_t const bufsize = sizeof(buf) - 1;
+
+  // forward cdc interfaces -> console
+  uint32_t count = tuh_cdc_read(idx, buf, bufsize);
+  buf[count] = 0;
+
+  printf((char *) buf);
+}
+
+void tuh_cdc_mount_cb(uint8_t idx) {
+  tuh_itf_info_t itf_info = { 0 };
+  tuh_cdc_itf_get_info(idx, &itf_info);
+
+  printf("CDC Interface is mounted: address = %u, itf_num = %u\r\n", itf_info.daddr, itf_info.desc.bInterfaceNumber);
+
+#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+  // CFG_TUH_CDC_LINE_CODING_ON_ENUM must be defined for line coding is set by tinyusb in enumeration
+  // otherwise you need to call tuh_cdc_set_line_coding() first
+  cdc_line_coding_t line_coding = { 0 };
+  if (tuh_cdc_get_local_line_coding(idx, &line_coding)) {
+    printf("  Baudrate: %lu, Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
+    printf("  Parity  : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits);
+  }
+#endif
+}
+
+void tuh_cdc_umount_cb(uint8_t idx) {
+  tuh_itf_info_t itf_info = { 0 };
+  tuh_cdc_itf_get_info(idx, &itf_info);
+
+  printf("CDC Interface is unmounted: address = %u, itf_num = %u\r\n", itf_info.daddr, itf_info.desc.bInterfaceNumber);
+}
diff --git a/examples/host/cdc_msc_hid_freertos/src/freertos_hook.c b/examples/host/cdc_msc_hid_freertos/src/freertos_hook.c
new file mode 100644
index 000000000..07d159fd5
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/src/freertos_hook.c
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ *
+ */
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "FreeRTOS.h"
+#include "task.h"
+#include "common/tusb_common.h"
+
+void vApplicationMallocFailedHook(void) {
+  taskDISABLE_INTERRUPTS();
+  TU_ASSERT(false,);
+}
+
+void vApplicationStackOverflowHook(xTaskHandle pxTask, char *pcTaskName) {
+  (void) pxTask;
+  (void) pcTaskName;
+
+  taskDISABLE_INTERRUPTS();
+  TU_ASSERT(false,);
+}
+
+/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an
+ * implementation of vApplicationGetIdleTaskMemory() to provide the memory that is
+ * used by the Idle task. */
+void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer,
+                                   uint32_t *pulIdleTaskStackSize) {
+  /* If the buffers to be provided to the Idle task are declared inside this
+   * function then they must be declared static - otherwise they will be allocated on
+   * the stack and so not exists after this function exits. */
+  static StaticTask_t xIdleTaskTCB;
+  static StackType_t uxIdleTaskStack[configMINIMAL_STACK_SIZE];
+
+  /* Pass out a pointer to the StaticTask_t structure in which the Idle task's
+    state will be stored. */
+  *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
+
+  /* Pass out the array that will be used as the Idle task's stack. */
+  *ppxIdleTaskStackBuffer = uxIdleTaskStack;
+
+  /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
+    Note that, as the array is necessarily of type StackType_t,
+    configMINIMAL_STACK_SIZE is specified in words, not bytes. */
+  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
+}
+
+/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
+ * application must provide an implementation of vApplicationGetTimerTaskMemory()
+ * to provide the memory that is used by the Timer service task. */
+void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer,
+                                    uint32_t *pulTimerTaskStackSize) {
+  /* If the buffers to be provided to the Timer task are declared inside this
+   * function then they must be declared static - otherwise they will be allocated on
+   * the stack and so not exists after this function exits. */
+  static StaticTask_t xTimerTaskTCB;
+  static StackType_t uxTimerTaskStack[configTIMER_TASK_STACK_DEPTH];
+
+  /* Pass out a pointer to the StaticTask_t structure in which the Timer
+    task's state will be stored. */
+  *ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
+
+  /* Pass out the array that will be used as the Timer task's stack. */
+  *ppxTimerTaskStackBuffer = uxTimerTaskStack;
+
+  /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
+    Note that, as the array is necessarily of type StackType_t,
+    configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */
+  *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
+}
+
+#if CFG_TUSB_MCU == OPT_MCU_RX63X | CFG_TUSB_MCU == OPT_MCU_RX65X
+#include "iodefine.h"
+void vApplicationSetupTimerInterrupt(void)
+{
+  /* Enable CMT0 */
+  SYSTEM.PRCR.WORD = (0xA5u<<8) | TU_BIT(1);
+  MSTP(CMT0)       = 0;
+  SYSTEM.PRCR.WORD = (0xA5u<<8);
+
+  CMT0.CMCNT      = 0;
+  CMT0.CMCOR      = (unsigned short)(((configPERIPHERAL_CLOCK_HZ/configTICK_RATE_HZ)-1)/128);
+  CMT0.CMCR.WORD  = TU_BIT(6) | 2;
+  IR(CMT0, CMI0)  = 0;
+  IPR(CMT0, CMI0) = configKERNEL_INTERRUPT_PRIORITY;
+  IEN(CMT0, CMI0) = 1;
+  CMT.CMSTR0.BIT.STR0 = 1;
+}
+#endif
diff --git a/examples/host/cdc_msc_hid_freertos/src/hid_app.c b/examples/host/cdc_msc_hid_freertos/src/hid_app.c
new file mode 100644
index 000000000..9ea5c1be0
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/src/hid_app.c
@@ -0,0 +1,267 @@
+/*
+ * 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.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+// If your host terminal support ansi escape code such as TeraTerm
+// it can be use to simulate mouse cursor movement within terminal
+#define USE_ANSI_ESCAPE   0
+
+#define MAX_REPORT  4
+
+static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII };
+
+// Each HID instance can has multiple reports
+static struct {
+  uint8_t report_count;
+  tuh_hid_report_info_t report_info[MAX_REPORT];
+} hid_info[CFG_TUH_HID];
+
+static void process_kbd_report(hid_keyboard_report_t const *report);
+static void process_mouse_report(hid_mouse_report_t const *report);
+static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len);
+
+void hid_app_init(void) {
+  // nothing to do
+}
+
+//--------------------------------------------------------------------+
+// TinyUSB Callbacks
+//--------------------------------------------------------------------+
+
+// Invoked when device with hid interface is mounted
+// Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
+// can be used to parse common/simple enough descriptor.
+// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
+// therefore report_desc = NULL, desc_len = 0
+void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
+  printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
+
+  // Interface protocol (hid_interface_protocol_enum_t)
+  const char *protocol_str[] = { "None", "Keyboard", "Mouse" };
+  uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
+
+  printf("HID Interface Protocol = %s\r\n", protocol_str[itf_protocol]);
+
+  // By default host stack will use activate boot protocol on supported interface.
+  // Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser)
+  if (itf_protocol == HID_ITF_PROTOCOL_NONE) {
+    hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT,
+                                                                      desc_report, desc_len);
+    printf("HID has %u reports \r\n", hid_info[instance].report_count);
+  }
+
+  // request to receive report
+  // tuh_hid_report_received_cb() will be invoked when report is available
+  if (!tuh_hid_receive_report(dev_addr, instance)) {
+    printf("Error: cannot request to receive report\r\n");
+  }
+}
+
+// Invoked when device with hid interface is un-mounted
+void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
+  printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
+}
+
+// Invoked when received report from device via interrupt endpoint
+void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
+  uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
+
+  switch (itf_protocol) {
+    case HID_ITF_PROTOCOL_KEYBOARD:
+      TU_LOG2("HID receive boot keyboard report\r\n");
+      process_kbd_report((hid_keyboard_report_t const *) report);
+      break;
+
+    case HID_ITF_PROTOCOL_MOUSE:
+      TU_LOG2("HID receive boot mouse report\r\n");
+      process_mouse_report((hid_mouse_report_t const *) report);
+      break;
+
+    default:
+      // Generic report requires matching ReportID and contents with previous parsed report info
+      process_generic_report(dev_addr, instance, report, len);
+      break;
+  }
+
+  // continue to request to receive report
+  if (!tuh_hid_receive_report(dev_addr, instance)) {
+    printf("Error: cannot request to receive report\r\n");
+  }
+}
+
+//--------------------------------------------------------------------+
+// Keyboard
+//--------------------------------------------------------------------+
+
+// look up new key in previous keys
+static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8_t keycode) {
+  for (uint8_t i = 0; i < 6; i++) {
+    if (report->keycode[i] == keycode) return true;
+  }
+
+  return false;
+}
+
+static void process_kbd_report(hid_keyboard_report_t const *report) {
+  static hid_keyboard_report_t prev_report = { 0, 0, { 0 } }; // previous report to check key released
+
+  //------------- example code ignore control (non-printable) key affects -------------//
+  for (uint8_t i = 0; i < 6; i++) {
+    if (report->keycode[i]) {
+      if (find_key_in_report(&prev_report, report->keycode[i])) {
+        // exist in previous report means the current key is holding
+      } else {
+        // not existed in previous report means the current key is pressed
+        bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT);
+        uint8_t ch = keycode2ascii[report->keycode[i]][is_shift ? 1 : 0];
+        putchar(ch);
+        if (ch == '\r') putchar('\n'); // added new line for enter key
+
+        #ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
+        fflush(stdout); // flush right away, else nanolib will wait for newline
+        #endif
+      }
+    }
+    // TODO example skips key released
+  }
+
+  prev_report = *report;
+}
+
+//--------------------------------------------------------------------+
+// Mouse
+//--------------------------------------------------------------------+
+
+void cursor_movement(int8_t x, int8_t y, int8_t wheel) {
+#if USE_ANSI_ESCAPE
+  // Move X using ansi escape
+  if ( x < 0) {
+    printf(ANSI_CURSOR_BACKWARD(%d), (-x)); // move left
+  }else if ( x > 0) {
+    printf(ANSI_CURSOR_FORWARD(%d), x); // move right
+  }
+
+  // Move Y using ansi escape
+  if ( y < 0) {
+    printf(ANSI_CURSOR_UP(%d), (-y)); // move up
+  }else if ( y > 0) {
+    printf(ANSI_CURSOR_DOWN(%d), y); // move down
+  }
+
+  // Scroll using ansi escape
+  if (wheel < 0) {
+    printf(ANSI_SCROLL_UP(%d), (-wheel)); // scroll up
+  }else if (wheel > 0) {
+    printf(ANSI_SCROLL_DOWN(%d), wheel); // scroll down
+  }
+
+  printf("\r\n");
+#else
+  printf("(%d %d %d)\r\n", x, y, wheel);
+#endif
+}
+
+static void process_mouse_report(hid_mouse_report_t const *report) {
+  static hid_mouse_report_t prev_report = { 0 };
+
+  //------------- button state  -------------//
+  uint8_t button_changed_mask = report->buttons ^ prev_report.buttons;
+  if (button_changed_mask & report->buttons) {
+    printf(" %c%c%c ",
+           report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-',
+           report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-',
+           report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-');
+  }
+
+  //------------- cursor movement -------------//
+  cursor_movement(report->x, report->y, report->wheel);
+}
+
+//--------------------------------------------------------------------+
+// Generic Report
+//--------------------------------------------------------------------+
+static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
+  (void) dev_addr;
+
+  uint8_t const rpt_count = hid_info[instance].report_count;
+  tuh_hid_report_info_t *rpt_info_arr = hid_info[instance].report_info;
+  tuh_hid_report_info_t *rpt_info = NULL;
+
+  if (rpt_count == 1 && rpt_info_arr[0].report_id == 0) {
+    // Simple report without report ID as 1st byte
+    rpt_info = &rpt_info_arr[0];
+  } else {
+    // Composite report, 1st byte is report ID, data starts from 2nd byte
+    uint8_t const rpt_id = report[0];
+
+    // Find report id in the array
+    for (uint8_t i = 0; i < rpt_count; i++) {
+      if (rpt_id == rpt_info_arr[i].report_id) {
+        rpt_info = &rpt_info_arr[i];
+        break;
+      }
+    }
+
+    report++;
+    len--;
+  }
+
+  if (!rpt_info) {
+    printf("Couldn't find report info !\r\n");
+    return;
+  }
+
+  // For complete list of Usage Page & Usage checkout src/class/hid/hid.h. For examples:
+  // - Keyboard                     : Desktop, Keyboard
+  // - Mouse                        : Desktop, Mouse
+  // - Gamepad                      : Desktop, Gamepad
+  // - Consumer Control (Media Key) : Consumer, Consumer Control
+  // - System Control (Power key)   : Desktop, System Control
+  // - Generic (vendor)             : 0xFFxx, xx
+  if (rpt_info->usage_page == HID_USAGE_PAGE_DESKTOP) {
+    switch (rpt_info->usage) {
+      case HID_USAGE_DESKTOP_KEYBOARD:
+        TU_LOG1("HID receive keyboard report\r\n");
+        // Assume keyboard follow boot report layout
+        process_kbd_report((hid_keyboard_report_t const *) report);
+        break;
+
+      case HID_USAGE_DESKTOP_MOUSE:
+        TU_LOG1("HID receive mouse report\r\n");
+        // Assume mouse follow boot report layout
+        process_mouse_report((hid_mouse_report_t const *) report);
+        break;
+
+      default:
+        break;
+    }
+  }
+}
diff --git a/examples/host/cdc_msc_hid_freertos/src/main.c b/examples/host/cdc_msc_hid_freertos/src/main.c
new file mode 100644
index 000000000..691ff3e29
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/src/main.c
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ *
+ */
+
+#include 
+#include 
+#include 
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+  // ESP-IDF need "freertos/" prefix in include path.
+  // CFG_TUSB_OS_INC_PATH should be defined accordingly.
+  #include "freertos/FreeRTOS.h"
+  #include "freertos/semphr.h"
+  #include "freertos/queue.h"
+  #include "freertos/task.h"
+  #include "freertos/timers.h"
+
+  #define USBH_STACK_SIZE     4096
+#else
+  #include "FreeRTOS.h"
+  #include "semphr.h"
+  #include "queue.h"
+  #include "task.h"
+  #include "timers.h"
+
+  // Increase stack size when debug log is enabled
+  #define USBH_STACK_SIZE    (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+#endif
+
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTOTYPES
+//--------------------------------------------------------------------+
+/* Blink pattern
+ * - 250 ms  : device not mounted
+ * - 1000 ms : device mounted
+ * - 2500 ms : device is suspended
+ */
+enum  {
+  BLINK_NOT_MOUNTED = 250,
+  BLINK_MOUNTED = 1000,
+  BLINK_SUSPENDED = 2500,
+};
+
+// static timer & task
+#if configSUPPORT_STATIC_ALLOCATION
+StaticTimer_t blinky_tmdef;
+
+StackType_t  usb_host_stack[USBH_STACK_SIZE];
+StaticTask_t usb_host_taskdef;
+#endif
+
+TimerHandle_t blinky_tm;
+
+static void led_blinky_cb(TimerHandle_t xTimer);
+static void usb_host_task(void* param);
+
+extern void cdc_app_init(void);
+extern void hid_app_init(void);
+extern void msc_app_init(void);
+
+/*------------- MAIN -------------*/
+int main(void) {
+  board_init();
+
+  printf("TinyUSB Host CDC MSC HID with FreeRTOS Example\r\n");
+
+  // Create soft timer for blinky, task for tinyusb stack
+#if configSUPPORT_STATIC_ALLOCATION
+  blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef);
+  xTaskCreateStatic(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_host_stack, &usb_host_taskdef);
+#else
+  blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_NOT_MOUNTED), true, NULL, led_blinky_cb);
+  xTaskCreate(usb_host_task, "usbd", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES-1, NULL);
+#endif
+
+  xTimerStart(blinky_tm, 0);
+
+  // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
+#if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+  vTaskStartScheduler();
+#endif
+
+  return 0;
+}
+
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+void app_main(void) {
+  main();
+}
+#endif
+
+// USB Host task
+// This top level thread process all usb events and invoke callbacks
+static void usb_host_task(void *param) {
+  (void) param;
+
+  // init host stack on configured roothub port
+  tuh_init(BOARD_TUH_RHPORT);
+
+  if (board_init_after_tusb) {
+    board_init_after_tusb();
+  }
+
+  cdc_app_init();
+  hid_app_init();
+  msc_app_init();
+
+  // RTOS forever loop
+  while (1) {
+    // put this thread to waiting state until there is new events
+    tuh_task();
+
+    // following code only run if tuh_task() process at least 1 event
+  }
+}
+
+//--------------------------------------------------------------------+
+// TinyUSB Callbacks
+//--------------------------------------------------------------------+
+
+void tuh_mount_cb(uint8_t dev_addr) {
+  // application set-up
+  printf("A device with address %d is mounted\r\n", dev_addr);
+}
+
+void tuh_umount_cb(uint8_t dev_addr) {
+  // application tear-down
+  printf("A device with address %d is unmounted \r\n", dev_addr);
+}
+
+//--------------------------------------------------------------------+
+// BLINKING TASK
+//--------------------------------------------------------------------+
+static void led_blinky_cb(TimerHandle_t xTimer) {
+  (void) xTimer;
+  static bool led_state = false;
+
+  board_led_write(led_state);
+  led_state = 1 - led_state; // toggle
+}
diff --git a/examples/host/cdc_msc_hid_freertos/src/msc_app.c b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
new file mode 100644
index 000000000..ee02ba917
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ *
+ */
+
+#include "tusb.h"
+
+static scsi_inquiry_resp_t inquiry_resp;
+
+void msc_app_init(void) {
+  // nothing to do
+}
+
+bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const *cb_data) {
+  msc_cbw_t const *cbw = cb_data->cbw;
+  msc_csw_t const *csw = cb_data->csw;
+
+  if (csw->status != 0) {
+    printf("Inquiry failed\r\n");
+    return false;
+  }
+
+  // Print out Vendor ID, Product ID and Rev
+  printf("%.8s %.16s rev %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev);
+
+  // Get capacity of device
+  uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
+  uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
+
+  printf("Disk Size: %lu MB\r\n", block_count / ((1024 * 1024) / block_size));
+  printf("Block Count = %lu, Block Size: %lu\r\n", block_count, block_size);
+
+  return true;
+}
+
+//------------- IMPLEMENTATION -------------//
+void tuh_msc_mount_cb(uint8_t dev_addr) {
+  printf("A MassStorage device is mounted\r\n");
+
+  uint8_t const lun = 0;
+  tuh_msc_inquiry(dev_addr, lun, &inquiry_resp, inquiry_complete_cb, 0);
+}
+
+void tuh_msc_umount_cb(uint8_t dev_addr) {
+  (void) dev_addr;
+  printf("A MassStorage device is unmounted\r\n");
+}
diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
new file mode 100644
index 000000000..1bed9a9b3
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+  // change to 1 if using pico-pio-usb as host controller for raspberry rp2040
+  #define CFG_TUH_RPI_PIO_USB   0
+  #define BOARD_TUH_RHPORT      CFG_TUH_RPI_PIO_USB
+#endif
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT      0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED   OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS           OPT_OS_FREERTOS
+#endif
+
+// Espressif IDF requires "freertos/" prefix in include path
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#define CFG_TUSB_OS_INC_PATH  freertos/
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG        0
+#endif
+
+// Enable Host stack
+#define CFG_TUH_ENABLED       1
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED     BOARD_TUH_MAX_SPEED
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUH_MEM_SECTION
+#define CFG_TUH_MEM_SECTION
+#endif
+
+#ifndef CFG_TUH_MEM_ALIGN
+#define CFG_TUH_MEM_ALIGN        __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// CONFIGURATION
+//--------------------------------------------------------------------
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+#define CFG_TUH_HUB                 1 // number of supported hubs
+#define CFG_TUH_CDC                 1 // CDC ACM
+#define CFG_TUH_CDC_FTDI            1 // FTDI Serial.  FTDI is not part of CDC class, only to re-use CDC driver API
+#define CFG_TUH_CDC_CP210X          1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API
+#define CFG_TUH_HID                 (3*CFG_TUH_DEVICE_MAX) // typical keyboard + mouse device can have 3-4 HID interfaces
+#define CFG_TUH_MSC                 1
+#define CFG_TUH_VENDOR              0
+
+// max device support (excluding hub device): 1 hub typically has 4 ports
+#define CFG_TUH_DEVICE_MAX          (3*CFG_TUH_HUB + 1)
+
+//------------- HID -------------//
+#define CFG_TUH_HID_EPIN_BUFSIZE    64
+#define CFG_TUH_HID_EPOUT_BUFSIZE   64
+
+//------------- CDC -------------//
+
+// Set Line Control state on enumeration/mounted:
+// DTR ( bit 0), RTS (bit 1)
+#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM    0x03
+
+// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
+// bit rate = 115200, 1 stop bit, no parity, 8 bit data width
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM   { 115200, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/examples/host/hid_controller/CMakeLists.txt b/examples/host/hid_controller/CMakeLists.txt
index 3fb630aaa..c1b500dd8 100644
--- a/examples/host/hid_controller/CMakeLists.txt
+++ b/examples/host/hid_controller/CMakeLists.txt
@@ -10,6 +10,11 @@ project(${PROJECT} C CXX ASM)
 # Checks this example is valid for the family and initializes the project
 family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
 
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+  return()
+endif()
+
 add_executable(${PROJECT})
 
 # Example source
diff --git a/examples/host/hid_controller/Makefile b/examples/host/hid_controller/Makefile
index cda2977bc..e7f603f25 100644
--- a/examples/host/hid_controller/Makefile
+++ b/examples/host/hid_controller/Makefile
@@ -11,14 +11,4 @@ EXAMPLE_SOURCE += \
 
 SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
 
-# TinyUSB Host Stack source
-SRC_C += \
-	src/class/cdc/cdc_host.c \
-	src/class/hid/hid_host.c \
-	src/class/msc/msc_host.c \
-	src/host/hub.c \
-	src/host/usbh.c \
-	src/portable/ohci/ohci.c \
-	src/portable/nxp/lpc17_40/hcd_lpc17_40.c
-
 include ../../rules.mk
diff --git a/examples/host/hid_controller/only.txt b/examples/host/hid_controller/only.txt
index 3837ac8a2..fee10f9e2 100644
--- a/examples/host/hid_controller/only.txt
+++ b/examples/host/hid_controller/only.txt
@@ -1,3 +1,4 @@
+mcu:KINETIS_KL
 mcu:LPC175X_6X
 mcu:LPC177X_8X
 mcu:LPC18XX
diff --git a/examples/host/msc_file_explorer/CMakeLists.txt b/examples/host/msc_file_explorer/CMakeLists.txt
index 1868b632e..1a57c7466 100644
--- a/examples/host/msc_file_explorer/CMakeLists.txt
+++ b/examples/host/msc_file_explorer/CMakeLists.txt
@@ -10,6 +10,11 @@ project(${PROJECT} C CXX ASM)
 # Checks this example is valid for the family and initializes the project
 family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
 
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+  return()
+endif()
+
 add_executable(${PROJECT})
 
 # Example source
diff --git a/examples/host/msc_file_explorer/Makefile b/examples/host/msc_file_explorer/Makefile
index 1fda72b18..8319d3c2b 100644
--- a/examples/host/msc_file_explorer/Makefile
+++ b/examples/host/msc_file_explorer/Makefile
@@ -24,14 +24,4 @@ SRC_C += \
 # suppress warning caused by fatfs
 CFLAGS += -Wno-error=cast-qual
 
-# TinyUSB Host Stack source
-SRC_C += \
-	src/class/cdc/cdc_host.c \
-	src/class/hid/hid_host.c \
-	src/class/msc/msc_host.c \
-	src/host/hub.c \
-	src/host/usbh.c \
-	src/portable/ohci/ohci.c \
-	src/portable/nxp/lpc17_40/hcd_lpc17_40.c
-
 include ../../rules.mk
diff --git a/examples/host/msc_file_explorer/only.txt b/examples/host/msc_file_explorer/only.txt
index 3837ac8a2..fee10f9e2 100644
--- a/examples/host/msc_file_explorer/only.txt
+++ b/examples/host/msc_file_explorer/only.txt
@@ -1,3 +1,4 @@
+mcu:KINETIS_KL
 mcu:LPC175X_6X
 mcu:LPC177X_8X
 mcu:LPC18XX
diff --git a/examples/make.mk b/examples/make.mk
index 8fe4a3486..448d7883d 100644
--- a/examples/make.mk
+++ b/examples/make.mk
@@ -26,9 +26,6 @@ ifeq '$(findstring ;,$(PATH))' ';'
 CMDEXE := 1
 
 # makefile shell commands should use syntax for DOS CMD, not unix sh
-# Unfortunately, SHELL may point to sh or bash, which can't accept DOS syntax.
-# We can't just use sh, because while sh and/or bash shell may be available,
-# many Windows environments won't have utilities like realpath used below, so...
 # Force DOS command shell on Windows.
 SHELL := cmd.exe
 endif
diff --git a/hw/bsp/espressif/boards/CMakeLists.txt b/hw/bsp/espressif/boards/CMakeLists.txt
index 325263c1d..8209e8747 100644
--- a/hw/bsp/espressif/boards/CMakeLists.txt
+++ b/hw/bsp/espressif/boards/CMakeLists.txt
@@ -4,3 +4,5 @@ idf_component_register(SRCS family.c
                     INCLUDE_DIRS "." ${BOARD} ${hw_dir}
                     PRIV_REQUIRES "driver"
                     REQUIRES led_strip src tinyusb_src)
+
+target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error=format)
diff --git a/hw/bsp/espressif/boards/adafruit_metro_esp32s2/board.h b/hw/bsp/espressif/boards/adafruit_metro_esp32s2/board.h
index 2ec80ef47..44ff11aa0 100644
--- a/hw/bsp/espressif/boards/adafruit_metro_esp32s2/board.h
+++ b/hw/bsp/espressif/boards/adafruit_metro_esp32s2/board.h
@@ -36,6 +36,14 @@
 #define BUTTON_PIN            0
 #define BUTTON_STATE_ACTIVE   0
 
+// SPI for USB host shield
+#define MAX3421_SPI_HOST  SPI2_HOST
+#define MAX3421_SCK_PIN  36
+#define MAX3421_MOSI_PIN 35
+#define MAX3421_MISO_PIN 37
+#define MAX3421_CS_PIN   15
+#define MAX3421_INTR_PIN 14
+
 #ifdef __cplusplus
  }
 #endif
diff --git a/hw/bsp/espressif/boards/espressif_kaluga_1/board.h b/hw/bsp/espressif/boards/espressif_kaluga_1/board.h
index 0acb9c439..613e6ae0c 100644
--- a/hw/bsp/espressif/boards/espressif_kaluga_1/board.h
+++ b/hw/bsp/espressif/boards/espressif_kaluga_1/board.h
@@ -37,6 +37,14 @@
 #define BUTTON_PIN            0
 #define BUTTON_STATE_ACTIVE   0
 
+// SPI for USB host shield
+#define MAX3421_SPI_HOST  SPI2_HOST
+#define MAX3421_SCK_PIN  36
+#define MAX3421_MOSI_PIN 35
+#define MAX3421_MISO_PIN 37
+#define MAX3421_CS_PIN   15
+#define MAX3421_INTR_PIN 14
+
 #ifdef __cplusplus
  }
 #endif
diff --git a/hw/bsp/espressif/boards/espressif_s2_devkitc/board.cmake b/hw/bsp/espressif/boards/espressif_s2_devkitc/board.cmake
new file mode 100644
index 000000000..abbdf7abc
--- /dev/null
+++ b/hw/bsp/espressif/boards/espressif_s2_devkitc/board.cmake
@@ -0,0 +1,2 @@
+# Apply board specific content here
+set(IDF_TARGET "esp32s2")
diff --git a/src/common/tusb_timeout.h b/hw/bsp/espressif/boards/espressif_s2_devkitc/board.h
similarity index 57%
rename from src/common/tusb_timeout.h
rename to hw/bsp/espressif/boards/espressif_s2_devkitc/board.h
index 533e67ab8..e068efef9 100644
--- a/src/common/tusb_timeout.h
+++ b/hw/bsp/espressif/boards/espressif_s2_devkitc/board.h
@@ -1,7 +1,7 @@
 /*
  * The MIT License (MIT)
  *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020, 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
@@ -24,57 +24,22 @@
  * This file is part of the TinyUSB stack.
  */
 
-/** \ingroup Group_Common Common Files
- *  \defgroup Group_TimeoutTimer timeout timer
- *  @{ */
-
-#ifndef _TUSB_TIMEOUT_H_
-#define _TUSB_TIMEOUT_H_
-
-#include 
-#include 
+#ifndef BOARD_H_
+#define BOARD_H_
 
 #ifdef __cplusplus
-extern "C" {
+ extern "C" {
 #endif
 
-typedef struct {
-  uint32_t start;
-  uint32_t interval;
-}tu_timeout_t;
+// Note: On the production version (v1.2) WS2812 is connected to GPIO 18,
+// however earlier revision v1.1 WS2812 is connected to GPIO 17
+#define NEOPIXEL_PIN          18
 
-#if 0
-
-extern uint32_t tusb_hal_millis(void);
-
-static inline void tu_timeout_set(tu_timeout_t* tt, uint32_t msec)
-{
-  tt->interval = msec;
-  tt->start    = tusb_hal_millis();
-}
-
-static inline bool tu_timeout_expired(tu_timeout_t* tt)
-{
-  return ( tusb_hal_millis() - tt->start ) >= tt->interval;
-}
-
-// For used with periodic event to prevent drift
-static inline void tu_timeout_reset(tu_timeout_t* tt)
-{
-  tt->start += tt->interval;
-}
-
-static inline void tu_timeout_restart(tu_timeout_t* tt)
-{
-  tt->start = tusb_hal_millis();
-}
-
-#endif
+#define BUTTON_PIN            0
+#define BUTTON_STATE_ACTIVE   0
 
 #ifdef __cplusplus
  }
 #endif
 
-#endif /* _TUSB_TIMEOUT_H_ */
-
-/** @} */
+#endif /* BOARD_H_ */
diff --git a/hw/bsp/espressif/boards/espressif_s3_devkitm/board.h b/hw/bsp/espressif/boards/espressif_s3_devkitm/board.h
index fe33b5c43..4b4151e72 100644
--- a/hw/bsp/espressif/boards/espressif_s3_devkitm/board.h
+++ b/hw/bsp/espressif/boards/espressif_s3_devkitm/board.h
@@ -36,6 +36,14 @@
 #define BUTTON_PIN            0
 #define BUTTON_STATE_ACTIVE   0
 
+// SPI for USB host shield
+#define MAX3421_SPI_HOST  SPI2_HOST
+#define MAX3421_SCK_PIN  36
+#define MAX3421_MOSI_PIN 35
+#define MAX3421_MISO_PIN 37
+#define MAX3421_CS_PIN   15
+#define MAX3421_INTR_PIN 14
+
 #ifdef __cplusplus
  }
 #endif
diff --git a/hw/bsp/espressif/boards/family.c b/hw/bsp/espressif/boards/family.c
index 8f857fb71..912ca5f35 100644
--- a/hw/bsp/espressif/boards/family.c
+++ b/hw/bsp/espressif/boards/family.c
@@ -33,27 +33,55 @@
 #include "soc/usb_periph.h"
 
 #include "driver/rmt.h"
+#include "driver/uart.h"
 
 #if ESP_IDF_VERSION_MAJOR > 4
   #include "esp_private/periph_ctrl.h"
 #else
+
   #include "driver/periph_ctrl.h"
+
 #endif
 
+// Note; current code use UART0 can cause device to reset while monitoring
+#define USE_UART  0
+#define UART_ID  UART_NUM_0
+
 #ifdef NEOPIXEL_PIN
+
 #include "led_strip.h"
-static led_strip_t *strip;
+
+static led_strip_t* strip;
 #endif
 
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
+#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
 
-static void configure_pins(usb_hal_context_t *usb);
+#include "driver/spi_master.h"
+
+static void max3421_init(void);
+
+#endif
+
+static void configure_pins(usb_hal_context_t* usb);
+
+//--------------------------------------------------------------------+
+// Implementation
+//--------------------------------------------------------------------+
 
 // Initialize on-board peripherals : led, button, uart and USB
-void board_init(void)
-{
+void board_init(void) {
+#if USE_UART
+  // uart init
+  uart_config_t uart_config = {
+      .baud_rate = 115200,
+      .data_bits = UART_DATA_8_BITS,
+      .parity = UART_PARITY_DISABLE,
+      .stop_bits = UART_STOP_BITS_1,
+      .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
+  };
+  uart_driver_install(UART_ID, 1024, 0, 0, NULL, 0);
+  uart_param_config(UART_ID, &uart_config);
+#endif
 
 #ifdef NEOPIXEL_PIN
   #ifdef NEOPIXEL_POWER_PIN
@@ -84,19 +112,21 @@ void board_init(void)
   periph_module_enable(PERIPH_USB_MODULE);
 
   usb_hal_context_t hal = {
-    .use_external_phy = false // use built-in PHY
+      .use_external_phy = false // use built-in PHY
   };
   usb_hal_init(&hal);
   configure_pins(&hal);
+
+#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
+  max3421_init();
+#endif
 }
 
-static void configure_pins(usb_hal_context_t *usb)
-{
+static void configure_pins(usb_hal_context_t* usb) {
   /* usb_periph_iopins currently configures USB_OTG as USB Device.
    * Introduce additional parameters in usb_hal_context_t when adding support
-   * for USB Host.
-   */
-  for (const usb_iopin_dsc_t *iopin = usb_periph_iopins; iopin->pin != -1; ++iopin) {
+   * for USB Host. */
+  for (const usb_iopin_dsc_t* iopin = usb_periph_iopins; iopin->pin != -1; ++iopin) {
     if ((usb->use_external_phy) || (iopin->ext_phy_only == 0)) {
       esp_rom_gpio_pad_select_gpio(iopin->pin);
       if (iopin->is_output) {
@@ -115,6 +145,7 @@ static void configure_pins(usb_hal_context_t *usb)
       esp_rom_gpio_pad_unhold(iopin->pin);
     }
   }
+
   if (!usb->use_external_phy) {
     gpio_set_drive_capability(USBPHY_DM_NUM, GPIO_DRIVE_CAP_3);
     gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3);
@@ -122,8 +153,7 @@ static void configure_pins(usb_hal_context_t *usb)
 }
 
 // Turn LED on or off
-void board_led_write(bool state)
-{
+void board_led_write(bool state) {
 #ifdef NEOPIXEL_PIN
   strip->set_pixel(strip, 0, (state ? 0x88 : 0x00), 0x00, 0x00);
   strip->refresh(strip, 100);
@@ -132,21 +162,138 @@ void board_led_write(bool state)
 
 // Get the current state of button
 // a '1' means active (pressed), a '0' means inactive.
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
   return gpio_get_level(BUTTON_PIN) == BUTTON_STATE_ACTIVE;
 }
 
 // Get characters from UART
-int board_uart_read(uint8_t* buf, int len)
-{
-  (void) buf; (void) len;
-  return 0;
+int board_uart_read(uint8_t* buf, int len) {
+#if USE_UART
+  return uart_read_bytes(UART_ID, buf, len, 0);
+#else
+  return -1;
+#endif
 }
 
 // Send characters to UART
-int board_uart_write(void const * buf, int len)
-{
-  (void) buf; (void) len;
+int board_uart_write(void const* buf, int len) {
+  (void) buf;
+  (void) len;
   return 0;
 }
+
+int board_getchar(void) {
+  uint8_t c = 0;
+  return board_uart_read(&c, 1) > 0 ? (int) c : (-1);
+}
+
+//--------------------------------------------------------------------+
+// API: SPI transfer with MAX3421E, must be implemented by application
+//--------------------------------------------------------------------+
+#if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
+
+static spi_device_handle_t max3421_spi;
+SemaphoreHandle_t max3421_intr_sem;
+
+static void IRAM_ATTR max3421_isr_handler(void* arg) {
+  (void) arg; // arg is gpio num
+  gpio_set_level(13, 1);
+
+  BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+  xSemaphoreGiveFromISR(max3421_intr_sem, &xHigherPriorityTaskWoken);
+  if (xHigherPriorityTaskWoken) {
+    portYIELD_FROM_ISR();
+  }
+
+  gpio_set_level(13, 0);
+}
+
+static void max3421_intr_task(void* param) {
+  (void) param;
+
+  while (1) {
+    xSemaphoreTake(max3421_intr_sem, portMAX_DELAY);
+    tuh_int_handler(BOARD_TUH_RHPORT, false);
+  }
+}
+
+static void max3421_init(void) {
+  // CS pin
+  gpio_set_direction(MAX3421_CS_PIN, GPIO_MODE_OUTPUT);
+  gpio_set_level(MAX3421_CS_PIN, 1);
+
+  // SPI
+  spi_bus_config_t buscfg = {
+      .miso_io_num = MAX3421_MISO_PIN,
+      .mosi_io_num = MAX3421_MOSI_PIN,
+      .sclk_io_num = MAX3421_SCK_PIN,
+      .quadwp_io_num = -1,
+      .quadhd_io_num = -1,
+      .data4_io_num = -1,
+      .data5_io_num = -1,
+      .data6_io_num = -1,
+      .data7_io_num = -1,
+      .max_transfer_sz = 1024
+  };
+  ESP_ERROR_CHECK(spi_bus_initialize(MAX3421_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
+
+  spi_device_interface_config_t max3421_cfg = {
+      .mode = 0,
+      .clock_speed_hz = 26000000,
+      .spics_io_num = -1, // manual control CS
+      .queue_size = 1
+  };
+  ESP_ERROR_CHECK(spi_bus_add_device(MAX3421_SPI_HOST, &max3421_cfg, &max3421_spi));
+
+  // debug
+  gpio_set_direction(13, GPIO_MODE_OUTPUT);
+  gpio_set_level(13, 0);
+
+  // Interrupt pin
+  max3421_intr_sem = xSemaphoreCreateBinary();
+  xTaskCreate(max3421_intr_task, "max3421 intr", 2048, NULL, configMAX_PRIORITIES - 2, NULL);
+
+  gpio_set_direction(MAX3421_INTR_PIN, GPIO_MODE_INPUT);
+  gpio_set_intr_type(MAX3421_INTR_PIN, GPIO_INTR_NEGEDGE);
+
+  gpio_install_isr_service(0);
+  gpio_isr_handler_add(MAX3421_INTR_PIN, max3421_isr_handler, NULL);
+}
+
+void tuh_max3421_int_api(uint8_t rhport, bool enabled) {
+  (void) rhport;
+  if (enabled) {
+    gpio_intr_enable(MAX3421_INTR_PIN);
+  } else {
+    gpio_intr_disable(MAX3421_INTR_PIN);
+  }
+}
+
+void tuh_max3421_spi_cs_api(uint8_t rhport, bool active) {
+  (void) rhport;
+  gpio_set_level(MAX3421_CS_PIN, active ? 0 : 1);
+}
+
+bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes) {
+  (void) rhport;
+
+  if (tx_buf == NULL) {
+    // fifo read, transmit rx_buf as dummy
+    tx_buf = rx_buf;
+  }
+
+  // length in bits
+  size_t const len_bits = xfer_bytes << 3;
+
+  spi_transaction_t xact = {
+      .length = len_bits,
+      .rxlength = rx_buf ? len_bits : 0,
+      .tx_buffer = tx_buf,
+      .rx_buffer = rx_buf
+  };
+
+  ESP_ERROR_CHECK(spi_device_transmit(max3421_spi, &xact));
+  return true;
+}
+
+#endif
diff --git a/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt b/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt
index bf8e45be2..abe276910 100644
--- a/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt
+++ b/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt
@@ -16,40 +16,58 @@ else()
   return()
 endif()
 
-list(APPEND compile_options
-    "-DCFG_TUSB_MCU=${tusb_mcu}"
-    "-DCFG_TUSB_OS=OPT_OS_FREERTOS"
-    #"-DCFG_TUSB_DEBUG=1"
-    )
-
-idf_component_get_property(freertos_component_dir freertos COMPONENT_DIR)
-
-list(APPEND includes_public
-    "${tusb_src}"
-    # The FreeRTOS API include convention in tinyusb is different from esp-idf
-    #"${freertos_component_dir}/include/freertos"
-    )
+list(APPEND compile_definitions
+  CFG_TUSB_MCU=${tusb_mcu}
+  CFG_TUSB_OS=OPT_OS_FREERTOS
+  )
 
 list(APPEND srcs
-    "${tusb_src}/tusb.c"
-    "${tusb_src}/common/tusb_fifo.c"
-    "${tusb_src}/device/usbd.c"
-    "${tusb_src}/device/usbd_control.c"
-    "${tusb_src}/class/cdc/cdc_device.c"
-    "${tusb_src}/class/dfu/dfu_rt_device.c"
-    "${tusb_src}/class/hid/hid_device.c"
-    "${tusb_src}/class/midi/midi_device.c"
-    "${tusb_src}/class/msc/msc_device.c"
-    "${tusb_src}/class/net/ecm_rndis_device.c"
-    "${tusb_src}/class/net/ncm_device.c"
-    "${tusb_src}/class/usbtmc/usbtmc_device.c"
-    "${tusb_src}/class/vendor/vendor_device.c"
-    "${tusb_src}/portable/synopsys/dwc2/dcd_dwc2.c"
-    )
+  # common
+  ${tusb_src}/tusb.c
+  ${tusb_src}/common/tusb_fifo.c
+  # device
+  ${tusb_src}/device/usbd.c
+  ${tusb_src}/device/usbd_control.c
+  ${tusb_src}/class/audio/audio_device.c
+  ${tusb_src}/class/cdc/cdc_device.c
+  ${tusb_src}/class/dfu/dfu_device.c
+  ${tusb_src}/class/dfu/dfu_rt_device.c
+  ${tusb_src}/class/hid/hid_device.c
+  ${tusb_src}/class/midi/midi_device.c
+  ${tusb_src}/class/msc/msc_device.c
+  ${tusb_src}/class/net/ecm_rndis_device.c
+  ${tusb_src}/class/net/ncm_device.c
+  ${tusb_src}/class/usbtmc/usbtmc_device.c
+  ${tusb_src}/class/vendor/vendor_device.c
+  ${tusb_src}/class/video/video_device.c
+  ${tusb_src}/portable/synopsys/dwc2/dcd_dwc2.c
+  # host
+  ${tusb_src}/host/usbh.c
+  ${tusb_src}/host/hub.c
+  ${tusb_src}/class/cdc/cdc_host.c
+  ${tusb_src}/class/hid/hid_host.c
+  ${tusb_src}/class/msc/msc_host.c
+  ${tusb_src}/class/vendor/vendor_host.c
+  )
+
+# use max3421 as host controller
+if (MAX3421_HOST STREQUAL "1")
+  list(APPEND srcs ${tusb_src}/portable/analog/max3421/hcd_max3421.c)
+  list(APPEND compile_definitions CFG_TUH_MAX3421=1)
+endif ()
+
+if (DEFINED LOG)
+  list(APPEND compile_definitions CFG_TUSB_DEBUG=${LOG})
+  if (LOG STREQUAL "4")
+    # no inline for debug level 4
+    list(APPEND compile_definitions TU_ATTR_ALWAYS_INLINE=)
+  endif ()
+endif()
 
 idf_component_register(SRCS ${srcs}
-    INCLUDE_DIRS ${includes_public}
-    REQUIRES src
-    )
+  INCLUDE_DIRS ${tusb_src}
+  REQUIRES src
+  )
 
-target_compile_options(${COMPONENT_LIB} PUBLIC ${compile_options})
+target_compile_definitions(${COMPONENT_LIB} PUBLIC ${compile_definitions})
+target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error=format)
diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake
index 642282b0e..9c625b58e 100644
--- a/hw/bsp/family_support.cmake
+++ b/hw/bsp/family_support.cmake
@@ -40,6 +40,7 @@ if (NOT FAMILY STREQUAL rp2040)
   # enable LTO if supported skip rp2040
   include(CheckIPOSupported)
   check_ipo_supported(RESULT IPO_SUPPORTED)
+  cmake_print_variables(IPO_SUPPORTED)
   if (IPO_SUPPORTED)
     set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
   endif()
diff --git a/hw/bsp/imxrt/family.c b/hw/bsp/imxrt/family.c
index ec95a8b24..32d89f794 100644
--- a/hw/bsp/imxrt/family.c
+++ b/hw/bsp/imxrt/family.c
@@ -188,7 +188,7 @@ void USB_OTG1_IRQHandler(void)
   #endif
 
   #if PORT_SUPPORT_HOST(0)
-    tuh_int_handler(0);
+    tuh_int_handler(0, true);
   #endif
 }
 
@@ -199,7 +199,7 @@ void USB_OTG2_IRQHandler(void)
   #endif
 
   #if PORT_SUPPORT_HOST(1)
-    tuh_int_handler(1);
+    tuh_int_handler(1, true);
   #endif
 }
 
diff --git a/hw/bsp/kinetis_kl/family.c b/hw/bsp/kinetis_kl/family.c
index 3e9aa83a4..c436be3e6 100644
--- a/hw/bsp/kinetis_kl/family.c
+++ b/hw/bsp/kinetis_kl/family.c
@@ -39,7 +39,7 @@
 void USB0_IRQHandler(void)
 {
 #if CFG_TUH_ENABLED
-  tuh_int_handler(0);
+  tuh_int_handler(0, true);
 #endif
 #if CFG_TUD_ENABLED
   tud_int_handler(0);
diff --git a/hw/bsp/kinetis_kl/family.cmake b/hw/bsp/kinetis_kl/family.cmake
index 793ef1783..4151979a0 100644
--- a/hw/bsp/kinetis_kl/family.cmake
+++ b/hw/bsp/kinetis_kl/family.cmake
@@ -22,48 +22,47 @@ set(FAMILY_MCUS KINETIS_KL CACHE INTERNAL "")
 #------------------------------------
 # 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}/drivers/gpio/fsl_gpio.c
-      ${SDK_DIR}/drivers/lpsci/fsl_lpsci.c
-      ${SDK_DIR}/drivers/uart/fsl_uart.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_VARIANT}.c
-      )
-    target_compile_definitions(${BOARD_TARGET} PUBLIC
-      )
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${CMSIS_DIR}/CMSIS/Core/Include
-      ${SDK_DIR}/devices/${MCU_VARIANT}
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
-      ${SDK_DIR}/drivers/common
-      ${SDK_DIR}/drivers/gpio
-      ${SDK_DIR}/drivers/lpsci
-      ${SDK_DIR}/drivers/port
-      ${SDK_DIR}/drivers/smc
-      ${SDK_DIR}/drivers/uart
-      )
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
 
-    update_board(${BOARD_TARGET})
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/drivers/gpio/fsl_gpio.c
+    ${SDK_DIR}/drivers/lpsci/fsl_lpsci.c
+    ${SDK_DIR}/drivers/uart/fsl_uart.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_VARIANT}.c
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMSIS_DIR}/CMSIS/Core/Include
+    ${SDK_DIR}/devices/${MCU_VARIANT}
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
+    ${SDK_DIR}/drivers/common
+    ${SDK_DIR}/drivers/gpio
+    ${SDK_DIR}/drivers/lpsci
+    ${SDK_DIR}/drivers/port
+    ${SDK_DIR}/drivers/smc
+    ${SDK_DIR}/drivers/uart
+    )
+  update_board(${BOARD_TARGET})
 
-    # LD_FILE and STARTUP_FILE can be defined in board.cmake
+  # LD_FILE and STARTUP_FILE can be defined in board.cmake
+  target_sources(${BOARD_TARGET} PUBLIC
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
 
-    target_sources(${BOARD_TARGET} PUBLIC
-      ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+  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}"
       )
-
-    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()
 
diff --git a/hw/bsp/lpc17/boards/lpcxpresso1769/lpcxpresso1769.c b/hw/bsp/lpc17/boards/lpcxpresso1769/lpcxpresso1769.c
index c7d655368..8c26b906f 100644
--- a/hw/bsp/lpc17/boards/lpcxpresso1769/lpcxpresso1769.c
+++ b/hw/bsp/lpc17/boards/lpcxpresso1769/lpcxpresso1769.c
@@ -37,7 +37,7 @@ void USB_IRQHandler(void)
   #endif
 
   #if CFG_TUH_ENABLED
-    tuh_int_handler(0);
+    tuh_int_handler(0, true);
   #endif
 }
 
diff --git a/hw/bsp/lpc17/boards/mbed1768/mbed1768.c b/hw/bsp/lpc17/boards/mbed1768/mbed1768.c
index b2c92d640..613dcb570 100644
--- a/hw/bsp/lpc17/boards/mbed1768/mbed1768.c
+++ b/hw/bsp/lpc17/boards/mbed1768/mbed1768.c
@@ -151,7 +151,7 @@ void USB_IRQHandler(void)
   #endif
 
   #if CFG_TUH_ENABLED
-    tuh_int_handler(0);
+    tuh_int_handler(0, true);
   #endif
 }
 
diff --git a/hw/bsp/lpc17/family.mk b/hw/bsp/lpc17/family.mk
index abf6abe13..44b406d90 100644
--- a/hw/bsp/lpc17/family.mk
+++ b/hw/bsp/lpc17/family.mk
@@ -22,6 +22,8 @@ MCU_DIR = hw/mcu/nxp/lpcopen/lpc175x_6x/lpc_chip_175x_6x
 
 SRC_C += \
 	src/portable/nxp/lpc17_40/dcd_lpc17_40.c \
+	src/portable/ohci/ohci.c \
+	src/portable/nxp/lpc17_40/hcd_lpc17_40.c \
 	$(MCU_DIR)/../gcc/cr_startup_lpc175x_6x.c \
 	$(MCU_DIR)/src/chip_17xx_40xx.c \
 	$(MCU_DIR)/src/clock_17xx_40xx.c \
@@ -29,7 +31,7 @@ SRC_C += \
 	$(MCU_DIR)/src/iocon_17xx_40xx.c \
 	$(MCU_DIR)/src/sysctl_17xx_40xx.c \
 	$(MCU_DIR)/src/sysinit_17xx_40xx.c \
-	$(MCU_DIR)/src/uart_17xx_40xx.c
+	$(MCU_DIR)/src/uart_17xx_40xx.c \
 
 INC += \
 	$(TOP)/$(MCU_DIR)/inc
diff --git a/hw/bsp/lpc18/family.c b/hw/bsp/lpc18/family.c
index b11f4fe0e..e6abecb4b 100644
--- a/hw/bsp/lpc18/family.c
+++ b/hw/bsp/lpc18/family.c
@@ -43,25 +43,23 @@
 //--------------------------------------------------------------------+
 // USB Interrupt Handler
 //--------------------------------------------------------------------+
-void USB0_IRQHandler(void)
-{
+void USB0_IRQHandler(void) {
   #if PORT_SUPPORT_DEVICE(0)
-    tud_int_handler(0);
+  tud_int_handler(0);
   #endif
 
   #if PORT_SUPPORT_HOST(0)
-    tuh_int_handler(0);
+  tuh_int_handler(0, true);
   #endif
 }
 
-void USB1_IRQHandler(void)
-{
+void USB1_IRQHandler(void) {
   #if PORT_SUPPORT_DEVICE(1)
-    tud_int_handler(1);
+  tud_int_handler(1);
   #endif
 
   #if PORT_SUPPORT_HOST(1)
-    tuh_int_handler(1);
+  tuh_int_handler(1, true);
   #endif
 }
 
@@ -74,28 +72,26 @@ const uint32_t OscRateIn = 12000000;
 const uint32_t ExtRateIn = 0;
 
 // Invoked by startup code
-void SystemInit(void)
-{
+void SystemInit(void) {
 #ifdef __USE_LPCOPEN
-	extern void (* const g_pfnVectors[])(void);
+  extern void (*const g_pfnVectors[])(void);
   unsigned int *pSCB_VTOR = (unsigned int *) 0xE000ED08;
-	*pSCB_VTOR = (unsigned int) g_pfnVectors;
+  *pSCB_VTOR = (unsigned int) g_pfnVectors;
 #endif
 
   board_lpc18_pinmux();
 
-  #ifdef TRACE_ETM
+#ifdef TRACE_ETM
   // Trace clock is limited to 60MHz, limit CPU clock to 120MHz
   Chip_SetupCoreClock(CLKIN_CRYSTAL, 120000000UL, true);
-  #else
+#else
   // CPU clock max to 180 Mhz
   Chip_SetupCoreClock(CLKIN_CRYSTAL, MAX_CLOCK_FREQ, true);
-  #endif
+#endif
 
 }
 
-void board_init(void)
-{
+void board_init(void) {
   SystemCoreClockUpdate();
 
 #if CFG_TUSB_OS == OPT_OS_NONE
@@ -135,27 +131,22 @@ void board_init(void)
 // Board porting API
 //--------------------------------------------------------------------+
 
-void board_led_write(bool state)
-{
+void board_led_write(bool state) {
   Chip_GPIO_SetPinState(LPC_GPIO_PORT, LED_PORT, LED_PIN, state);
 }
 
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
   // active low
   return Chip_GPIO_GetPinState(LPC_GPIO_PORT, BUTTON_PORT, BUTTON_PIN) ? 0 : 1;
 }
 
-int board_uart_read(uint8_t* buf, int len)
-{
+int board_uart_read(uint8_t *buf, int len) {
   return Chip_UART_Read(UART_DEV, buf, len);
 }
 
-int board_uart_write(void const * buf, int len)
-{
-  uint8_t const* buf8 = (uint8_t const*) buf;
-  for(int i=0; iDHCSR */ \
+        if ( (*ARM_CM_DHCSR) & 1UL ) {  /* Only halt mcu if debugger is attached */ \
+          taskDISABLE_INTERRUPTS(); \
+           __asm("BKPT #0\n"); \
+        }\
+      }\
+    } while(0)
+#else
+  #define configASSERT( x )
+#endif
+
+/* 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<)
 #------------------------------------
 # 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
-      # driver
-      ${SDK_DIR}/drivers/lpc_gpio/fsl_gpio.c
-      ${SDK_DIR}/drivers/common/fsl_common_arm.c
-      ${SDK_DIR}/drivers/flexcomm/fsl_flexcomm.c
-      ${SDK_DIR}/drivers/flexcomm/fsl_usart.c
-      # mcu
-      ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_CORE}.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_power.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_reset.c
-      )
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
 
+  add_library(${BOARD_TARGET} STATIC
+    # driver
+    ${SDK_DIR}/drivers/lpc_gpio/fsl_gpio.c
+    ${SDK_DIR}/drivers/common/fsl_common_arm.c
+    ${SDK_DIR}/drivers/flexcomm/fsl_flexcomm.c
+    ${SDK_DIR}/drivers/flexcomm/fsl_usart.c
+    # mcu
+    ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_CORE}.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_power.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_reset.c
+    )
+
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    CFG_TUSB_MEM_ALIGN=TU_ATTR_ALIGNED\(64\)
+    BOARD_TUD_RHPORT=${PORT}
+    BOARD_TUH_RHPORT=${HOST_PORT}
+    )
+  # Port 0 is Fullspeed, Port 1 is Highspeed. Port1 controller can only access USB_SRAM
+  if (PORT EQUAL 1)
     target_compile_definitions(${BOARD_TARGET} PUBLIC
-      CFG_TUSB_MEM_ALIGN=TU_ATTR_ALIGNED\(64\)
-      BOARD_TUD_RHPORT=${PORT}
-      BOARD_TUH_RHPORT=${HOST_PORT}
+      BOARD_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED
+      BOARD_TUH_MAX_SPEED=OPT_MODE_FULL_SPEED
+      CFG_TUD_MEM_SECTION=__attribute__\(\(section\(\"m_usb_global\"\)\)\)
       )
-    # Port 0 is Fullspeed, Port 1 is Highspeed. Port1 controller can only access USB_SRAM
-    if (PORT EQUAL 1)
-      target_compile_definitions(${BOARD_TARGET} PUBLIC
-        BOARD_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED
-        BOARD_TUH_MAX_SPEED=OPT_MODE_FULL_SPEED
-        CFG_TUD_MEM_SECTION=__attribute__\(\(section\(\"m_usb_global\"\)\)\)
-        )
-    else ()
-      target_compile_definitions(${BOARD_TARGET} PUBLIC
-        BOARD_TUD_MAX_SPEED=OPT_MODE_FULL_SPEED
-        BOARD_TUH_MAX_SPEED=OPT_MODE_HIGH_SPEED
-        CFG_TUH_MEM_SECTION=__attribute__\(\(section\(\"m_usb_global\"\)\)\)
-        #CFG_TUD_MEM_SECTION=__attribute__\(\(section\(\"m_usb_global\"\)\)\)
-        )
-    endif ()
-
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${TOP}/lib/sct_neopixel
-      # driver
-      ${SDK_DIR}/drivers/common
-      ${SDK_DIR}/drivers/flexcomm
-      ${SDK_DIR}/drivers/lpc_iocon
-      ${SDK_DIR}/drivers/lpc_gpio
-      ${SDK_DIR}/drivers/lpuart
-      ${SDK_DIR}/drivers/sctimer
-      # mcu
-      ${CMSIS_DIR}/CMSIS/Core/Include
-      ${SDK_DIR}/devices/${MCU_VARIANT}
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
+  else ()
+    target_compile_definitions(${BOARD_TARGET} PUBLIC
+      BOARD_TUD_MAX_SPEED=OPT_MODE_FULL_SPEED
+      BOARD_TUH_MAX_SPEED=OPT_MODE_HIGH_SPEED
+      CFG_TUH_MEM_SECTION=__attribute__\(\(section\(\"m_usb_global\"\)\)\)
+      #CFG_TUD_MEM_SECTION=__attribute__\(\(section\(\"m_usb_global\"\)\)\)
       )
+  endif ()
 
-    update_board(${BOARD_TARGET})
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${TOP}/lib/sct_neopixel
+    # driver
+    ${SDK_DIR}/drivers/common
+    ${SDK_DIR}/drivers/flexcomm
+    ${SDK_DIR}/drivers/lpc_iocon
+    ${SDK_DIR}/drivers/lpc_gpio
+    ${SDK_DIR}/drivers/lpuart
+    ${SDK_DIR}/drivers/sctimer
+    # mcu
+    ${CMSIS_DIR}/CMSIS/Core/Include
+    ${SDK_DIR}/devices/${MCU_VARIANT}
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
+    )
 
-    if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
-      set(LD_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_CORE}_flash.ld)
-    endif ()
+  update_board(${BOARD_TARGET})
 
-    if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
-      set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_CORE}.S)
-    endif ()
+  if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
+    set(LD_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_CORE}_flash.ld)
+  endif ()
 
-    target_sources(${BOARD_TARGET} PUBLIC
-      ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+  if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
+    set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_CORE}.S)
+  endif ()
+
+  target_sources(${BOARD_TARGET} PUBLIC
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      # linker file
+      "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}"
       )
-
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        # linker file
-        "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()
 
@@ -124,8 +126,6 @@ function(family_configure_example TARGET RTOS)
     # BSP
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
-    # external driver
-    ${TOP}/lib/sct_neopixel/sct_neopixel.c
     )
 
   # https://github.com/gsteiert/sct_neopixel/pull/1
diff --git a/hw/bsp/msp432e4/family.c b/hw/bsp/msp432e4/family.c
index 10f3ac736..d5ef7f930 100644
--- a/hw/bsp/msp432e4/family.c
+++ b/hw/bsp/msp432e4/family.c
@@ -34,7 +34,7 @@
 void USB0_IRQHandler(void)
 {
 #if CFG_TUH_ENABLED
-  tuh_int_handler(0);
+  tuh_int_handler(0, true);
 #endif
 #if CFG_TUD_ENABLED
   tud_int_handler(0);
diff --git a/hw/bsp/ngx4330/ngx4330.c b/hw/bsp/ngx4330/ngx4330.c
index a767d5f29..bd84e449b 100644
--- a/hw/bsp/ngx4330/ngx4330.c
+++ b/hw/bsp/ngx4330/ngx4330.c
@@ -214,7 +214,7 @@ void USB0_IRQHandler(void)
   #endif
 
   #if PORT_SUPPORT_HOST(0)
-    tuh_int_handler(0);
+    tuh_int_handler(0, true);
   #endif
 }
 
@@ -225,7 +225,7 @@ void USB1_IRQHandler(void)
   #endif
 
   #if PORT_SUPPORT_HOST(1)
-    tuh_int_handler(1);
+    tuh_int_handler(1, true);
   #endif
 }
 
diff --git a/hw/bsp/nrf/boards/feather_nrf52840_express/board.cmake b/hw/bsp/nrf/boards/feather_nrf52840_express/board.cmake
index ce6da8b43..726438d05 100644
--- a/hw/bsp/nrf/boards/feather_nrf52840_express/board.cmake
+++ b/hw/bsp/nrf/boards/feather_nrf52840_express/board.cmake
@@ -1,6 +1,6 @@
 set(MCU_VARIANT nrf52840)
-#set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/../../linker/nrf52840_s140_v6.ld)
-set(LD_FILE_GNU ${NRFX_DIR}/mdk/nrf52840_xxaa.ld)
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/../../linker/nrf52840_s140_v6.ld)
+#set(LD_FILE_GNU ${NRFX_DIR}/mdk/nrf52840_xxaa.ld)
 
 # enable max3421 host driver for this board
 set(MAX3421_HOST 1)
diff --git a/hw/bsp/nrf/boards/feather_nrf52840_express/board.h b/hw/bsp/nrf/boards/feather_nrf52840_express/board.h
index 76100a14a..3d59516d8 100644
--- a/hw/bsp/nrf/boards/feather_nrf52840_express/board.h
+++ b/hw/bsp/nrf/boards/feather_nrf52840_express/board.h
@@ -49,8 +49,8 @@
 #define MAX3421_SCK_PIN  14
 #define MAX3421_MOSI_PIN 13
 #define MAX3421_MISO_PIN 15
-#define MAX3421_CS_PIN   27
-#define MAX3421_INTR_PIN 26
+#define MAX3421_CS_PIN   27 // D10
+#define MAX3421_INTR_PIN 26 // D9
 
 #ifdef __cplusplus
  }
diff --git a/hw/bsp/nrf/family.c b/hw/bsp/nrf/family.c
index 6fcfd9476..c431389f3 100644
--- a/hw/bsp/nrf/family.c
+++ b/hw/bsp/nrf/family.c
@@ -95,13 +95,10 @@ TU_ATTR_UNUSED static void power_event_handler(nrfx_power_usb_evt_t event) {
 
 //------------- Host using MAX2341E -------------//
 #if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
+
+static void max3421_init(void);
+
 static nrfx_spim_t _spi = NRFX_SPIM_INSTANCE(1);
-
-void max3421_int_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
-  if (!(pin == MAX3421_INTR_PIN && action == NRF_GPIOTE_POLARITY_HITOLO)) return;
-
-  tuh_int_handler(1);
-}
 #endif
 
 
@@ -191,50 +188,7 @@ void board_init(void) {
 #endif
 
 #if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
-  // MAX3421 need 3.3v signal (may not be needed)
-  #if defined(UICR_REGOUT0_VOUT_Msk) && 0
-  if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) != UICR_REGOUT0_VOUT_3V3) {
-    NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
-    while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
-
-    NRF_UICR->REGOUT0 = (NRF_UICR->REGOUT0 & ~UICR_REGOUT0_VOUT_Msk) | UICR_REGOUT0_VOUT_3V3;
-
-    NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
-    while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
-
-    NVIC_SystemReset();
-  }
-  #endif
-
-  // manually manage CS
-  nrf_gpio_cfg_output(MAX3421_CS_PIN);
-  nrf_gpio_pin_write(MAX3421_CS_PIN, 1);
-
-  // USB host using max3421e usb controller via SPI
-  nrfx_spim_config_t cfg = {
-      .sck_pin        = MAX3421_SCK_PIN,
-      .mosi_pin       = MAX3421_MOSI_PIN,
-      .miso_pin       = MAX3421_MISO_PIN,
-      .ss_pin         = NRFX_SPIM_PIN_NOT_USED,
-      .ss_active_high = false,
-      .irq_priority   = 3,
-      .orc            = 0xFF,
-      // default setting 4 Mhz, Mode 0, MSB first
-      .frequency      = NRF_SPIM_FREQ_4M,
-      .mode           = NRF_SPIM_MODE_0,
-      .bit_order      = NRF_SPIM_BIT_ORDER_MSB_FIRST,
-  };
-
-  // no handler --> blocking
-  nrfx_spim_init(&_spi, &cfg, NULL, NULL);
-
-  // max3421e interrupt pin
-  nrfx_gpiote_init(1);
-  nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
-  in_config.pull = NRF_GPIO_PIN_PULLUP;
-
-  nrfx_gpiote_in_init(MAX3421_INTR_PIN, &in_config, max3421_int_handler);
-  nrfx_gpiote_trigger_enable(MAX3421_INTR_PIN, true);
+  max3421_init();
 #endif
 
 }
@@ -251,15 +205,15 @@ uint32_t board_button_read(void) {
   return BUTTON_STATE_ACTIVE == nrf_gpio_pin_read(BUTTON_PIN);
 }
 
-int board_uart_read(uint8_t *buf, int len) {
+int board_uart_read(uint8_t* buf, int len) {
   (void) buf;
   (void) len;
   return 0;
 //  return NRFX_SUCCESS == nrfx_uart_rx(&_uart_id, buf, (size_t) len) ? len : 0;
 }
 
-int board_uart_write(void const *buf, int len) {
-  return (NRFX_SUCCESS == nrfx_uarte_tx(&_uart_id, (uint8_t const *) buf, (size_t) len)) ? len : 0;
+int board_uart_write(void const* buf, int len) {
+  return (NRFX_SUCCESS == nrfx_uarte_tx(&_uart_id, (uint8_t const*) buf, (size_t) len)) ? len : 0;
 }
 
 #if CFG_TUSB_OS == OPT_OS_NONE
@@ -317,6 +271,61 @@ void nrf_error_cb(uint32_t id, uint32_t pc, uint32_t info) {
 //--------------------------------------------------------------------+
 #if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
 
+void max3421_int_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
+  if (!(pin == MAX3421_INTR_PIN && action == NRF_GPIOTE_POLARITY_HITOLO)) return;
+  tuh_int_handler(1, true);
+}
+
+static void max3421_init(void) {
+  // MAX3421 need 3.3v signal (may not be needed)
+//  #if defined(UICR_REGOUT0_VOUT_Msk)
+//  if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) != UICR_REGOUT0_VOUT_3V3) {
+//    NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
+//    while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
+//
+//    NRF_UICR->REGOUT0 = (NRF_UICR->REGOUT0 & ~UICR_REGOUT0_VOUT_Msk) | UICR_REGOUT0_VOUT_3V3;
+//
+//    NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
+//    while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
+//
+//    NVIC_SystemReset();
+//  }
+//  #endif
+
+  // manually manage CS
+  nrf_gpio_cfg_output(MAX3421_CS_PIN);
+  nrf_gpio_pin_write(MAX3421_CS_PIN, 1);
+
+  // USB host using max3421e usb controller via SPI
+  nrfx_spim_config_t cfg = {
+      .sck_pin        = MAX3421_SCK_PIN,
+      .mosi_pin       = MAX3421_MOSI_PIN,
+      .miso_pin       = MAX3421_MISO_PIN,
+      .ss_pin         = NRFX_SPIM_PIN_NOT_USED,
+      .ss_active_high = false,
+      .irq_priority   = 3,
+      .orc            = 0xFF,
+      // default setting 4 Mhz, Mode 0, MSB first
+      .frequency      = NRF_SPIM_FREQ_4M,
+      .mode           = NRF_SPIM_MODE_0,
+      .bit_order      = NRF_SPIM_BIT_ORDER_MSB_FIRST,
+  };
+
+  // no handler --> blocking
+  nrfx_spim_init(&_spi, &cfg, NULL, NULL);
+
+  // max3421e interrupt pin
+  nrfx_gpiote_init(1);
+  nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
+  in_config.pull = NRF_GPIO_PIN_PULLUP;
+
+  NVIC_SetPriority(GPIOTE_IRQn, 2);
+
+  nrfx_gpiote_in_init(MAX3421_INTR_PIN, &in_config, max3421_int_handler);
+  nrfx_gpiote_trigger_enable(MAX3421_INTR_PIN, true);
+}
+
+// API to enable/disable MAX3421 INTR pin interrupt
 void tuh_max3421_int_api(uint8_t rhport, bool enabled) {
   (void) rhport;
 
@@ -329,22 +338,25 @@ void tuh_max3421_int_api(uint8_t rhport, bool enabled) {
   }
 }
 
+// API to control MAX3421 SPI CS
 void tuh_max3421_spi_cs_api(uint8_t rhport, bool active) {
   (void) rhport;
   nrf_gpio_pin_write(MAX3421_CS_PIN, active ? 0 : 1);
 }
 
-bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, size_t tx_len, uint8_t *rx_buf, size_t rx_len) {
+// API to transfer data with MAX3421 SPI
+// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
+bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes) {
   (void) rhport;
 
   nrfx_spim_xfer_desc_t xfer = {
       .p_tx_buffer = tx_buf,
-      .tx_length   = tx_len,
+      .tx_length   = tx_buf ? xfer_bytes : 0,
       .p_rx_buffer = rx_buf,
-      .rx_length   = rx_len,
+      .rx_length   = rx_buf ? xfer_bytes : 0,
   };
 
-  return (nrfx_spim_xfer(&_spi, &xfer, 0) == NRFX_SUCCESS);
+  return nrfx_spim_xfer(&_spi, &xfer, 0) == NRFX_SUCCESS;
 }
 
 #endif
diff --git a/hw/bsp/ra/family.c b/hw/bsp/ra/family.c
index 0aa58d86d..fdf4c8666 100644
--- a/hw/bsp/ra/family.c
+++ b/hw/bsp/ra/family.c
@@ -188,7 +188,7 @@ void usbfs_interrupt_handler(void) {
   R_BSP_IrqStatusClear(irq);
 
   #if PORT_SUPPORT_HOST(0)
-  tuh_int_handler(0);
+  tuh_int_handler(0, true);
   #endif
 
   #if PORT_SUPPORT_DEVICE(0)
@@ -201,7 +201,7 @@ void usbfs_resume_handler(void) {
   R_BSP_IrqStatusClear(irq);
 
   #if PORT_SUPPORT_HOST(0)
-  tuh_int_handler(0);
+  tuh_int_handler(0, true);
   #endif
 
   #if PORT_SUPPORT_DEVICE(0)
@@ -229,7 +229,7 @@ void usbhs_interrupt_handler(void) {
   R_BSP_IrqStatusClear(irq);
 
   #if PORT_SUPPORT_HOST(1)
-  tuh_int_handler(1);
+  tuh_int_handler(1, true);
   #endif
 
   #if PORT_SUPPORT_DEVICE(1)
diff --git a/hw/bsp/rx/boards/rx65n_target/rx65n_target.c b/hw/bsp/rx/boards/rx65n_target/rx65n_target.c
index 513eca678..032dac810 100644
--- a/hw/bsp/rx/boards/rx65n_target/rx65n_target.c
+++ b/hw/bsp/rx/boards/rx65n_target/rx65n_target.c
@@ -177,7 +177,7 @@ void INT_Excep_SCI5_RXI5(void)
 void INT_Excep_USB0_USBI0(void)
 {
 #if CFG_TUH_ENABLED
-  tuh_int_handler(0);
+  tuh_int_handler(0, true);
 #endif
 #if CFG_TUD_ENABLED
   tud_int_handler(0);
diff --git a/hw/bsp/samd21/family.c b/hw/bsp/samd21/family.c
index cfe66b8dc..ccb2c99b1 100644
--- a/hw/bsp/samd21/family.c
+++ b/hw/bsp/samd21/family.c
@@ -72,6 +72,7 @@ static void uart_init(void);
 #define MAX3421_SERCOM TU_XSTRCAT(SERCOM, MAX3421_SERCOM_ID)
 
 static void max3421_init(void);
+
 #endif
 
 void board_init(void) {
@@ -237,13 +238,13 @@ int board_uart_write(void const * buf, int len)
 static void uart_init(void) {
 }
 
-int board_uart_read(uint8_t *buf, int len) {
+int board_uart_read(uint8_t* buf, int 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) {
   (void) buf;
   (void) len;
   return 0;
@@ -262,6 +263,8 @@ uint32_t board_millis(void) {
   return system_ticks;
 }
 
+#endif
+
 //--------------------------------------------------------------------+
 //
 //--------------------------------------------------------------------+
@@ -269,15 +272,16 @@ uint32_t board_millis(void) {
 
 static void max3421_init(void) {
   //------------- SPI Init -------------//
-  uint32_t const baudrate = 4000000u;
+  // MAX3421E max SPI clock is 26MHz however SAMD can only work reliably at 12 Mhz
+  uint32_t const baudrate = 12000000u;
 
   // Enable the APB clock for SERCOM
   PM->APBCMASK.reg |= 1u << (PM_APBCMASK_SERCOM0_Pos + MAX3421_SERCOM_ID);
 
   // Configure GCLK for SERCOM
 //  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_SERCOM4_CORE | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_CLKEN;
-  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCLK_CLKCTRL_ID_SERCOM0_CORE_Val+MAX3421_SERCOM_ID) |
-      GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_CLKEN;
+  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCLK_CLKCTRL_ID_SERCOM0_CORE_Val + MAX3421_SERCOM_ID) |
+                      GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_CLKEN;
   while (GCLK->STATUS.bit.SYNCBUSY);
 
   Sercom* sercom = MAX3421_SERCOM;
@@ -291,7 +295,7 @@ static void max3421_init(void) {
 
   // Set up SPI in master mode, MSB first, SPI mode 0
   sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_DOPO(MAX3421_TX_PAD) | SERCOM_SPI_CTRLA_DIPO(MAX3421_RX_PAD) |
-      SERCOM_SPI_CTRLA_MODE(3);
+                          SERCOM_SPI_CTRLA_MODE(3);
 
   sercom->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_SPI_CTRLB_RXEN;
   while (sercom->SPI.SYNCBUSY.bit.CTRLB == 1);
@@ -343,6 +347,11 @@ static void max3421_init(void) {
   EIC->CONFIG[0].reg &= ~(7 << sense_shift);
   EIC->CONFIG[0].reg |= 2 << sense_shift;
 
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+  // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
+  NVIC_SetPriority(EIC_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
+#endif
+
   // Enable External Interrupt
   EIC->INTENSET.reg = EIC_INTENSET_EXTINT(1 << MAX3421_INTR_EIC_ID);
 
@@ -356,9 +365,10 @@ void EIC_Handler(void) {
   EIC->INTFLAG.reg = EIC_INTFLAG_EXTINT(1 << MAX3421_INTR_EIC_ID);
 
   // Call the TinyUSB interrupt handler
-  tuh_int_handler(1);
+  tuh_int_handler(1, true);
 }
 
+// API to enable/disable MAX3421 INTR pin interrupt
 void tuh_max3421_int_api(uint8_t rhport, bool enabled) {
   (void) rhport;
 
@@ -369,24 +379,26 @@ void tuh_max3421_int_api(uint8_t rhport, bool enabled) {
   }
 }
 
+// API to control MAX3421 SPI CS
 void tuh_max3421_spi_cs_api(uint8_t rhport, bool active) {
   (void) rhport;
   gpio_set_pin_level(MAX3421_CS_PIN, active ? 0 : 1);
 }
 
-bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, size_t tx_len, uint8_t *rx_buf, size_t rx_len) {
+// API to transfer data with MAX3421 SPI
+// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
+bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes) {
   (void) rhport;
 
   Sercom* sercom = MAX3421_SERCOM;
 
-  size_t count = 0;
-  while (count < tx_len || count < rx_len) {
+  for (size_t count = 0; count < xfer_bytes; count++) {
     // Wait for the transmit buffer to be empty
     while (!sercom->SPI.INTFLAG.bit.DRE);
 
     // Write data to be transmitted
     uint8_t data = 0x00;
-    if (count < tx_len) {
+    if (tx_buf) {
       data = tx_buf[count];
     }
 
@@ -397,11 +409,9 @@ bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, size_t tx_l
 
     // Read received data
     data = (uint8_t) sercom->SPI.DATA.reg;
-    if (count < rx_len) {
+    if (rx_buf) {
       rx_buf[count] = data;
     }
-
-    count++;
   }
 
   // wait for bus idle and clear flags
@@ -412,6 +422,3 @@ bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, size_t tx_l
 }
 
 #endif
-
-
-#endif
diff --git a/hw/bsp/samd51/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/samd51/FreeRTOSConfig/FreeRTOSConfig.h
index e3ee529cf..32db4ad95 100644
--- a/hw/bsp/samd51/FreeRTOSConfig/FreeRTOSConfig.h
+++ b/hw/bsp/samd51/FreeRTOSConfig/FreeRTOSConfig.h
@@ -59,7 +59,7 @@
 #define configTICK_RATE_HZ                      ( 1000 )
 #define configMAX_PRIORITIES                    ( 5 )
 #define configMINIMAL_STACK_SIZE                ( 128 )
-#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*6*1024 )
 #define configMAX_TASK_NAME_LEN                 16
 #define configUSE_16_BIT_TICKS                  0
 #define configIDLE_SHOULD_YIELD                 1
diff --git a/hw/bsp/samd51/family.c b/hw/bsp/samd51/family.c
index 2cf00c7af..bca18e1a2 100644
--- a/hw/bsp/samd51/family.c
+++ b/hw/bsp/samd51/family.c
@@ -81,6 +81,7 @@ void USB_3_Handler(void) {
 #define MAX3421_EIC_Handler TU_XSTRCAT3(EIC_, MAX3421_INTR_EIC_ID, _Handler)
 
 static void max3421_init(void);
+
 #endif
 
 void board_init(void) {
@@ -153,13 +154,13 @@ uint32_t board_button_read(void) {
   return gpio_get_pin_level(BUTTON_PIN) ? 0 : 1;
 }
 
-int board_uart_read(uint8_t *buf, int len) {
+int board_uart_read(uint8_t* buf, int 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) {
   (void) buf;
   (void) len;
   return 0;
@@ -176,6 +177,8 @@ uint32_t board_millis(void) {
   return system_ticks;
 }
 
+#endif
+
 //--------------------------------------------------------------------+
 // API: SPI transfer with MAX3421E, must be implemented by application
 //--------------------------------------------------------------------+
@@ -183,10 +186,12 @@ uint32_t board_millis(void) {
 
 static void max3421_init(void) {
   //------------- SPI Init -------------//
-  uint32_t const baudrate = 4000000u;
+
+  // MAX3421E max SPI clock is 26MHz however SAMD can only work reliably at 12 Mhz
+  uint32_t const baudrate = 12000000u;
 
   struct {
-    volatile uint32_t *mck_apb;
+    volatile uint32_t* mck_apb;
     uint32_t mask;
     uint8_t gclk_id_core;
     uint8_t gclk_id_slow;
@@ -211,8 +216,10 @@ static void max3421_init(void) {
   *sercom_clock[MAX3421_SERCOM_ID].mck_apb |= sercom_clock[MAX3421_SERCOM_ID].mask;
 
   // Configure GCLK for SERCOM
-  GCLK->PCHCTRL[sercom_clock[MAX3421_SERCOM_ID].gclk_id_core].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
-  GCLK->PCHCTRL[sercom_clock[MAX3421_SERCOM_ID].gclk_id_slow].reg = GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
+  GCLK->PCHCTRL[sercom_clock[MAX3421_SERCOM_ID].gclk_id_core].reg =
+      GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
+  GCLK->PCHCTRL[sercom_clock[MAX3421_SERCOM_ID].gclk_id_slow].reg =
+      GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
 
   // Disable the SPI module
   sercom->SPI.CTRLA.bit.ENABLE = 0;
@@ -223,13 +230,18 @@ static void max3421_init(void) {
 
   // Set up SPI in master mode, MSB first, SPI mode 0
   sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_DOPO(MAX3421_TX_PAD) | SERCOM_SPI_CTRLA_DIPO(MAX3421_RX_PAD) |
-      SERCOM_SPI_CTRLA_MODE(3);
+                          SERCOM_SPI_CTRLA_MODE(3);
 
   sercom->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_SPI_CTRLB_RXEN;
   while (sercom->SPI.SYNCBUSY.bit.CTRLB == 1);
 
   // Set the baud rate
-  sercom->SPI.BAUD.reg = (uint8_t) (SystemCoreClock / (2 * baudrate) - 1);
+  uint8_t baud_reg = (uint8_t) (SystemCoreClock / (2 * baudrate));
+  if (baud_reg) {
+    baud_reg--;
+  }
+
+  sercom->SPI.BAUD.reg = baud_reg;
 
   // Configure PA12 as MOSI (PAD0), PA13 as SCK (PAD1), PA14 as MISO (PAD2), function C (sercom)
   gpio_set_pin_direction(MAX3421_SCK_PIN, GPIO_DIRECTION_OUT);
@@ -270,9 +282,9 @@ static void max3421_init(void) {
   while (EIC->SYNCBUSY.bit.ENABLE);
 
   // Configure EIC to trigger on falling edge
-  volatile uint32_t * eic_config;
+  volatile uint32_t* eic_config;
   uint8_t sense_shift;
-  if ( MAX3421_INTR_EIC_ID < 8 ) {
+  if (MAX3421_INTR_EIC_ID < 8) {
     eic_config = &EIC->CONFIG[0].reg;
     sense_shift = MAX3421_INTR_EIC_ID * 4;
   } else {
@@ -283,6 +295,11 @@ static void max3421_init(void) {
   *eic_config &= ~(7 << sense_shift);
   *eic_config |= 2 << sense_shift;
 
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+  // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
+  NVIC_SetPriority(EIC_0_IRQn + MAX3421_INTR_EIC_ID, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
+#endif
+
   // Enable External Interrupt
   EIC->INTENSET.reg = EIC_INTENSET_EXTINT(1 << MAX3421_INTR_EIC_ID);
 
@@ -296,9 +313,10 @@ void MAX3421_EIC_Handler(void) {
   EIC->INTFLAG.reg = EIC_INTFLAG_EXTINT(1 << MAX3421_INTR_EIC_ID);
 
   // Call the TinyUSB interrupt handler
-  tuh_int_handler(1);
+  tuh_int_handler(1, true);
 }
 
+// API to enable/disable MAX3421 INTR pin interrupt
 void tuh_max3421_int_api(uint8_t rhport, bool enabled) {
   (void) rhport;
 
@@ -310,24 +328,26 @@ void tuh_max3421_int_api(uint8_t rhport, bool enabled) {
   }
 }
 
+// API to control MAX3421 SPI CS
 void tuh_max3421_spi_cs_api(uint8_t rhport, bool active) {
   (void) rhport;
   gpio_set_pin_level(MAX3421_CS_PIN, active ? 0 : 1);
 }
 
-bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, size_t tx_len, uint8_t *rx_buf, size_t rx_len) {
+// API to transfer data with MAX3421 SPI
+// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
+bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes) {
   (void) rhport;
 
   Sercom* sercom = MAX3421_SERCOM;
 
-  size_t count = 0;
-  while (count < tx_len || count < rx_len) {
+  for (size_t count = 0; count < xfer_bytes; count++) {
     // Wait for the transmit buffer to be empty
     while (!sercom->SPI.INTFLAG.bit.DRE);
 
     // Write data to be transmitted
     uint8_t data = 0x00;
-    if (count < tx_len) {
+    if (tx_buf) {
       data = tx_buf[count];
     }
 
@@ -338,11 +358,9 @@ bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, size_t tx_l
 
     // Read received data
     data = (uint8_t) sercom->SPI.DATA.reg;
-    if (count < rx_len) {
+    if (rx_buf) {
       rx_buf[count] = data;
     }
-
-    count++;
   }
 
   // wait for bus idle and clear flags
@@ -353,5 +371,3 @@ bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, size_t tx_l
 }
 
 #endif
-
-#endif
diff --git a/hw/bsp/stm32g4/family.cmake b/hw/bsp/stm32g4/family.cmake
index 675a96c74..eee6bd9ed 100644
--- a/hw/bsp/stm32g4/family.cmake
+++ b/hw/bsp/stm32g4/family.cmake
@@ -26,50 +26,46 @@ set(FAMILY_MCUS STM32G4 CACHE INTERNAL "")
 #------------------------------------
 # only need to be built ONCE for all examples
 function(add_board_target BOARD_TARGET)
-  if (NOT TARGET ${BOARD_TARGET})
-    # Startup & Linker script
-    set(STARTUP_FILE_GNU ${ST_CMSIS}/Source/Templates/gcc/startup_${MCU_VARIANT}.s)
-    set(STARTUP_FILE_IAR ${ST_CMSIS}/Source/Templates/iar/startup_${MCU_VARIANT}.s)
-    set(LD_FILE_IAR ${ST_CMSIS}/Source/Templates/iar/linker/${MCU_VARIANT}_flash.icf)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
 
-    add_library(${BOARD_TARGET} STATIC
-      ${ST_CMSIS}/Source/Templates/system_${ST_PREFIX}.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_cortex.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_pwr_ex.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_rcc.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_rcc_ex.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_gpio.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_uart.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_uart_ex.c
-      ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
-      )
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
-      ${CMSIS_5}/CMSIS/Core/Include
-      ${ST_CMSIS}/Include
-      ${ST_HAL_DRIVER}/Inc
-      )
-    target_compile_options(${BOARD_TARGET} PUBLIC
-      )
-    target_compile_definitions(${BOARD_TARGET} PUBLIC
-      )
+  # Startup & Linker script
+  set(STARTUP_FILE_GNU ${ST_CMSIS}/Source/Templates/gcc/startup_${MCU_VARIANT}.s)
+  set(STARTUP_FILE_IAR ${ST_CMSIS}/Source/Templates/iar/startup_${MCU_VARIANT}.s)
+  set(LD_FILE_IAR ${ST_CMSIS}/Source/Templates/iar/linker/${MCU_VARIANT}_flash.icf)
 
-    update_board(${BOARD_TARGET})
+  add_library(${BOARD_TARGET} STATIC
+    ${ST_CMSIS}/Source/Templates/system_${ST_PREFIX}.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_cortex.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_gpio.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_pwr_ex.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_rcc.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_rcc_ex.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_uart.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_uart_ex.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMSIS_5}/CMSIS/Core/Include
+    ${ST_CMSIS}/Include
+    ${ST_HAL_DRIVER}/Inc
+    )
+  update_board(${BOARD_TARGET})
 
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--script=${LD_FILE_GNU}"
-        -nostartfiles
-        # 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 ()
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -nostartfiles
+      # 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 ()
 endfunction()
 
diff --git a/hw/bsp/stm32u5/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/stm32u5/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..138fc6ba6
--- /dev/null
+++ b/hw/bsp/stm32u5/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,165 @@
+/*
+ * 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 "stm32u5xx.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#define configENABLE_FPU                        1
+#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         0
+#define configSUPPORT_DYNAMIC_ALLOCATION        1
+
+/* 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
+
+/* 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
+
+/* Define to trap errors during development. */
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
+  #define configASSERT(_exp) \
+    do {\
+      if ( !(_exp) ) { \
+        volatile uint32_t* ARM_CM_DHCSR =  ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
+        if ( (*ARM_CM_DHCSR) & 1UL ) {  /* Only halt mcu if debugger is attached */ \
+          taskDISABLE_INTERRUPTS(); \
+           __asm("BKPT #0\n"); \
+        }\
+      }\
+    } while(0)
+#else
+  #define configASSERT( x )
+#endif
+
+/* 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       4
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<max_lun;
 }
 
-uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun)
-{
+uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun) {
   msch_interface_t* p_msc = get_itf(dev_addr);
   return p_msc->capacity[lun].block_count;
 }
 
-uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun)
-{
+uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun) {
   msch_interface_t* p_msc = get_itf(dev_addr);
   return p_msc->capacity[lun].block_size;
 }
 
-bool tuh_msc_mounted(uint8_t dev_addr)
-{
+bool tuh_msc_mounted(uint8_t dev_addr) {
   msch_interface_t* p_msc = get_itf(dev_addr);
   return p_msc->mounted;
 }
 
-bool tuh_msc_ready(uint8_t dev_addr)
-{
+bool tuh_msc_ready(uint8_t dev_addr) {
   msch_interface_t* p_msc = get_itf(dev_addr);
   return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in) && !usbh_edpt_busy(dev_addr, p_msc->ep_out);
 }
@@ -127,20 +119,20 @@ bool tuh_msc_ready(uint8_t dev_addr)
 //--------------------------------------------------------------------+
 // PUBLIC API: SCSI COMMAND
 //--------------------------------------------------------------------+
-static inline void cbw_init(msc_cbw_t *cbw, uint8_t lun)
-{
+static inline void cbw_init(msc_cbw_t* cbw, uint8_t lun) {
   tu_memclr(cbw, sizeof(msc_cbw_t));
   cbw->signature = MSC_CBW_SIGNATURE;
   cbw->tag       = 0x54555342; // TUSB
   cbw->lun       = lun;
 }
 
-bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
-  msch_interface_t* p_msc = get_itf(dev_addr);
+bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data,
+                          tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
+  msch_interface_t* p_msc = get_itf(daddr);
   TU_VERIFY(p_msc->configured);
 
-  // TODO claim endpoint
+  // claim endpoint
+  TU_VERIFY(usbh_edpt_claim(daddr, p_msc->ep_out));
 
   p_msc->cbw = *cbw;
   p_msc->stage = MSC_STAGE_CMD;
@@ -148,15 +140,18 @@ bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tu
   p_msc->complete_cb = complete_cb;
   p_msc->complete_arg = arg;
 
-  TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)));
+  if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t))) {
+    usbh_edpt_release(daddr, p_msc->ep_out);
+    return false;
+  }
 
   return true;
 }
 
-bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
-   msch_interface_t* p_msc = get_itf(dev_addr);
-   TU_VERIFY(p_msc->configured);
+bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response,
+                           tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
+  msch_interface_t* p_msc = get_itf(dev_addr);
+  TU_VERIFY(p_msc->configured);
 
   msc_cbw_t cbw;
   cbw_init(&cbw, lun);
@@ -169,8 +164,8 @@ bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_r
   return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
 }
 
-bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response,
+                     tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
   msch_interface_t* p_msc = get_itf(dev_addr);
   TU_VERIFY(p_msc->mounted);
 
@@ -181,18 +176,16 @@ bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* respons
   cbw.dir         = TUSB_DIR_IN_MASK;
   cbw.cmd_len     = sizeof(scsi_inquiry_t);
 
-  scsi_inquiry_t const cmd_inquiry =
-  {
-    .cmd_code     = SCSI_CMD_INQUIRY,
-    .alloc_length = sizeof(scsi_inquiry_resp_t)
+  scsi_inquiry_t const cmd_inquiry = {
+      .cmd_code     = SCSI_CMD_INQUIRY,
+      .alloc_length = sizeof(scsi_inquiry_resp_t)
   };
   memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
 
   return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
 }
 
-bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
   msch_interface_t* p_msc = get_itf(dev_addr);
   TU_VERIFY(p_msc->configured);
 
@@ -200,16 +193,16 @@ bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_
   cbw_init(&cbw, lun);
 
   cbw.total_bytes = 0;
-  cbw.dir         = TUSB_DIR_OUT;
-  cbw.cmd_len     = sizeof(scsi_test_unit_ready_t);
-  cbw.command[0]  = SCSI_CMD_TEST_UNIT_READY;
-  cbw.command[1]  = lun; // according to wiki TODO need verification
+  cbw.dir        = TUSB_DIR_OUT;
+  cbw.cmd_len    = sizeof(scsi_test_unit_ready_t);
+  cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
+  cbw.command[1] = lun; // according to wiki TODO need verification
 
   return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb, arg);
 }
 
-bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void* response,
+                           tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
   msc_cbw_t cbw;
   cbw_init(&cbw, lun);
 
@@ -217,73 +210,64 @@ bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_ms
   cbw.dir         = TUSB_DIR_IN_MASK;
   cbw.cmd_len     = sizeof(scsi_request_sense_t);
 
-  scsi_request_sense_t const cmd_request_sense =
-  {
-    .cmd_code     = SCSI_CMD_REQUEST_SENSE,
-    .alloc_length = 18
+  scsi_request_sense_t const cmd_request_sense = {
+      .cmd_code     = SCSI_CMD_REQUEST_SENSE,
+      .alloc_length = 18
   };
-
   memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
 
   return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
 }
 
-bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void* buffer, uint32_t lba, uint16_t block_count,
+                    tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
   msch_interface_t* p_msc = get_itf(dev_addr);
   TU_VERIFY(p_msc->mounted);
 
   msc_cbw_t cbw;
   cbw_init(&cbw, lun);
 
-  cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
-  cbw.dir         = TUSB_DIR_IN_MASK;
-  cbw.cmd_len     = sizeof(scsi_read10_t);
+  cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
+  cbw.dir = TUSB_DIR_IN_MASK;
+  cbw.cmd_len = sizeof(scsi_read10_t);
 
-  scsi_read10_t const cmd_read10 =
-  {
-    .cmd_code    = SCSI_CMD_READ_10,
-    .lba         = tu_htonl(lba),
-    .block_count = tu_htons(block_count)
+  scsi_read10_t const cmd_read10 = {
+      .cmd_code    = SCSI_CMD_READ_10,
+      .lba         = tu_htonl(lba),
+      .block_count = tu_htons(block_count)
   };
-
   memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
 
   return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb, arg);
 }
 
-bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
-{
+bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const* buffer, uint32_t lba, uint16_t block_count,
+                     tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
   msch_interface_t* p_msc = get_itf(dev_addr);
   TU_VERIFY(p_msc->mounted);
 
   msc_cbw_t cbw;
   cbw_init(&cbw, lun);
 
-  cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
+  cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
   cbw.dir         = TUSB_DIR_OUT;
   cbw.cmd_len     = sizeof(scsi_write10_t);
 
-  scsi_write10_t const cmd_write10 =
-  {
-    .cmd_code    = SCSI_CMD_WRITE_10,
-    .lba         = tu_htonl(lba),
-    .block_count = tu_htons(block_count)
+  scsi_write10_t const cmd_write10 = {
+      .cmd_code    = SCSI_CMD_WRITE_10,
+      .lba         = tu_htonl(lba),
+      .block_count = tu_htons(block_count)
   };
-
   memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
 
-  return tuh_msc_scsi_command(dev_addr, &cbw, (void*)(uintptr_t) buffer, complete_cb, arg);
+  return tuh_msc_scsi_command(dev_addr, &cbw, (void*) (uintptr_t) buffer, complete_cb, arg);
 }
 
 #if 0
 // MSC interface Reset (not used now)
-bool tuh_msc_reset(uint8_t dev_addr)
-{
-  tusb_control_request_t const new_request =
-  {
-    .bmRequestType_bit =
-    {
+bool tuh_msc_reset(uint8_t dev_addr) {
+  tusb_control_request_t const new_request = {
+    .bmRequestType_bit = {
       .recipient = TUSB_REQ_RCPT_INTERFACE,
       .type      = TUSB_REQ_TYPE_CLASS,
       .direction = TUSB_DIR_OUT
@@ -300,79 +284,71 @@ bool tuh_msc_reset(uint8_t dev_addr)
 //--------------------------------------------------------------------+
 // CLASS-USBH API
 //--------------------------------------------------------------------+
-void msch_init(void)
-{
+void msch_init(void) {
   tu_memclr(_msch_itf, sizeof(_msch_itf));
 }
 
-void msch_close(uint8_t dev_addr)
-{
-  TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
+void msch_close(uint8_t dev_addr) {
+  TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX,);
   msch_interface_t* p_msc = get_itf(dev_addr);
-  TU_VERIFY(p_msc->configured, );
+  TU_VERIFY(p_msc->configured,);
 
   TU_LOG_DRV("  MSCh close addr = %d\r\n", dev_addr);
 
   // invoke Application Callback
   if (p_msc->mounted) {
-    if(tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
+    if (tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
   }
 
   tu_memclr(p_msc, sizeof(msch_interface_t));
 }
 
-bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
-{
+bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
   msch_interface_t* p_msc = get_itf(dev_addr);
   msc_cbw_t const * cbw = &p_msc->cbw;
   msc_csw_t       * csw = &p_msc->csw;
 
-  switch (p_msc->stage)
-  {
+  switch (p_msc->stage) {
     case MSC_STAGE_CMD:
       // Must be Command Block
-      TU_ASSERT(ep_addr == p_msc->ep_out &&  event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
+      TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
 
-      if ( cbw->total_bytes && p_msc->buffer )
-      {
+      if (cbw->total_bytes && p_msc->buffer) {
         // Data stage if any
         p_msc->stage = MSC_STAGE_DATA;
-
         uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
         TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes));
-      }else
-      {
+      } else {
         // Status stage
         p_msc->stage = MSC_STAGE_STATUS;
         TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
       }
-    break;
+      break;
 
     case MSC_STAGE_DATA:
       // Status stage
       p_msc->stage = MSC_STAGE_STATUS;
       TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
-    break;
+      break;
 
     case MSC_STAGE_STATUS:
       // SCSI op is complete
       p_msc->stage = MSC_STAGE_IDLE;
 
-      if (p_msc->complete_cb)
-      {
-        tuh_msc_complete_data_t const cb_data =
-        {
-          .cbw = cbw,
-          .csw = csw,
-          .scsi_data = p_msc->buffer,
-          .user_arg = p_msc->complete_arg
+      if (p_msc->complete_cb) {
+        tuh_msc_complete_data_t const cb_data = {
+            .cbw = cbw,
+            .csw = csw,
+            .scsi_data = p_msc->buffer,
+            .user_arg = p_msc->complete_arg
         };
         p_msc->complete_cb(dev_addr, &cb_data);
       }
-    break;
+      break;
 
-    // unknown state
-    default: break;
+      // unknown state
+    default:
+      break;
   }
 
   return true;
@@ -381,39 +357,35 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
 //--------------------------------------------------------------------+
 // MSC Enumeration
 //--------------------------------------------------------------------+
-
-static void config_get_maxlun_complete (tuh_xfer_t* xfer);
-static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data);
+static void config_get_maxlun_complete(tuh_xfer_t* xfer);
+static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
 static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
 static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
 
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
-{
+bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
   (void) rhport;
   TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
-             MSC_PROTOCOL_BOT  == desc_itf->bInterfaceProtocol);
+             MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
 
   // msc driver length is fixed
-  uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+  uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) +
+                                       desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
   TU_ASSERT(drv_len <= max_len);
 
   msch_interface_t* p_msc = get_itf(dev_addr);
-  tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(desc_itf);
+  tusb_desc_endpoint_t const* ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(desc_itf);
 
-  for(uint32_t i=0; i<2; i++)
-  {
+  for (uint32_t i = 0; i < 2; i++) {
     TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
     TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
 
-    if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
-    {
+    if (TUSB_DIR_IN == tu_edpt_dir(ep_desc->bEndpointAddress)) {
       p_msc->ep_in = ep_desc->bEndpointAddress;
-    }else
-    {
+    } else {
       p_msc->ep_out = ep_desc->bEndpointAddress;
     }
 
-    ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
+    ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(ep_desc);
   }
 
   p_msc->itf_num = desc_itf->bInterfaceNumber;
@@ -430,32 +402,31 @@ bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) {
   //------------- Get Max Lun -------------//
   TU_LOG_DRV("MSC Get Max Lun\r\n");
   tusb_control_request_t const request = {
-    .bmRequestType_bit = {
-      .recipient = TUSB_REQ_RCPT_INTERFACE,
-      .type      = TUSB_REQ_TYPE_CLASS,
-      .direction = TUSB_DIR_IN
-    },
-    .bRequest = MSC_REQ_GET_MAX_LUN,
-    .wValue   = 0,
-    .wIndex   = itf_num,
-    .wLength  = 1
+      .bmRequestType_bit = {
+          .recipient = TUSB_REQ_RCPT_INTERFACE,
+          .type      = TUSB_REQ_TYPE_CLASS,
+          .direction = TUSB_DIR_IN
+      },
+      .bRequest = MSC_REQ_GET_MAX_LUN,
+      .wValue   = 0,
+      .wIndex   = itf_num,
+      .wLength  = 1
   };
 
   tuh_xfer_t xfer = {
-    .daddr       = dev_addr,
-    .ep_addr     = 0,
-    .setup       = &request,
-    .buffer      = _msch_buffer,
-    .complete_cb = config_get_maxlun_complete,
-    .user_data    = 0
+      .daddr       = dev_addr,
+      .ep_addr     = 0,
+      .setup       = &request,
+      .buffer      = _msch_buffer,
+      .complete_cb = config_get_maxlun_complete,
+      .user_data    = 0
   };
   TU_ASSERT(tuh_control_xfer(&xfer));
 
   return true;
 }
 
-static void config_get_maxlun_complete (tuh_xfer_t* xfer)
-{
+static void config_get_maxlun_complete(tuh_xfer_t* xfer) {
   uint8_t const daddr = xfer->daddr;
   msch_interface_t* p_msc = get_itf(daddr);
 
@@ -471,18 +442,16 @@ static void config_get_maxlun_complete (tuh_xfer_t* xfer)
   tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete, 0);
 }
 
-static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
-{
+static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
   msc_cbw_t const* cbw = cb_data->cbw;
   msc_csw_t const* csw = cb_data->csw;
 
-  if (csw->status == 0)
-  {
+  if (csw->status == 0) {
     // Unit is ready, read its capacity
     TU_LOG_DRV("SCSI Read Capacity\r\n");
-    tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer), config_read_capacity_complete, 0);
-  }else
-  {
+    tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer),
+                          config_read_capacity_complete, 0);
+  } else {
     // Note: During enumeration, some device fails Test Unit Ready and require a few retries
     // with Request Sense to start working !!
     // TODO limit number of retries
@@ -493,8 +462,7 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_d
   return true;
 }
 
-static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
-{
+static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
   msc_cbw_t const* cbw = cb_data->cbw;
   msc_csw_t const* csw = cb_data->csw;
 
@@ -503,8 +471,7 @@ static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_dat
   return true;
 }
 
-static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
-{
+static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
   msc_cbw_t const* cbw = cb_data->cbw;
   msc_csw_t const* csw = cb_data->csw;
 
diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h
index 6c0e5c9dd..9ca1b4703 100644
--- a/src/class/msc/msc_host.h
+++ b/src/class/msc/msc_host.h
@@ -24,8 +24,8 @@
  * This file is part of the TinyUSB stack.
  */
 
-#ifndef _TUSB_MSC_HOST_H_
-#define _TUSB_MSC_HOST_H_
+#ifndef TUSB_MSC_HOST_H_
+#define TUSB_MSC_HOST_H_
 
 #include "msc.h"
 
@@ -73,7 +73,7 @@ uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
 // Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
 // Complete callback is invoked when SCSI op is complete.
 // return true if success, false if there is already pending operation.
-bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
+bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
 
 // Perform SCSI Inquiry command
 // Complete callback is invoked when SCSI op is complete.
@@ -123,4 +123,4 @@ bool msch_xfer_cb    (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, ui
  }
 #endif
 
-#endif /* _TUSB_MSC_HOST_H_ */
+#endif
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index 6fffed11c..caeb5f2ef 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -75,8 +75,6 @@
 #include "tusb_types.h"
 #include "tusb_debug.h"
 
-#include "tusb_timeout.h" // TODO remove
-
 //--------------------------------------------------------------------+
 // Optional API implemented by application if needed
 // TODO move to a more ovious place/file
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index 12355e8be..1b5f53dfc 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -56,12 +56,8 @@
  *   #define TU_VERIFY(cond)                  if(cond) return false;
  *   #define TU_VERIFY(cond,ret)              if(cond) return ret;
  *
- *   #define TU_VERIFY_HDLR(cond,handler)     if(cond) {handler; return false;}
- *   #define TU_VERIFY_HDLR(cond,ret,handler) if(cond) {handler; 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
@@ -97,40 +93,23 @@
   #define TU_BREAKPOINT() do {} while (0)
 #endif
 
-/*------------------------------------------------------------------*/
-/* Macro Generator
- *------------------------------------------------------------------*/
-
 // 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
-
-/*------------- Generator for TU_VERIFY and TU_VERIFY_HDLR -------------*/
-#define TU_VERIFY_DEFINE(_cond, _handler, _ret)  do            \
-{                                                              \
-  if ( !(_cond) ) { _handler; return _ret;  }                  \
-} while(0)
 
 /*------------------------------------------------------------------*/
 /* TU_VERIFY
  * - TU_VERIFY_1ARGS : return false if failed
  * - TU_VERIFY_2ARGS : return provided value if failed
  *------------------------------------------------------------------*/
-#define TU_VERIFY_1ARGS(_cond)                         TU_VERIFY_DEFINE(_cond, , false)
-#define TU_VERIFY_2ARGS(_cond, _ret)                   TU_VERIFY_DEFINE(_cond, , _ret)
+#define TU_VERIFY_DEFINE(_cond, _ret)    \
+  do {                                   \
+    if ( !(_cond) ) { return _ret; }     \
+  } while(0)
 
-#define TU_VERIFY(...)                   _GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, UNUSED)(__VA_ARGS__)
+#define TU_VERIFY_1ARGS(_cond)         TU_VERIFY_DEFINE(_cond, false)
+#define TU_VERIFY_2ARGS(_cond, _ret)   TU_VERIFY_DEFINE(_cond, _ret)
 
-
-/*------------------------------------------------------------------*/
-/* TU_VERIFY WITH HANDLER
- * - TU_VERIFY_HDLR_2ARGS : execute handler, return false if failed
- * - TU_VERIFY_HDLR_3ARGS : execute handler, return provided error if failed
- *------------------------------------------------------------------*/
-#define TU_VERIFY_HDLR_2ARGS(_cond, _handler)           TU_VERIFY_DEFINE(_cond, _handler, false)
-#define TU_VERIFY_HDLR_3ARGS(_cond, _handler, _ret)     TU_VERIFY_DEFINE(_cond, _handler, _ret)
-
-#define TU_VERIFY_HDLR(...)              _GET_4TH_ARG(__VA_ARGS__, TU_VERIFY_HDLR_3ARGS, TU_VERIFY_HDLR_2ARGS,UNUSED)(__VA_ARGS__)
+#define TU_VERIFY(...)                 _GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, _dummy)(__VA_ARGS__)
 
 /*------------------------------------------------------------------*/
 /* ASSERT
@@ -138,19 +117,20 @@
  * - 1 arg : return false if failed
  * - 2 arg : return error if failed
  *------------------------------------------------------------------*/
-#define ASSERT_1ARGS(_cond)            TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), false)
-#define ASSERT_2ARGS(_cond, _ret)      TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), _ret)
+#define TU_ASSERT_DEFINE(_cond, _ret)                                 \
+  do {                                                                \
+    if ( !(_cond) ) { _MESS_FAILED(); 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__, ASSERT_2ARGS, ASSERT_1ARGS,UNUSED)(__VA_ARGS__)
+#define TU_ASSERT(...)                 _GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_2ARGS, TU_ASSERT_1ARGS, _dummy)(__VA_ARGS__)
 #endif
 
-/*------------------------------------------------------------------*/
-/* ASSERT HDLR
- *------------------------------------------------------------------*/
-
 #ifdef __cplusplus
  }
 #endif
 
-#endif /* TUSB_VERIFY_H_ */
+#endif
diff --git a/src/host/hcd.h b/src/host/hcd.h
index e25b20ea3..2bde289df 100644
--- a/src/host/hcd.h
+++ b/src/host/hcd.h
@@ -131,7 +131,7 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) TU_AT
 bool hcd_init(uint8_t rhport);
 
 // Interrupt Handler
-void hcd_int_handler(uint8_t rhport);
+void hcd_int_handler(uint8_t rhport, bool in_isr);
 
 // Enable USB interrupt
 void hcd_int_enable (uint8_t rhport);
diff --git a/src/host/usbh.c b/src/host/usbh.c
index e332f35cc..8d75c9726 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -817,6 +817,7 @@ bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr)
 }
 
 // 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)
 {
@@ -1713,19 +1714,16 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur
   return true;
 }
 
-void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
-{
+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++)
-  {
+  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)
-    {
+    if (driver) {
       TU_LOG_USBH("%s set config: itf = %u\r\n", driver->name, itf_num);
       driver->set_config(dev_addr, itf_num);
       break;
@@ -1733,23 +1731,19 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
   }
 
   // all interface are configured
-  if (itf_num == CFG_TUH_INTERFACE_MAX)
-  {
+  if (itf_num == CFG_TUH_INTERFACE_MAX) {
     enum_full_complete();
 
-    if (is_hub_addr(dev_addr))
-    {
+    if (is_hub_addr(dev_addr)) {
       TU_LOG(CFG_TUH_LOG_LEVEL, "HUB address = %u is mounted\r\n", dev_addr);
-    }else
-    {
+    }else {
       // Invoke callback if available
       if (tuh_mount_cb) tuh_mount_cb(dev_addr);
     }
   }
 }
 
-static void enum_full_complete(void)
-{
+static void enum_full_complete(void) {
   // mark enumeration as complete
   _dev0.enumerating = 0;
 
diff --git a/src/host/usbh.h b/src/host/usbh.h
index 684e8240e..770d70dfe 100644
--- a/src/host/usbh.h
+++ b/src/host/usbh.h
@@ -124,11 +124,16 @@ void tuh_task(void) {
 bool tuh_task_event_ready(void);
 
 #ifndef _TUSB_HCD_H_
-extern void hcd_int_handler(uint8_t rhport);
+extern void hcd_int_handler(uint8_t rhport, bool in_isr);
 #endif
 
-// Interrupt handler, name alias to HCD
-#define tuh_int_handler   hcd_int_handler
+// 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);
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index 238b518a0..787c1e511 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -210,16 +210,21 @@ static max3421_data_t _hcd_data;
 // API: SPI transfer with MAX3421E, must be implemented by application
 //--------------------------------------------------------------------+
 
-void tuh_max3421_spi_cs_api(uint8_t rhport, bool active);
-bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const * tx_buf, size_t tx_len, uint8_t * rx_buf, size_t rx_len);
-void tuh_max3421_int_api(uint8_t rhport, bool enabled);
+// API to control MAX3421 SPI CS
+extern void tuh_max3421_spi_cs_api(uint8_t rhport, bool active);
 
-static void handle_connect_irq(uint8_t rhport, bool in_isr);
-static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr);
+// API to transfer data with MAX3421 SPI
+// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
+extern bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes);
+
+// API to enable/disable MAX3421 INTR pin interrupt
+extern void tuh_max3421_int_api(uint8_t rhport, bool enabled);
 
 //--------------------------------------------------------------------+
 // SPI Helper
 //--------------------------------------------------------------------+
+static void handle_connect_irq(uint8_t rhport, bool in_isr);
+static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr);
 
 static void max3421_spi_lock(uint8_t rhport, bool in_isr) {
   // disable interrupt and mutex lock (for pre-emptive RTOS) if not in_isr
@@ -249,9 +254,9 @@ static void fifo_write(uint8_t rhport, uint8_t reg, uint8_t const * buffer, uint
 
   max3421_spi_lock(rhport, in_isr);
 
-  tuh_max3421_spi_xfer_api(rhport, ®, 1, &hirq, 1);
+  tuh_max3421_spi_xfer_api(rhport, ®, &hirq, 1);
   _hcd_data.hirq = hirq;
-  tuh_max3421_spi_xfer_api(rhport, buffer, len, NULL, 0);
+  tuh_max3421_spi_xfer_api(rhport, buffer, NULL, len);
 
   max3421_spi_unlock(rhport, in_isr);
 
@@ -263,9 +268,9 @@ static void fifo_read(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_is
 
   max3421_spi_lock(rhport, in_isr);
 
-  tuh_max3421_spi_xfer_api(rhport, ®, 1, &hirq, 0);
+  tuh_max3421_spi_xfer_api(rhport, ®, &hirq, 1);
   _hcd_data.hirq = hirq;
-  tuh_max3421_spi_xfer_api(rhport, NULL, 0, buffer, len);
+  tuh_max3421_spi_xfer_api(rhport, NULL, buffer, len);
 
   max3421_spi_unlock(rhport, in_isr);
 }
@@ -276,7 +281,7 @@ static void reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr) {
 
   max3421_spi_lock(rhport, in_isr);
 
-  tuh_max3421_spi_xfer_api(rhport, tx_buf, 2, rx_buf, 2);
+  tuh_max3421_spi_xfer_api(rhport, tx_buf, rx_buf, 2);
 
   max3421_spi_unlock(rhport, in_isr);
 
@@ -290,7 +295,7 @@ static uint8_t reg_read(uint8_t rhport, uint8_t reg, bool in_isr) {
 
   max3421_spi_lock(rhport, in_isr);
 
-  bool ret = tuh_max3421_spi_xfer_api(rhport, tx_buf, 2, rx_buf, 2);
+  bool ret = tuh_max3421_spi_xfer_api(rhport, tx_buf, rx_buf, 2);
 
   max3421_spi_unlock(rhport, in_isr);
 
@@ -428,8 +433,8 @@ bool hcd_init(uint8_t rhport) {
   reg_write(rhport, PINCTL_ADDR, PINCTL_FDUPSPI, false);
 
   // V1 is 0x01, V2 is 0x12, V3 is 0x13
-  // uint8_t const revision = reg_read(rhport, REVISION_ADDR, false);
-  // TU_LOG2_HEX(revision);
+//  uint8_t const revision = reg_read(rhport, REVISION_ADDR, false);
+//  TU_LOG2_HEX(revision);
 
   // reset
   reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false);
@@ -693,9 +698,7 @@ static void handle_connect_irq(uint8_t rhport, bool in_isr) {
 
       // port reset anyway, this will help to stable bus signal for next connection
       reg_write(rhport, HCTL_ADDR, HCTL_BUSRST, in_isr);
-
       hcd_event_device_remove(rhport, in_isr);
-
       reg_write(rhport, HCTL_ADDR, 0, in_isr);
       break;
 
@@ -721,13 +724,12 @@ static void handle_connect_irq(uint8_t rhport, bool in_isr) {
       free_ep(daddr);
 
       hcd_event_device_attach(rhport, in_isr);
-
       break;
     }
   }
 }
 
-static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl) {
+static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) {
   uint8_t const ep_addr = tu_edpt_addr(ep->ep_num, ep->ep_dir);
 
   // save data toggle
@@ -738,20 +740,20 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re
   }
 
   ep->xfer_pending = 0;
-  hcd_event_xfer_complete(ep->daddr, ep_addr, ep->xferred_len, result, true);
+  hcd_event_xfer_complete(ep->daddr, ep_addr, ep->xferred_len, result, in_isr);
 
   // Find next pending endpoint
   max3421_ep_t *next_ep = find_next_pending_ep(ep);
   if (next_ep) {
-    xact_inout(rhport, next_ep, true, true);
+    xact_inout(rhport, next_ep, true, in_isr);
   }else {
     // no more pending
     atomic_flag_clear(&_hcd_data.busy);
   }
 }
 
-static void handle_xfer_done(uint8_t rhport) {
-  uint8_t const hrsl = reg_read(rhport, HRSL_ADDR, true);
+static void handle_xfer_done(uint8_t rhport, bool in_isr) {
+  uint8_t const hrsl = reg_read(rhport, HRSL_ADDR, in_isr);
   uint8_t const hresult = hrsl & HRSL_RESULT_MASK;
 
   uint8_t const ep_num = _hcd_data.hxfr & HXFR_EPNUM_MASK;
@@ -774,17 +776,17 @@ static void handle_xfer_done(uint8_t rhport) {
     case HRSL_NAK:
       if (ep_num == 0) {
         // NAK on control, retry immediately
-        hxfr_write(rhport, _hcd_data.hxfr, true);
+        hxfr_write(rhport, _hcd_data.hxfr, in_isr);
       }else {
         // NAK on non-control, find next pending to switch
         max3421_ep_t *next_ep = find_next_pending_ep(ep);
 
         if (ep == next_ep) {
           // this endpoint is only one pending, retry immediately
-          hxfr_write(rhport, _hcd_data.hxfr, true);
+          hxfr_write(rhport, _hcd_data.hxfr, in_isr);
         }else if (next_ep) {
           // switch to next pending TODO could have issue with double buffered if not clear previously out data
-          xact_inout(rhport, next_ep, true, true);
+          xact_inout(rhport, next_ep, true, in_isr);
         }else {
           TU_ASSERT(false,);
         }
@@ -802,7 +804,7 @@ static void handle_xfer_done(uint8_t rhport) {
   }
 
   if (xfer_result != XFER_RESULT_SUCCESS) {
-    xfer_complete_isr(rhport, ep, xfer_result, hrsl);
+    xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
     return;
   }
 
@@ -814,10 +816,10 @@ static void handle_xfer_done(uint8_t rhport) {
 
     // short packet or all bytes transferred
     if ( ep->xfer_complete ) {
-      xfer_complete_isr(rhport, ep, xfer_result, hrsl);
+      xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
     }else {
       // more to transfer
-      hxfr_write(rhport, _hcd_data.hxfr, true);
+      hxfr_write(rhport, _hcd_data.hxfr, in_isr);
     }
   } else {
     // SETUP or OUT transfer
@@ -835,10 +837,10 @@ static void handle_xfer_done(uint8_t rhport) {
     ep->buf += xact_len;
 
     if (xact_len < ep->packet_size || ep->xferred_len >= ep->total_len) {
-      xfer_complete_isr(rhport, ep, xfer_result, hrsl);
+      xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
     } else {
       // more to transfer
-      xact_out(rhport, ep, false, true);
+      xact_out(rhport, ep, false, in_isr);
     }
   }
 }
@@ -862,9 +864,9 @@ void print_hirq(uint8_t hirq) {
   #define print_hirq(hirq)
 #endif
 
-// Interrupt Handler
-void hcd_int_handler(uint8_t rhport) {
-  uint8_t hirq = reg_read(rhport, HIRQ_ADDR, true) & _hcd_data.hien;
+// Interrupt handler
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+  uint8_t hirq = reg_read(rhport, HIRQ_ADDR, in_isr) & _hcd_data.hien;
   if (!hirq) return;
 //  print_hirq(hirq);
 
@@ -873,7 +875,7 @@ void hcd_int_handler(uint8_t rhport) {
   }
 
   if (hirq & HIRQ_CONDET_IRQ) {
-    handle_connect_irq(rhport, true);
+    handle_connect_irq(rhport, in_isr);
   }
 
   // queue more transfer in handle_xfer_done() can cause hirq to be set again while external IRQ may not catch and/or
@@ -882,21 +884,21 @@ void hcd_int_handler(uint8_t rhport) {
     if ( hirq & HIRQ_RCVDAV_IRQ ) {
       uint8_t const ep_num = _hcd_data.hxfr & HXFR_EPNUM_MASK;
       max3421_ep_t *ep = find_opened_ep(_hcd_data.peraddr, ep_num, 1);
-      uint8_t xact_len;
+      uint8_t xact_len = 0;
 
       // RCVDAV_IRQ can trigger 2 times (dual buffered)
       while ( hirq & HIRQ_RCVDAV_IRQ ) {
-        uint8_t rcvbc = reg_read(rhport, RCVBC_ADDR, true);
+        uint8_t rcvbc = reg_read(rhport, RCVBC_ADDR, in_isr);
         xact_len = (uint8_t) tu_min16(rcvbc, ep->total_len - ep->xferred_len);
         if ( xact_len ) {
-          fifo_read(rhport, ep->buf, xact_len, true);
+          fifo_read(rhport, ep->buf, xact_len, in_isr);
           ep->buf += xact_len;
           ep->xferred_len += xact_len;
         }
 
         // ack RCVDVAV IRQ
-        hirq_write(rhport, HIRQ_RCVDAV_IRQ, true);
-        hirq = reg_read(rhport, HIRQ_ADDR, true);
+        hirq_write(rhport, HIRQ_RCVDAV_IRQ, in_isr);
+        hirq = reg_read(rhport, HIRQ_ADDR, in_isr);
       }
 
       if ( xact_len < ep->packet_size || ep->xferred_len >= ep->total_len ) {
@@ -905,17 +907,17 @@ void hcd_int_handler(uint8_t rhport) {
     }
 
     if ( hirq & HIRQ_HXFRDN_IRQ ) {
-      hirq_write(rhport, HIRQ_HXFRDN_IRQ, true);
-      handle_xfer_done(rhport);
+      hirq_write(rhport, HIRQ_HXFRDN_IRQ, in_isr);
+      handle_xfer_done(rhport, in_isr);
     }
 
-    hirq = reg_read(rhport, HIRQ_ADDR, true);
+    hirq = reg_read(rhport, HIRQ_ADDR, in_isr);
   }
 
   // clear all interrupt except SNDBAV_IRQ (never clear by us). Note RCVDAV_IRQ, HXFRDN_IRQ already clear while processing
   hirq &= ~HIRQ_SNDBAV_IRQ;
   if ( hirq ) {
-    hirq_write(rhport, hirq, true);
+    hirq_write(rhport, hirq, in_isr);
   }
 }
 
diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c
index c93c33fc0..572b9826c 100644
--- a/src/portable/ehci/ehci.c
+++ b/src/portable/ehci/ehci.c
@@ -656,8 +656,8 @@ void process_period_xfer_isr(uint8_t rhport, uint32_t interval_ms)
 }
 
 //------------- Host Controller Driver's Interrupt Handler -------------//
-void hcd_int_handler(uint8_t rhport)
-{
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+  (void) in_isr;
   ehci_registers_t* regs = ehci_data.regs;
   uint32_t const int_status = regs->status;
 
diff --git a/src/portable/mentor/musb/hcd_musb.c b/src/portable/mentor/musb/hcd_musb.c
index 02090df85..5312c2812 100644
--- a/src/portable/mentor/musb/hcd_musb.c
+++ b/src/portable/mentor/musb/hcd_musb.c
@@ -847,8 +847,10 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
 /*-------------------------------------------------------------------
  * ISR
  *-------------------------------------------------------------------*/
-void hcd_int_handler(uint8_t rhport)
+void hcd_int_handler(uint8_t rhport, bool in_isr)
 {
+  (void) in_isr;
+
   uint_fast8_t is, txis, rxis;
 
   is   = USB0->IS;   /* read and clear interrupt status */
diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c
index 55327e02d..57684b259 100644
--- a/src/portable/nxp/khci/hcd_khci.c
+++ b/src/portable/nxp/khci/hcd_khci.c
@@ -447,6 +447,10 @@ void hcd_port_reset(uint8_t rhport)
   _hcd.need_reset = false;
 }
 
+void hcd_port_reset_end(uint8_t rhport) {
+  (void) rhport;
+}
+
 tusb_speed_t hcd_port_speed_get(uint8_t rhport)
 {
   (void)rhport;
@@ -583,8 +587,9 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
 /*--------------------------------------------------------------------+
  * ISR
  *--------------------------------------------------------------------+*/
-void hcd_int_handler(uint8_t rhport)
+void hcd_int_handler(uint8_t rhport, bool in_isr)
 {
+  (void) in_isr;
   uint32_t is  = KHCI->ISTAT;
   uint32_t msk = KHCI->INTEN;
 
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c
index 413e72037..f978b0965 100644
--- a/src/portable/ohci/ohci.c
+++ b/src/portable/ohci/ohci.c
@@ -667,8 +667,9 @@ static void done_queue_isr(uint8_t hostid)
   }
 }
 
-void hcd_int_handler(uint8_t hostid)
-{
+void hcd_int_handler(uint8_t hostid, bool in_isr) {
+  (void) in_isr;
+
   uint32_t const int_en     = OHCI_REG->interrupt_enable;
   uint32_t const int_status = OHCI_REG->interrupt_status & int_en;
 
diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
index 21dc3f67d..4ed6d36bb 100644
--- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
@@ -252,9 +252,9 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void)
   }
 }
 
-void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport)
-{
+void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport, bool in_isr) {
   (void) rhport;
+  (void) in_isr;
   hcd_rp2040_irq();
 }
 
diff --git a/src/portable/renesas/rusb2/hcd_rusb2.c b/src/portable/renesas/rusb2/hcd_rusb2.c
index 790cd6b32..bf95be707 100644
--- a/src/portable/renesas/rusb2/hcd_rusb2.c
+++ b/src/portable/renesas/rusb2/hcd_rusb2.c
@@ -771,8 +771,9 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
 //--------------------------------------------------------------------+
 // ISR
 //--------------------------------------------------------------------+
-void hcd_int_handler(uint8_t rhport)
-{
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+  (void) in_isr;
+
   rusb2_reg_t* rusb = RUSB2_REG(rhport);
   unsigned is0 = rusb->INTSTS0;
   unsigned is1 = rusb->INTSTS1;
diff --git a/src/portable/template/hcd_template.c b/src/portable/template/hcd_template.c
index 3e3b91558..b073d6057 100644
--- a/src/portable/template/hcd_template.c
+++ b/src/portable/template/hcd_template.c
@@ -51,8 +51,9 @@ bool hcd_init(uint8_t rhport) {
 }
 
 // Interrupt Handler
-void hcd_int_handler(uint8_t rhport) {
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
   (void) rhport;
+  (void) in_isr;
 }
 
 // Enable USB interrupt
diff --git a/src/tinyusb.mk b/src/tinyusb.mk
index 85052f90f..89ea0212c 100644
--- a/src/tinyusb.mk
+++ b/src/tinyusb.mk
@@ -17,3 +17,10 @@ TINYUSB_SRC_C += \
 	src/class/usbtmc/usbtmc_device.c \
 	src/class/video/video_device.c \
 	src/class/vendor/vendor_device.c \
+  src/host/usbh.c \
+  src/host/hub.c \
+  src/class/cdc/cdc_host.c \
+  src/class/hid/hid_host.c \
+  src/class/msc/msc_host.c \
+  src/class/vendor/vendor_host.c \
+  src/typec/usbc.c \
diff --git a/src/tusb.h b/src/tusb.h
index d6534ca28..c9d56d3c3 100644
--- a/src/tusb.h
+++ b/src/tusb.h
@@ -66,7 +66,7 @@
   #endif
 #else
   #ifndef tuh_int_handler
-  #define tuh_int_handler(_x)
+  #define tuh_int_handler(...)
   #endif
 #endif
 
@@ -123,7 +123,7 @@
   #endif
 #else
   #ifndef tud_int_handler
-  #define tud_int_handler(_x)
+  #define tud_int_handler(...)
   #endif
 #endif
 
diff --git a/src/tusb_option.h b/src/tusb_option.h
index c72117850..a41f5a07e 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -424,11 +424,11 @@
 //------------- CLASS -------------//
 
 #ifndef CFG_TUH_HUB
-#define CFG_TUH_HUB    0
+  #define CFG_TUH_HUB    0
 #endif
 
 #ifndef CFG_TUH_CDC
-#define CFG_TUH_CDC    0
+  #define CFG_TUH_CDC    0
 #endif
 
 #ifndef CFG_TUH_CDC_FTDI
@@ -442,34 +442,38 @@
 #endif
 
 #ifndef CFG_TUH_HID
-#define CFG_TUH_HID    0
+  #define CFG_TUH_HID    0
 #endif
 
 #ifndef CFG_TUH_MIDI
-#define CFG_TUH_MIDI   0
+  #define CFG_TUH_MIDI   0
 #endif
 
 #ifndef CFG_TUH_MSC
-#define CFG_TUH_MSC    0
+  #define CFG_TUH_MSC    0
 #endif
 
 #ifndef CFG_TUH_VENDOR
-#define CFG_TUH_VENDOR 0
+  #define CFG_TUH_VENDOR 0
 #endif
 
 #ifndef CFG_TUH_API_EDPT_XFER
-#define CFG_TUH_API_EDPT_XFER 0
+  #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
+  #define CFG_TUH_RPI_PIO_USB 0
 #endif
 
 #ifndef CFG_TUD_RPI_PIO_USB
-#define CFG_TUD_RPI_PIO_USB 0
+  #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)
diff --git a/tools/build_esp32.py b/tools/build_esp32.py
index 00783bf58..1f73d3b22 100644
--- a/tools/build_esp32.py
+++ b/tools/build_esp32.py
@@ -17,8 +17,8 @@ exit_status = 0
 
 total_time = time.monotonic()
 
-build_format = '| {:23} | {:30} | {:18} | {:7} | {:6} | {:6} |'
-build_separator = '-' * 100
+build_format = '| {:30} | {:30} | {:18} | {:7} | {:6} | {:6} |'
+build_separator = '-' * 107
 
 def filter_with_input(mylist):
     if len(sys.argv) > 1:
@@ -26,12 +26,9 @@ def filter_with_input(mylist):
         if len(input_args) > 0:
             mylist[:] = input_args
 
+
 # Build all examples if not specified
-all_examples = []
-for entry in os.scandir("examples/device"):
-    # Only includes example with CMakeLists.txt for esp32s, and skip board_test to speed up ci
-    if entry.is_dir() and os.path.exists(entry.path + "/sdkconfig.defaults") and entry.name != 'board_test':
-        all_examples.append(entry.name)
+all_examples = [entry.replace('examples/', '') for entry in glob.glob("examples/*/*_freertos")]
 filter_with_input(all_examples)
 all_examples.sort()
 
@@ -46,32 +43,41 @@ all_boards.sort()
 def build_board(example, board):
     global success_count, fail_count, skip_count, exit_status
     start_time = time.monotonic()
+
+    # Check if board is skipped
+    build_dir = f"cmake-build/cmake-build-{board}/{example}"
+
+    # Generate and build
+    r = subprocess.run(f"cmake examples/{example} -B {build_dir} -G \"Ninja\" -DBOARD={board} -DMAX3421_HOST=1",
+                       shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    if r.returncode == 0:
+        r = subprocess.run(f"cmake --build {build_dir}", shell=True, stdout=subprocess.PIPE,
+                           stderr=subprocess.STDOUT)
+    build_duration = time.monotonic() - start_time
     flash_size = "-"
     sram_size = "-"
 
-    # Check if board is skipped
-    if build_utils.skip_example(example, board):
-        success = SKIPPED
-        skip_count += 1
-        print(build_format.format(example, board, success, '-', flash_size, sram_size))
+    if r.returncode == 0:
+        success = SUCCEEDED
+        success_count += 1
+        #(flash_size, sram_size) = build_size(example, board)
     else:
-        subprocess.run("make -C examples/device/{} BOARD={} clean".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        build_result = subprocess.run("make -j -C examples/device/{} BOARD={} all".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        exit_status = r.returncode
+        success = FAILED
+        fail_count += 1
 
-        if build_result.returncode == 0:
-            success = SUCCEEDED
-            success_count += 1
-            (flash_size, sram_size) = build_size(example, board)
-        else:
-            exit_status = build_result.returncode
-            success = FAILED
-            fail_count += 1
+    title = build_format.format(example, board, success, "{:.2f}s".format(build_duration), flash_size, sram_size)
+    if os.getenv('CI'):
+        # always print build output if in CI
+        print(f"::group::{title}")
+        print(r.stdout.decode("utf-8"))
+        print(f"::endgroup::")
+    else:
+        # print build output if failed
+        print(title)
+        if r.returncode != 0:
+            print(r.stdout.decode("utf-8"))
 
-        build_duration = time.monotonic() - start_time
-        print(build_format.format(example, board, success, "{:.2f}s".format(build_duration), flash_size, sram_size))
-
-        if build_result.returncode != 0:
-            print(build_result.stdout.decode("utf-8"))
 
 def build_size(example, board):
     #elf_file = 'examples/device/{}/_build/{}/{}-firmware.elf'.format(example, board, board)
@@ -82,6 +88,7 @@ def build_size(example, board):
     sram_size = int(size_list[1]) + int(size_list[2])
     return (flash_size, sram_size)
 
+
 print(build_separator)
 print(build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', 'Flash', 'SRAM'))
 print(build_separator)
diff --git a/tools/iar_gen.py b/tools/iar_gen.py
index 73c8b29fc..264dd9a58 100644
--- a/tools/iar_gen.py
+++ b/tools/iar_gen.py
@@ -1,51 +1,84 @@
 #!/usr/bin/python3
 
 import os
+import sys
 import xml.dom.minidom as XML
+import glob
 
-# Read base configuration
-base = ""
-with open("iar_template.ipcf") as f:
-    base = f.read()
+def Main():
+    # Read base configuration
+    base = ""
+    with open("iar_template.ipcf") as f:
+        base = f.read()
 
-# Enumerate all device/host examples
-dir_1 = os.listdir("../examples")
-for dir_2 in dir_1:
-    if os.path.isdir("../examples/{}".format(dir_2)):
-        print(dir_2)
-        examples = os.listdir("../examples/{}".format(dir_2))
-        for example in examples:
-            if os.path.isdir("../examples/{}/{}".format(dir_2, example)):
-                print("../examples/{}/{}".format(dir_2, example))
-                conf = XML.parseString(base)
-                files = conf.getElementsByTagName("files")[0]
-                inc   = conf.getElementsByTagName("includePath")[0]
-                # Add bsp inc
-                path = conf.createElement('path')
-                path_txt = conf.createTextNode("$TUSB_DIR$/hw")
-                path.appendChild(path_txt)
-                inc.appendChild(path)
-                # Add board.c/.h
-                grp = conf.createElement('group')
-                grp.setAttribute("name", "bsp")
-                path = conf.createElement('path')
-                path_txt = conf.createTextNode("$TUSB_DIR$/hw/bsp/board.c")
-                path.appendChild(path_txt)
-                grp.appendChild(path)
-                files.appendChild(grp)
-                # Add example's .c/.h
-                grp = conf.createElement('group')
-                grp.setAttribute("name", "example")
-                for file in os.listdir("../examples/{}/{}/src".format(dir_2, example)):
-                    if file.endswith(".c") or file.endswith(".h"):
-                        path = conf.createElement('path')
-                        path.setAttribute("copyTo", "$PROJ_DIR$/{}".format(file))
-                        path_txt = conf.createTextNode("$TUSB_DIR$/examples/{0}/{1}/src/{2}".format(dir_2, example, file))
-                        path.appendChild(path_txt)
-                        grp.appendChild(path)
-                files.appendChild(grp)
-                cfg_str = conf.toprettyxml()
-                cfg_str = '\n'.join([s for s in cfg_str.splitlines() if s.strip()])
-                #print(cfg_str)
-                with open("../examples/{0}/{1}/iar_{1}.ipcf".format(dir_2, example), 'w') as f:
-                    f.write(cfg_str)
+    # Enumerate all device/host examples
+    dir_1 = os.listdir("../examples")
+    for dir_2 in dir_1:
+        if os.path.isdir("../examples/{}".format(dir_2)):
+            print(dir_2)
+            examples = os.listdir("../examples/{}".format(dir_2))
+            for example in examples:
+                if os.path.isdir("../examples/{}/{}".format(dir_2, example)):
+                    print("../examples/{}/{}".format(dir_2, example))
+                    conf = XML.parseString(base)
+                    files = conf.getElementsByTagName("files")[0]
+                    inc   = conf.getElementsByTagName("includePath")[0]
+                    # Add bsp inc
+                    path = conf.createElement('path')
+                    path_txt = conf.createTextNode("$TUSB_DIR$/hw")
+                    path.appendChild(path_txt)
+                    inc.appendChild(path)
+                    # Add board.c/.h
+                    grp = conf.createElement('group')
+                    grp.setAttribute("name", "bsp")
+                    path = conf.createElement('path')
+                    path_txt = conf.createTextNode("$TUSB_DIR$/hw/bsp/board.c")
+                    path.appendChild(path_txt)
+                    grp.appendChild(path)
+                    files.appendChild(grp)
+                    # Add example's .c/.h
+                    grp = conf.createElement('group')
+                    grp.setAttribute("name", "example")
+                    for file in os.listdir("../examples/{}/{}/src".format(dir_2, example)):
+                        if file.endswith(".c") or file.endswith(".h"):
+                            path = conf.createElement('path')
+                            path.setAttribute("copyTo", "$PROJ_DIR$/{}".format(file))
+                            path_txt = conf.createTextNode("$TUSB_DIR$/examples/{0}/{1}/src/{2}".format(dir_2, example, file))
+                            path.appendChild(path_txt)
+                            grp.appendChild(path)
+                    files.appendChild(grp)
+                    cfg_str = conf.toprettyxml()
+                    cfg_str = '\n'.join([s for s in cfg_str.splitlines() if s.strip()])
+                    #print(cfg_str)
+                    with open("../examples/{0}/{1}/iar_{1}.ipcf".format(dir_2, example), 'w') as f:
+                        f.write(cfg_str)
+
+def ListPath(path, blacklist=[]):
+    # Get all .c files
+    files = glob.glob(f'../{path}/**/*.c', recursive=True)
+    # Filter
+    files = [x for x in files if all(y not in x for y in blacklist)]
+    # Get common dir list
+    dirs = []
+    for file in files:
+        dir = os.path.dirname(file)
+        if dir not in dirs:
+            dirs.append(dir)
+    # Print .c grouped by dir
+    for dir in dirs:
+        print('')
+        for file in files:
+            if os.path.dirname(file) == dir:
+                print('    $TUSB_DIR$/' + file.replace('../','').replace('\\','/')+'')
+        print('')
+
+def List():
+    ListPath('src', [ 'template.c', 'dcd_synopsys.c', 'dcd_esp32sx.c' ])
+    ListPath('lib/SEGGER_RTT')
+
+if __name__ == "__main__":
+    if (len(sys.argv) > 1):
+        if (sys.argv[1] == 'l'):
+            List()
+    else:
+        Main()
diff --git a/tools/iar_template.ipcf b/tools/iar_template.ipcf
index 6ea1d576d..4919fe7b4 100644
--- a/tools/iar_template.ipcf
+++ b/tools/iar_template.ipcf
@@ -4,166 +4,176 @@
 	
 		$TUSB_DIR$/src
         $TUSB_DIR$/lib/SEGGER_RTT/RTT
+        $TUSB_DIR$/lib/SEGGER_RTT/Config
         $PROJ_DIR$
 	
 	
-		
-			$TUSB_DIR$/src/device/usbd.c
-			$TUSB_DIR$/src/device/usbd_control.c
-		
-		
-			$TUSB_DIR$/src/common/tusb_fifo.c
-		
-		
-			$TUSB_DIR$/src/class/audio/audio_device.c
-		
-		
-			$TUSB_DIR$/src/class/bth/bth_device.c
-		
-		
-			$TUSB_DIR$/src/class/cdc/cdc_device.c
-			$TUSB_DIR$/src/class/cdc/cdc_host.c
-			$TUSB_DIR$/src/class/cdc/cdc_rndis_host.c
-		
-		
-			$TUSB_DIR$/src/class/dfu/dfu_device.c
-			$TUSB_DIR$/src/class/dfu/dfu_rt_device.c
-		
-		
-			$TUSB_DIR$/src/class/hid/hid_device.c
-			$TUSB_DIR$/src/class/hid/hid_host.c
-		
-		
-			$TUSB_DIR$/src/class/midi/midi_device.c
-		
-		
-			$TUSB_DIR$/src/class/msc/msc_device.c
-			$TUSB_DIR$/src/class/msc/msc_host.c
-		
-		
-			$TUSB_DIR$/src/class/net/ecm_rndis_device.c
-			$TUSB_DIR$/src/class/net/ncm_device.c
-		
-		
-			$TUSB_DIR$/src/class/usbtmc/usbtmc_device.c
-		
-		
-			$TUSB_DIR$/src/class/vendor/vendor_device.c
-			$TUSB_DIR$/src/class/vendor/vendor_host.c
-		
         
             $TUSB_DIR$/src/tusb.c
         
-		
-			$TUSB_DIR$/src/host/hub.c
-			$TUSB_DIR$/src/host/usbh.c
-		
-		
-			$TUSB_DIR$/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
-		
-		
-			$TUSB_DIR$/src/portable/chipidea/ci_hs/dcd_ci_hs.c
-			$TUSB_DIR$/src/portable/chipidea/ci_hs/hcd_ci_hs.c
-		
-		
-			$TUSB_DIR$/src/portable/synopsys/dwc2/dcd_dwc2.c
-		
-		
-			$TUSB_DIR$/src/portable/dialog/da146xx/dcd_da146xx.c
-		
-		
-			$TUSB_DIR$/src/portable/ehci/ehci.c
-		
-		
-			$TUSB_DIR$/src/portable/espressif/esp32sx/dcd_esp32sx.c
-		
-		
-			$TUSB_DIR$/src/portable/mentor/musb/dcd_musb.c
-			$TUSB_DIR$/src/portable/mentor/musb/hcd_musb.c
-		
-		
-			$TUSB_DIR$/src/portable/microchip/samd/dcd_samd.c
-		
-		
-			$TUSB_DIR$/src/portable/microchip/samg/dcd_samg.c
-		
-		
-			$TUSB_DIR$/src/portable/microchip/samx7x/dcd_samx7x.c
-		
-		
-			$TUSB_DIR$/src/portable/microchip/pic/dcd_pic.c
-		
-		
-			$TUSB_DIR$/src/portable/microchip/pic32mz/dcd_pic32mz.c
-		
-		
-			$TUSB_DIR$/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
-		
-		
-			$TUSB_DIR$/src/portable/nordic/nrf5x/dcd_nrf5x.c
-		
-		
-			$TUSB_DIR$/src/portable/nuvoton/nuc120/dcd_nuc120.c
-		
-		
-			$TUSB_DIR$/src/portable/nuvoton/nuc121/dcd_nuc121.c
-		
-		
-			$TUSB_DIR$/src/portable/nuvoton/nuc505/dcd_nuc505.c
-		
-		
-			$TUSB_DIR$/src/portable/nxp/khci/dcd_khci.c
-			$TUSB_DIR$/src/portable/nxp/khci/hcd_khci.c
-		
-		
-			$TUSB_DIR$/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
-			$TUSB_DIR$/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
-		
-		
-			$TUSB_DIR$/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
-		
-		
-			$TUSB_DIR$/src/portable/nxp/transdimension/dcd_transdimension.c
-			$TUSB_DIR$/src/portable/nxp/transdimension/hcd_transdimension.c
-		
-		
-			$TUSB_DIR$/src/portable/ohci/ohci.c
-		
-		
-			$TUSB_DIR$/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
-			$TUSB_DIR$/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
-		
-		
-			$TUSB_DIR$/src/portable/raspberrypi/rp2040/dcd_rp2040.c
-			$TUSB_DIR$/src/portable/raspberrypi/rp2040/hcd_rp2040.c
-		
-		
-			$TUSB_DIR$/src/portable/renesas/rusb2/dcd_rusb2.c
-			$TUSB_DIR$/src/portable/renesas/rusb2/hcd_rusb2.c
-		
-		
-			$TUSB_DIR$/src/portable/sony/cxd56/dcd_cxd56.c
-		
-		
-			$TUSB_DIR$/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
-		
-		
-			$TUSB_DIR$/src/portable/sunxi/dcd_sunxi_musb.c
-		
-		
-			$TUSB_DIR$/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
-		
-		
-			$TUSB_DIR$/src/portable/valentyusb/eptri/dcd_eptri.c
-		
-		
-			$TUSB_DIR$/src/portable/wch/ch32v307/dcd_usbhs.c
-		
-        
-			$TUSB_DIR$/lib/SEGGER_RTT/RTT/SEGGER_RTT.c
+        
+            $TUSB_DIR$/src/class/audio/audio_device.c
+        
+        
+            $TUSB_DIR$/src/class/bth/bth_device.c
+        
+        
+            $TUSB_DIR$/src/class/cdc/cdc_device.c
+            $TUSB_DIR$/src/class/cdc/cdc_host.c
+            $TUSB_DIR$/src/class/cdc/cdc_rndis_host.c
+        
+        
+            $TUSB_DIR$/src/class/dfu/dfu_device.c
+            $TUSB_DIR$/src/class/dfu/dfu_rt_device.c
+        
+        
+            $TUSB_DIR$/src/class/hid/hid_device.c
+            $TUSB_DIR$/src/class/hid/hid_host.c
+        
+        
+            $TUSB_DIR$/src/class/midi/midi_device.c
+        
+        
+            $TUSB_DIR$/src/class/msc/msc_device.c
+            $TUSB_DIR$/src/class/msc/msc_host.c
+        
+        
+            $TUSB_DIR$/src/class/net/ecm_rndis_device.c
+            $TUSB_DIR$/src/class/net/ncm_device.c
+        
+        
+            $TUSB_DIR$/src/class/usbtmc/usbtmc_device.c
+        
+        
+            $TUSB_DIR$/src/class/vendor/vendor_device.c
+            $TUSB_DIR$/src/class/vendor/vendor_host.c
+        
+        
+            $TUSB_DIR$/src/class/video/video_device.c
+        
+        
+            $TUSB_DIR$/src/common/tusb_fifo.c
+        
+        
+            $TUSB_DIR$/src/device/usbd.c
+            $TUSB_DIR$/src/device/usbd_control.c
+        
+        
+            $TUSB_DIR$/src/host/hub.c
+            $TUSB_DIR$/src/host/usbh.c
+        
+        
+            $TUSB_DIR$/src/portable/analog/max3421/hcd_max3421.c
+        
+        
+            $TUSB_DIR$/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
+        
+        
+            $TUSB_DIR$/src/portable/chipidea/ci_fs/dcd_ci_fs.c
+        
+        
+            $TUSB_DIR$/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+            $TUSB_DIR$/src/portable/chipidea/ci_hs/hcd_ci_hs.c
+        
+        
+            $TUSB_DIR$/src/portable/dialog/da146xx/dcd_da146xx.c
+        
+        
+            $TUSB_DIR$/src/portable/ehci/ehci.c
+        
+        
+            $TUSB_DIR$/src/portable/mentor/musb/dcd_musb.c
+            $TUSB_DIR$/src/portable/mentor/musb/hcd_musb.c
+        
+        
+            $TUSB_DIR$/src/portable/microchip/pic/dcd_pic.c
+        
+        
+            $TUSB_DIR$/src/portable/microchip/pic32mz/dcd_pic32mz.c
+        
+        
+            $TUSB_DIR$/src/portable/microchip/samd/dcd_samd.c
+        
+        
+            $TUSB_DIR$/src/portable/microchip/samg/dcd_samg.c
+        
+        
+            $TUSB_DIR$/src/portable/microchip/samx7x/dcd_samx7x.c
+        
+        
+            $TUSB_DIR$/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
+        
+        
+            $TUSB_DIR$/src/portable/nordic/nrf5x/dcd_nrf5x.c
+        
+        
+            $TUSB_DIR$/src/portable/nuvoton/nuc120/dcd_nuc120.c
+        
+        
+            $TUSB_DIR$/src/portable/nuvoton/nuc121/dcd_nuc121.c
+        
+        
+            $TUSB_DIR$/src/portable/nuvoton/nuc505/dcd_nuc505.c
+        
+        
+            $TUSB_DIR$/src/portable/nxp/khci/dcd_khci.c
+            $TUSB_DIR$/src/portable/nxp/khci/hcd_khci.c
+        
+        
+            $TUSB_DIR$/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
+            $TUSB_DIR$/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+        
+        
+            $TUSB_DIR$/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
+        
+        
+            $TUSB_DIR$/src/portable/ohci/ohci.c
+        
+        
+            $TUSB_DIR$/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
+            $TUSB_DIR$/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
+        
+        
+            $TUSB_DIR$/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+            $TUSB_DIR$/src/portable/raspberrypi/rp2040/hcd_rp2040.c
+            $TUSB_DIR$/src/portable/raspberrypi/rp2040/rp2040_usb.c
+        
+        
+            $TUSB_DIR$/src/portable/renesas/rusb2/dcd_rusb2.c
+            $TUSB_DIR$/src/portable/renesas/rusb2/hcd_rusb2.c
+            $TUSB_DIR$/src/portable/renesas/rusb2/rusb2_common.c
+        
+        
+            $TUSB_DIR$/src/portable/sony/cxd56/dcd_cxd56.c
+        
+        
+            $TUSB_DIR$/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+        
+        
+            $TUSB_DIR$/src/portable/st/typec/typec_stm32.c
+        
+        
+            $TUSB_DIR$/src/portable/sunxi/dcd_sunxi_musb.c
+        
+        
+            $TUSB_DIR$/src/portable/synopsys/dwc2/dcd_dwc2.c
+        
+        
+            $TUSB_DIR$/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
+        
+        
+            $TUSB_DIR$/src/portable/valentyusb/eptri/dcd_eptri.c
+        
+        
+            $TUSB_DIR$/src/portable/wch/ch32v307/dcd_usbhs.c
+        
+        
+            $TUSB_DIR$/src/typec/usbc.c
+        
+        
+            $TUSB_DIR$/lib/SEGGER_RTT/RTT/SEGGER_RTT.c
             $TUSB_DIR$/lib/SEGGER_RTT/RTT/SEGGER_RTT_printf.c
-            $TUSB_DIR$/lib/SEGGER_RTT/Syscalls/SEGGER_RTT_Syscalls_IAR.c
-		
+